跳转到帖子

背景

Webshell 排查是应急响应里很常见的一类工作。很多时候,告警并不会直接告诉你“哪个文件是木马”,而是给出一些零散线索:异常 POST 请求、Web 目录新增文件、进程启动了系统命令、某个 PHP 文件被频繁访问等。

这篇文章整理一套偏实战的排查思路,重点放在日志分析、文件定位、代码审计和加固复盘上。内容只讨论防守侧分析方法,不涉及未授权利用、恶意控制或规避检测。

典型场景

场景可以简化为:一台运行 PHP 站点的服务器出现异常安全告警,告警信息包括:

  • Web 服务进程存在异常子进程,例如 sh、bash、cmd、curl、wget 等;
  • 站点目录出现近期新增的 PHP 文件;
  • 访问日志中存在异常 POST 请求,URL 不属于正常业务接口;
  • 部分请求 User-Agent、Referer、请求体长度明显异常;
  • 服务器对外发起了不符合业务特征的连接。

实际排查时,不建议一开始就凭经验删除文件。更稳妥的做法是先保全证据,再做定位,最后做清理和修复。

第一步:保全现场与确认影响面

如果是生产环境,建议先做最小化影响处理,例如临时下线可疑入口、限制外联、备份日志和可疑文件。直接重启或清空目录可能导致关键时间线丢失。

# 记录当前时间,方便后续对齐日志时间线
date '+%F %T %z'

# 备份 Web 访问日志与错误日志
mkdir -p /tmp/ir_backup
cp -a /var/log/nginx /tmp/ir_backup/nginx_$(date +%F_%H%M%S)
cp -a /var/log/httpd /tmp/ir_backup/httpd_$(date +%F_%H%M%S) 2>/dev/null

# 备份站点目录元数据与文件清单
find /var/www -printf '%TY-%Tm-%Td %TH:%TM:%TS %u %g %m %s %p\n' > /tmp/ir_backup/web_file_timeline.txt

这里的重点是建立时间线。Webshell 往往不是单点事件,可能存在上传入口、落地文件、执行命令、横向探测、清理痕迹等多个阶段。

第二步:从访问日志定位异常入口

先按状态码、请求方法、访问频次、请求大小做粗筛。Webshell 常见访问特征包括异常 POST、少量固定 URL 高频访问、请求参数长度异常、返回体大小稳定等,但这些都不是绝对规则,需要结合业务判断。

# 查看近期 POST 请求,按 URL 聚合
awk '$6 ~ /POST/ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -50

# 过滤常见可疑扩展访问
awk '{print $1,$4,$5,$6,$7,$9,$10,$12}' /var/log/nginx/access.log | grep -Ei '\.(php|phtml|php5|inc)(\?| )' | head

# 按状态码聚合
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr

# 查找非常规 User-Agent
awk -F '"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -30

如果使用 Nginx 反代 PHP-FPM,还要关注 upstream 响应时间、请求体大小、客户端 IP 是否来自代理之后的真实地址。若站点前面有 CDN 或负载均衡,需确认日志中记录的是 X-Forwarded-For 还是边缘节点 IP,避免误判。

第三步:检查 Web 目录近期变更

Webshell 通常会落在可写目录,例如上传目录、缓存目录、临时目录、插件目录或主题目录。先按修改时间筛选,再结合文件内容判断。

# 查找 7 天内变更的脚本文件
find /var/www -type f -mtime -7 \( -name '*.php' -o -name '*.phtml' -o -name '*.inc' \) -ls

# 查找上传目录中的脚本文件
find /var/www -type f \( -path '*upload*' -o -path '*uploads*' -o -path '*cache*' \) \
  \( -name '*.php' -o -name '*.phtml' -o -name '*.php5' \) -ls

# 查找权限异常的文件
find /var/www -type f -perm -002 -ls
find /var/www -type d -perm -002 -ls

需要注意,攻击者可能会修改文件时间戳,单看 mtime 不够。可以结合日志访问时间、文件 inode 变化、备份差异、版本控制记录以及 EDR/HIDS 事件进行交叉验证。

第四步:代码特征审计

Webshell 的代码形态很多,有明文、混淆、编码、拆分加载、伪装业务函数等。审计时不要只依赖单个关键字,但可以用关键函数做第一轮筛选。

# PHP 中常见高风险函数筛选
grep -RInE "(eval|assert|system|exec|shell_exec|passthru|popen|proc_open|base64_decode|gzinflate|str_rot13|create_function)" /var/www --include='*.php'

# 查找长行、疑似混淆代码
find /var/www -name '*.php' -type f -print0 | xargs -0 awk 'length($0) > 500 {print FILENAME ":" FNR ":" substr($0,1,120)}'

# 查找 PHP 文件头部前存在异常二进制或隐藏内容
find /var/www -name '*.php' -type f -exec file {} \; | grep -vi 'PHP\|text'

常见可疑点包括:

  • 使用动态函数调用,例如变量函数、拼接函数名;
  • 对外部输入直接进入命令执行、文件写入、反序列化、模板渲染;
  • 大量编码解码叠加,逻辑与业务代码风格明显不一致;
  • 文件名伪装成正常资源,例如 image.php、cache.php、class.upload.php;
  • 上传目录中出现可执行脚本;
  • 业务文件中被插入一小段加载器代码,通过 include 或 require 引入隐藏文件。
判断 Webshell 时要保守,尤其是业务系统、CMS 插件、历史遗留代码中也可能存在高风险函数。建议结合访问日志、文件变更时间、调用链和业务用途综合确认。

第五步:从进程与外联行为反推

