跳转到帖子

背景

最近整理了一起典型的 WebShell 入侵事件。场景并不复杂:一台对公网开放的业务服务器,运行常见的 PHP Web 应用,前面挂了反向代理。管理员最早发现的问题是站点偶尔返回异常内容,随后在 Web 目录下发现了可疑 PHP 文件。

这类事件在实际处置中很常见,难点往往不在于“删掉木马文件”,而在于确认入口、影响范围、是否存在持久化、以及如何把同类问题彻底堵住。本文按一次相对完整的排查流程整理,重点放在日志分析、文件取证、代码入口定位和加固闭环上。

问题场景

已知现象包括:

  • Web 根目录下出现异常 PHP 文件,文件名看起来像缓存或资源文件。
  • Nginx 访问日志中存在少量非常规请求,User-Agent 较少见。
  • 服务器负载没有明显升高,未发现大规模扫描或挖矿行为。
  • 应用目录中部分文件修改时间集中在某个时间段。

这种情况下,不建议直接重装或清空日志。比较稳妥的做法是先保护现场,复制关键日志和可疑样本,再进入清理与加固阶段。

第一步:保护现场与基础信息采集

如果业务允许,优先将站点切到维护页或只读状态。至少应避免攻击者继续写入文件。基础信息采集建议覆盖以下几类:

  • Web 访问日志、错误日志、反向代理日志。
  • 应用日志,例如框架日志、上传日志、后台操作日志。
  • 系统登录日志、计划任务、进程列表、网络连接。
  • Web 目录文件列表、修改时间、权限信息。

示例命令如下,注意将输出保存到独立目录,避免覆盖现场文件:

mkdir -p /root/incident-$(date +%F)
cd /root/incident-$(date +%F)

cp /var/log/nginx/access.log* ./ 2>/dev/null
cp /var/log/nginx/error.log* ./ 2>/dev/null
cp /var/log/secure* ./ 2>/dev/null
cp /var/log/auth.log* ./ 2>/dev/null

ps auxf > ps_auxf.txt
ss -antup > ss_antup.txt
crontab -l > crontab_current_user.txt 2>/dev/null
ls -lah /etc/cron* > cron_files.txt 2>/dev/null
find /var/www -printf '%TY-%Tm-%Td %TH:%TM %u %g %m %p\n' > web_files_timeline.txt

这里的目标不是一次性找出所有答案,而是先建立可回溯的数据基础。很多事件后期无法判断入口,根因就是早期清理过快,日志和样本被破坏。

第二步:定位异常文件

WebShell 通常会伪装成缓存、图片、配置备份或随机命名文件。排查时可以从时间线、文件内容、权限异常三个角度入手。

先看近期修改的 PHP 文件:

find /var/www -type f -name '*.php' -mtime -14 -printf '%TY-%Tm-%Td %TH:%TM %s %p\n' | sort

再粗略检索高风险函数和可疑编码特征。这里不是说出现这些函数就一定是木马,而是用于缩小审计范围:

grep -RInE "(eval\s*\(|assert\s*\(|base64_decode\s*\(|gzinflate\s*\(|shell_exec\s*\(|system\s*\(|passthru\s*\(|proc_open\s*\()" /var/www --include='*.php'

还可以关注图片目录、缓存目录中是否存在 PHP 文件:

find /var/www -type f \( -path '*upload*' -o -path '*uploads*' -o -path '*cache*' -o -path '*tmp*' \) -name '*.php' -print

在这次事件中,异常文件位于上传目录下,文件名前缀类似正常图片资源,但扩展名为 PHP。文件内容经过简单混淆,包含动态函数调用和外部输入拼接。这里不展开具体样本细节,重点是确认它不是业务代码的一部分,并记录其哈希、路径、时间戳和权限。

sha256sum /var/www/html/uploads/疑似文件.php
stat /var/www/html/uploads/疑似文件.php

第三步:从访问日志回溯入口

发现 WebShell 后,不要只看这个文件本身,还要追溯它第一次被访问、第一次被写入前后的请求。可以按文件名搜索访问日志:

zgrep -n "疑似文件.php" /var/log/nginx/access.log*

如果文件名没有直接出现在日志中,可能是通过上传接口写入,再由攻击者访问。此时应围绕文件修改时间向前后扩展窗口,例如前后 30 分钟:

# 假设可疑文件修改时间为 2025-01-10 14:32 左右
grep "10/Jan/2025:14:" /var/log/nginx/access.log > window_14.log
grep "10/Jan/2025:15:" /var/log/nginx/access.log > window_15.log

重点关注:

  • 上传接口、附件接口、头像接口、富文本编辑器接口。
  • 返回码为 200、302、500 的非常规 POST 请求。
  • 请求体较大的访问记录,尤其是上传类路径。
  • 同一 IP 在短时间内先探测接口,再访问新文件的行为。
  • User-Agent、Referer 缺失或高度固定的自动化请求。

如果 Nginx 未记录请求体,可以结合应用日志判断上传参数、文件名、用户 ID 等信息。很多情况下,入口并不是单纯的“弱口令后台”,而是上传校验缺陷、历史组件漏洞或编辑器插件遗留接口。

第四步:判断是否存在二次落地与持久化

WebShell 只是入口或操作通道之一。事件处置中需要确认攻击者是否写入了其他文件、添加了计划任务、修改了启动项或创建了系统账号。

建议检查近期新增和修改文件:

find /var/www -type f -newermt '2025-01-10 14:00:00' ! -newermt '2025-01-10 16:00:00' -printf '%TY-%Tm-%Td %TH:%TM %s %p\n' | sort

