网站被挂马后的应急响应实战:webshell 五路排查、持久化后门清理与二次入侵加固

当「文件被改过」这个信号出现时,你只有几十分钟

前面我写过文件完整性监控(AIDE、inotify)的搭建,也写过 AppArmor 怎么从内核层锁住写权限。但那些都是「防」和「发现」。这一篇讲的是最坏情况已经发生之后怎么办:你的服务器已经被入侵,磁盘上多了一个 webshell,你得在搜索引擎抓取它之前、在攻击者建立持久化之前,把它找出来、清掉、封死后门。

先明确一个心态上的前提:发现入侵时的第一反应不是「删掉那个可疑文件」,而是「先搞清楚他做了什么」。 你删了 shell,可能切断了唯一的线索来源,而攻击者留下的 crontab 后门还在,一小时后他又回来了。所以这是一场「取证优先、清理其次」的应急响应,顺序错了会反复被入侵。

第一步:切断与冻结,但别急着重启

确认入侵的第一时间,要做的三件事:

1. 从网络层隔离,而不是关机。 关机会清空内存、丢进程信息、丢 /proc 里的连接状态,而这些恰恰是最有价值的线索。正确的做法是在云控制台的安全组里,把入站规则收窄到只有你的 IP 能访问 SSH,其他全断。这样既阻止了攻击者继续利用,又保留了现场。

# 如果只有 iptables,临时只放行自己的 IP
iptables -I INPUT -p tcp --dport 22 -s 你的IP -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
# 注意别把自己也关在外面,操作前确认你的 IP

2. 保留内存与网络现场。

# 当前网络连接(谁连着你的服务器)
ss -antp > /root/ir/net.txt
# 当前进程树
ps auxf > /root/ir/ps.txt
# 监听端口
ss -tulnp > /root/ir/listen.txt
# 路由与 ARP(有没有被改)
ip route; ip neigh >> /root/ir/net.txt

3. 记录时间线。问自己:什么时候发现的?最后一次确认服务器正常是什么时候?这决定了你后面查日志的时间窗口。通常要往前查 7~30 天,因为很多后门是静默潜伏的。

第二步:找出 webshell 的特征

webshell 是一段被上传到网站目录、能通过 HTTP 请求执行的恶意脚本。它的核心特征是「接收外部输入并执行」或「接收外部输入并写文件」。按语言分几类常见形态:

PHP 一句话木马:

<?php @eval($_POST['cmd']); ?>
<?php assert($_REQUEST['x']); ?>
<?php system($_GET['c']); ?>
<?php $f=$_POST['f']; $d=$_POST['d']; file_put_contents($f,$d); ?>

变形与混淆。真实的 webshell 不会这么干净,常见手段有:base64 编码、字符串拼接、变量函数($a='sys'.'tem'; $a($_GET['c']))、preg_replace/e 修饰符、用 create_function、用 call_user_func、甚至藏在图片文件的 EXIF 里配合 include