如果告警提示 Web 进程启动了系统命令,需要尽快查看进程树、网络连接和历史命令痕迹。生产环境中很多短生命周期进程已经消失,此时可以依赖审计日志、EDR 记录或系统日志。

# 查看 Web 服务相关进程树
ps -ef --forest | grep -E 'nginx|apache|httpd|php-fpm' | grep -v grep

# 查看当前网络连接
ss -plant

# 查看最近登录与异常会话
last -a | head -30
lastb -a 2>/dev/null | head -30

# systemd 日志中检索 Web 服务异常
journalctl --since '24 hours ago' | grep -Ei 'nginx|httpd|php|segfault|error|denied'

如果系统开启了 auditd,可以进一步按执行行为检索。没有审计日志时,建议在复盘阶段补上,否则很多命令执行类事件很难还原。

# 示例:查看近期 execve 相关审计事件(前提是已开启审计规则)
ausearch -k web_exec --start recent

# 可配置审计规则示例,监控 Web 用户执行系统命令
-a always,exit -F arch=b64 -S execve -F auid>=0 -F key=web_exec

第六步:定位初始入口

只删除 Webshell 不够,关键是找到它是如何落地的。常见入口包括:

  • 文件上传校验不严,允许上传 PHP、phtml 或双后缀文件;
  • 编辑器、富文本、附件管理组件存在历史漏洞;
  • 后台弱口令或凭证泄露,攻击者通过合法功能写入文件;
  • CMS、插件、主题存在已知漏洞但未升级;
  • 反序列化、模板注入、任意文件写入等代码缺陷;
  • 服务器账号权限过大,业务进程可写入不该写的目录。

建议根据可疑文件首次访问时间向前回溯日志。例如某个 shell 文件首次访问时间是 10:32,可以重点查看 10:00 到 10:32 之间的上传接口、后台接口、插件接口请求。

# 粗略按时间窗口筛选访问日志
awk '$4 >= "[10/Jan/2026:10:00:00" && $4 <= "[10/Jan/2026:10:32:59" {print}' /var/log/nginx/access.log > /tmp/ir_backup/window.log

# 在时间窗口中查看上传、编辑、插件相关路径
grep -Ei 'upload|file|attach|editor|plugin|theme|admin|ajax' /tmp/ir_backup/window.log

如果站点有应用日志,也要同步检查。访问日志只能看到 HTTP 层信息,应用日志可能记录登录账号、文件名、异常堆栈、业务操作 ID,这些对确定入口非常有价值。

第七步:清理与修复

清理阶段建议按“隔离、比对、替换、验证”的顺序做,不建议只在原文件上手工删除几行可疑代码。

  • 将可疑文件复制到隔离目录并计算哈希,便于后续复盘;
  • 业务代码从可信版本库或干净备份重新发布;
  • 删除上传目录中的脚本执行能力;
  • 修复初始漏洞,例如上传校验、鉴权缺陷、组件升级;
  • 轮换后台账号、数据库账号、服务器账号、AccessKey 等敏感凭证;
  • 检查计划任务、启动项、SSH 公钥、隐藏用户等持久化点;
  • 复查是否存在多个 Webshell 或后门加载器。

对于 Nginx + PHP 站点,一个常见加固点是禁止上传目录执行 PHP:

location ^~ /uploads/ {
    location ~* \.(php|phtml|php5|inc)$ {
        deny all;
    }
}

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

同时要控制目录权限。原则上,Web 运行用户只应对必要目录有写权限,代码目录不应由 Web 进程随意写入。

# 示例:代码目录只读,上传目录可写但不可执行需要由 Web 服务配置配合限制
chown -R root:root /var/www/site
chown -R www-data:www-data /var/www/site/uploads
find /var/www/site -type d -exec chmod 755 {} \;
find /var/www/site -type f -exec chmod 644 {} \;
chmod -R 750 /var/www/site/uploads

常见误区

  • 只按关键字删除文件:容易漏掉加载器、伪装文件和已修改业务文件;
  • 只看 Web 目录:需要同时检查进程、计划任务、账号、外联和系统日志;
  • 忽略入口修复:Webshell 清掉后很快再次出现,通常是入口还在;
  • 没有证据保全:后续无法判断影响面,也难以对外部审计交代;
  • 直接在生产环境反复试探:可能破坏时间线,也可能扩大业务影响。

一个简化排查清单

阶段检查项目标
日志分析POST、异常 UA、异常路径、状态码定位可疑访问入口
文件排查近期变更、上传目录脚本、混淆代码发现 Webshell 或加载器
进程排查Web 进程子进程、外联连接确认是否存在命令执行行为
入口定位上传接口、后台操作、组件漏洞找到初始落点
清理修复可信发布、权限收敛、凭证轮换降低复发风险
复盘加固审计日志、WAF/HIDS、监控告警提升后续发现能力

总结

Webshell 排查的核心不是“找到一个可疑文件就结束”,而是要把时间线串起来:入口是什么,文件如何落地,执行过什么动作,影响了哪些资产,最后修复了哪些根因。

从经验看,真正有长期价值的做法是把排查过程沉淀成基线:日志字段完整、Web 目录权限清晰、上传目录不可执行、组件有升级机制、关键命令执行有审计。这样下次遇到类似事件时,不会只能靠猜。

网络流量与边界分析示意图
网络请求、日志与边界流量分析示意

0篇意见

推荐意见

没有意见。

游客
抱歉,你的帖子内容包括我们不允许的字词。请编辑你的帖子,删除下面高亮的屏蔽字。
添加意见…