检查系统账号和登录痕迹:

last -a | head -50
lastlog | head -50
awk -F: '$3 >= 1000 {print}' /etc/passwd

检查计划任务和启动项:

crontab -l 2>/dev/null
ls -lah /var/spool/cron/ 2>/dev/null
ls -lah /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/ 2>/dev/null
systemctl list-unit-files --type=service | grep enabled

检查异常网络连接和进程:

ps auxf
ss -antup

这里要注意一个误区:没有异常进程,并不代表没有入侵;没有系统登录,也不代表没有 Web 层操作。WebShell 场景下,很多动作都发生在 Web 服务用户权限下,需要结合 Web 日志、文件时间线和应用日志一起判断。

第五步:分析代码层入口

如果日志显示异常请求集中在上传接口,应重点审计上传链路。常见问题包括:

  • 只校验前端扩展名,后端未做强校验。
  • 仅检查 Content-Type,未检查实际文件内容。
  • 允许用户控制保存路径或文件扩展名。
  • 上传目录允许 PHP、JSP、ASP 等脚本执行。
  • 历史编辑器、文件管理插件未删除或未鉴权。

一个相对稳妥的上传处理思路应包含:

  • 服务端白名单限制扩展名。
  • 生成随机文件名,不信任原始文件名。
  • 校验文件头和实际内容类型。
  • 上传目录禁止脚本执行。
  • 文件访问与上传目录分离,必要时走对象存储或独立静态域名。

例如 Nginx 层可以对上传目录禁用 PHP 解析,配置示例:

location ~* ^/uploads/.*\.php$ {
    return 403;
}

location /uploads/ {
    types {
        image/jpeg jpg jpeg;
        image/png png;
        image/gif gif;
        application/pdf pdf;
    }
    default_type application/octet-stream;
}

如果使用 PHP-FPM,还要避免过宽的 PHP 解析规则。历史上不少问题来自类似“任意路径匹配到 PHP 解释器”的配置。建议只允许明确的 .php 文件进入 PHP-FPM,且不要对上传目录放行脚本解析。

日志分析中的几个实用技巧

为了快速筛选异常请求,可以先统计状态码、路径和来源 IP:

awk '{print $9}' access.log | sort | uniq -c | sort -nr | head
awk '{print $7}' access.log | sort | uniq -c | sort -nr | head
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head

筛选 POST 请求:

grep '"POST ' access.log | awk '{print $1, $4, $6, $7, $9, $10}' | head -100

查找访问上传目录中的脚本文件:

grep -E 'uploads/.*\.(php|phtml|phar)' access.log

查看异常 UA:

awk -F'"' '{print $6}' access.log | sort | uniq -c | sort -nr | head -30

如果访问日志字段经过定制,上述 awk 需要按实际格式调整。建议平时将 request_time、upstream_response_time、host、request_body_size、http_x_forwarded_for 等字段加入日志格式,事件复盘时会非常有帮助。

清理与恢复建议

确认范围后再进入清理阶段。基本顺序建议如下:

  • 隔离或下线受影响实例,保留磁盘快照和关键日志。
  • 删除确认的 WebShell 和异常落地文件。
  • 回滚被篡改的业务代码,优先使用可信发布包或版本库。
  • 修复入口漏洞,例如上传校验、组件漏洞、弱口令后台。
  • 重置后台账号、服务器账号、数据库账号、密钥和 Token。
  • 检查 Web 服务用户权限,避免拥有过大的写权限。
  • 上线后加强监控,关注同类路径是否再次出现请求。

如果无法确认攻击者是否获取了数据库配置或环境变量,建议按“已泄露”处理,及时轮换凭据。很多二次入侵不是通过原漏洞,而是通过第一次事件中泄露的账号、密钥或后台 Cookie。

加固重点

风险点建议措施
上传目录可执行脚本Web 服务器层禁止脚本解析,上传文件与业务代码分离
后台弱口令启用强密码、MFA、IP 限制和登录审计
历史组件未更新建立组件清单,定期核查 CVE 与官方补丁
日志字段不足记录请求时间、状态码、来源 IP、UA、请求大小等关键字段
Web 用户权限过大最小权限运行,业务代码目录尽量只读
缺少文件监控对 Web 目录新增脚本文件、关键配置变更做告警

几个容易忽略的注意点

  • 不要只删除一个 WebShell 文件。应围绕时间线检查同一时间段所有新增和修改文件。
  • 不要完全依赖杀毒或查杀脚本。混淆变形后的样本可能无法被规则命中。
  • 不要忽视反向代理后的真实 IP。需要确认 X-Forwarded-For 是否可信,避免误判来源。
  • 不要在未修复入口前恢复业务。否则攻击者可能很快再次写入。
  • 不要把备份压缩包、数据库导出文件放在 Web 可访问目录下。

总结

WebShell 事件处置的核心不是“看到木马就删”,而是建立完整链路:发现异常文件、回溯访问日志、确认写入入口、排查持久化、清理恢复、修复漏洞、持续监控。

从长期治理角度看,上传目录禁执行、组件及时更新、后台访问收敛、日志字段完善、凭据定期轮换,这几项投入不大,但能显著降低类似事件的处置成本。真正有价值的复盘,是把一次入侵变成一组可验证的安全改进项,而不是停留在单次清理。

代码审计与漏洞定位示意图
代码审计、调用链与关键函数定位示意

0篇意见

推荐意见

没有意见。

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