所以单纯 grep eval( 是抓不全的。要靠多维特征交叉定位

第三步:五路并行排查

路线一:按文件时间戳找。入侵者落地 webshell 的时间点,通常和正常业务文件的时间戳不一致。找最近 30 天被创建或修改的文件:

# 最近 30 天修改过的 php 文件(排除缓存目录)
find /www/wwwroot -type f -name '*.php' -mtime -30 \
  -not -path '*/cache/*' -not -path '*/runtime/*' \
  -printf '%TY-%Tm-%Td %TH:%TM  %p\n' | sort -r | head -50

重点看:创建时间(ctime)和修改时间(mtime)接近的文件、在 /uploads//images//attachment/ 这类「不该有 PHP」的目录里出现的 PHP 文件。后一条几乎是必中——我的经验里,八成 webshell 藏在附件/上传目录。

# 上传目录里的可疑可执行文件(一等一的信号)
find /www/wwwroot -type f \( -name '*.php' -o -name '*.phtml' -o -name '*.php5' -o -name '*.php7' -o -name '*.inc' \) \
  -path '*upload*' -o -path '*attach*' -o -path '*image*' 2>/dev/null

路线二:按内容特征找。用 grep 扫全站,搜危险函数组合:

grep -rIl --include='*.php' -E '(eval|assert|system|exec|passthru|shell_exec|popen|proc_open)[[:space:]]*\([[:space:]]*\\\$_(GET|POST|REQUEST|COOKIE|SERVER)' /www/wwwroot/

再找明显的编码特征:

# 大段 base64 / 十六进制字符串(混淆的典型特征)
grep -rI --include='*.php' -E 'base64_decode[[:space:]]*\(.{100,}' /www/wwwroot/
grep -rI --include='*.php' -E '\\x[0-9a-f]{2}\\x[0-9a-f]{2}\\x[0-9a-f]{2}' /www/wwwroot/
# 变量函数调用模式
grep -rI --include='*.php' -E '\\\$[a-z]+[[:space:]]*=[[:space:]]*.[a-z]+.;[[:space:]]*\\\$[a-z]+[[:space:]]*\\\(' /www/wwwroot/

还有一个技巧:按文件大小异常找。一个正常 PHP 文件通常几百字节到几十 KB,混淆的 webshell 经常因为编码膨胀到几百 KB 甚至几 MB:

find /www/wwwroot -type f -name '*.php' -size +200k -printf '%s %p\n' | sort -rn | head -20

路线三:按 Web 日志找访问痕迹。webshell 被使用过就会留日志。找那些「参数超长」「POST 到陌生路径」「404 后马上 200」的模式:

# 长度异常的 URI(webshell 传参往往很长)
awk '{ if (length($7) > 200) print }' /var/log/nginx/access.log | head -30
# 访问量极小的陌生 php 文件
awk '{print $7}' /var/log/nginx/access.log | grep '\.php' | sort | uniq -c | sort -n | head -40
# 高频 404 扫描(攻击者在找入口)
grep ' 404 ' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head

把「访问次数很少的 PHP 文件」和「文件系统里新出现的 PHP 文件」两个名单一交集,命中率极高。

路线四:查进程与网络行为。有些 shell 会 fork 出常驻进程(挖矿、反弹连接):

# 高 CPU 进程(挖矿特征)
ps aux --sort=-%cpu | head -15
# 对外的可疑连接(反弹 shell、C2)
ss -antp state established | grep -v -E '(127.0.0.1|你的可信IP)'
# 查看进程的可执行文件是否已被删除(常见于运行中的入侵程序)
ls -l /proc/*/exe 2>/dev/null | grep deleted

路线五:查持久化后门。这是最容易被忽略、也最致命的。攻击者要长期驻留,一定会在系统里埋点:

# 所有用户的 crontab
for u in $(cut -d: -f1 /etc/passwd); do crontab -l -u $u 2>/dev/null | grep -v '^#'; done
cat /etc/cron.d/* /etc/cron.daily/* /etc/cron.hourly/* 2>/dev/null

# SSH 后门:新增的密钥与账号
cat /root/.ssh/authorized_keys
find /home /root -name authorized_keys -exec sh -c 'echo "== {}"; cat {}' \;
grep -E 'sh$|bash$' /etc/passwd   # 有 shell 的账号,看有没有陌生的
awk -F: '$3==0' /etc/passwd        # UID 0 的账号,除 root 外都是后门

# 系统服务与启动项
systemctl list-units --type=service --state=running
ls -la /etc/init.d/ /etc/rc*.d/ 2>/dev/null
cat /etc/rc.local 2>/dev/null

# 动态链接库劫持(高级后门)
cat /etc/ld.so.preload 2>/dev/null    # 有内容基本就是被劫持了

/etc/ld.so.preload 这个文件平时应该不存在或为空。它是 LD_PRELOAD 劫持的持久化手段,能让系统里所有进程都加载恶意库,从而隐藏文件、隐藏进程、隐藏网络连接。看到它非空,说明遇到了有水平的攻击者,清理难度大幅上升。

第四步:清理,但按正确顺序

取证做完、时间线理清之后,才是清理。顺序是:先切断入口(修漏洞)→ 再清后门 → 最后清 webshell。反过来的话,你清完文件,漏洞还在,几分钟内会被重新打回来。

  1. 先确定入侵入口。没有确定入口就清理,等于没清。看日志里最早的异常请求,结合当时网站版本、插件版本,判断是文件上传漏洞、SQL 注入、还是弱口令进了后台。这一步是清理能不能彻底的分水岭。
  2. 修漏洞:升级 CMS 与插件到最新版、改掉所有弱口令(后台、FTP、SSH、数据库)、删掉不用的组件和后台入口。
  3. 清后门:按第三步路线五逐项检查清理,authorized_keys、crontab、rc.local、ld.so.preload、陌生服务。
  4. 清 webshell:确定是恶意的文件直接删;不确定的文件,比对官方源码包,用 diff 找出被植入的代码段。
  5. 改密码 + 换密钥:所有相关凭据全部轮换,包括数据库密码。

清理时用一个「隔离目录」而不是直接 rm,便于事后复核:

mkdir -p /root/ir/quarantine
cp -a --parents /www/wwwroot/xxx/upload/shell.php /root/ir/quarantine/
# 确认记录后再删原文件

第五步:加固与验证,防止二次入侵

清理完后,必须做加固,否则大概率复发。最低限度这六条:

  • 上传目录禁止执行 PHP。这是性价比最高的一条,直接在 Nginx 层禁掉:
location ^~ /uploads/ {
    location ~ \.(php|php5|php7|phtml|inc)$ {
        deny all;
    }
}
  • 网站目录对 web 用户只读。chattr +i 或文件系统挂载选项,让 php-fpm 进程无法写代码目录(前面写过的 AppArmor 方案也适用)。
  • 后台加二次防护:IP 白名单、HTTP Basic 认证、或改掉默认后台路径。
  • SSH 只允许密钥登录:PasswordAuthentication no,配合 fail2ban。
  • 开启文件完整性监控并配置实时告警(AIDE + inotify),下次被改你能秒级知道。
  • 装一个轻量查杀做兜底:ClamAV 或专门的 webshell 扫描脚本,定期全站扫一遍。

验证环节不能省。清理完后至少观察一周:

# 一周内新增的可疑文件监控
find /www/wwwroot -type f -name '*.php' -newermt '-7 days' \
  -not -path '*/cache/*' -printf '%TY-%Tm-%Td %TH:%TM %p\n'

# 观察 access.log 里是否还有对已删 shell 的请求(说明攻击者还在尝试)
grep -E '(shell|cmd|eval|upload)\.php' /var/log/nginx/access.log

# 检查是否有新的异常外连
ss -antp state established | grep -v -E '(127.0.0.1|你的可信IP)'

如果一周后 access.log 里还有针对已删文件的请求,说明攻击者手里还留着入口清单,或者有你没找到的后门——回到第三步重新走一遍,重点查 ld.so.preload 和运行中的可疑进程。

一份可以打印出来的应急清单

  1. 隔离网络(安全组收窄),不关机
  2. 保存现场:连接、进程、监听端口、路由
  3. 确定时间线,锁定日志窗口
  4. 五路排查:文件时间戳 / 内容特征 / Web 日志 / 进程网络 / 持久化后门
  5. 两个名单求交集:新出现的 PHP × 被访问过的陌生 PHP
  6. 确定入口,先修漏洞
  7. 按顺序清理:入口 → 后门 → webshell
  8. 轮换全部凭据
  9. 加固六条(上传目录禁执行、目录只读、后台防护、SSH 密钥、FIM 监控、查杀兜底)
  10. 观察一周,确认无复发

最后说句实在话:网站被入侵,几乎 100% 不是「被高手盯上定向攻击」,而是「某个组件有已知漏洞」+「某个口令太弱」+「没装监控所以拖了一个月才发现」。这三件事都不需要多高的技术门槛去防,需要的是持续的执行力。发现入侵时慌是正常的,但按上面的清单一步步走,绝大多数中小站点都能在两小时内完成处置。

Last modification:September 22nd, 2026 at 07:56 pm

Leave a Comment