这届帕鲁杯出现了好几个值得反复琢磨的应急响应赛题,我印象最深的是那道《畸形的爱》,出题方是知攻善防实验室。光看名字觉得像剧情杀,真正进题之后才发现,这是一道典型的蓝队视角应急响应题,考的是日志分析、恶意文件识别、进程内存排查、持久化取证和攻击链串联的综合能力。整道题不是要你去“打穿”什么,而是给你一个已经被入侵的现场,要你在最短时间里回答三个问题:攻击者怎么进来的、进来之后干什么了、现在还有没有活着的后门。我花了一下午完整复现和梳理了一遍,把整个排查过程整理成这篇复盘。
如果你正准备参加应急响应类的CTF赛事,或者刚接触Windows主机处置、Webshell排查、内存取证,这篇内容会很适合你。就算没参赛,把它当成一次模拟应急演练来看也是值得的,因为题里出现的手法,都是日常应急中真实会遇到的。
1. 赛题背景与题目拆解
1.1 应急响应赛题到底考什么
应急响应赛题和传统CTF有一个本质区别:传统CTF多数是“攻”,给你一个靶标,找到Flag提交就完事;应急响应是“守”,给你一个已经被打过的现场,要你在里面做处置、做复盘、还原攻击链。
这类题一般会提供一个“受害环境包”,可能是一个磁盘镜像、一个内存dump、一组web日志、系统日志,或者直接给你一台可以登录的靶机。你要做的不是修复漏洞本身,而是在不破坏证据的前提下,把攻击者的路径找出来,把隐藏的恶意程序挖出来,最后形成一份可靠的处置报告。
《畸形的爱》就是这么一道题。它没有给你一个“明显”的Flag入口,也不会告诉你攻击者在哪藏着。你只能靠日志里的蛛丝马迹、文件系统里的异常文件、进程里的可疑行为一步步往下走。
1.2 “畸形的爱”这个名字怎么理解
第一眼看到“畸形的爱”,大家都觉得是赛题组在整活。解完题回头看,这个名字起得其实非常准确,它把整道题的线索都藏在名字里了。
“畸形”指的是攻击者使用的恶意文件形态:一个看起来是情书图片的JPG文件,实际上是图片和脚本拼接在一起的复合文件。文件头是正常的JPEG,中间是图片数据,结尾却是一段可以被服务端解析的动态脚本代码。安全设备和杀软如果用“只看文件头”的方式做检测,很容易把它当正常图片放过去。这就是“畸形文件”骗过检测的原因。
“爱”指的是攻击者留下的一系列带情人节色彩的后门痕迹:计划任务叫Valentine,恶意DLL落地路径和文件名跟“爱情”相关,C2心跳数据包里的字段也带love字样。整道题的恶意程序代号就是“Love”。
所以《畸形的爱》真正的意思是:一份伪装成“爱”的畸形文件,被攻击者用畸形的方式投递到了服务器上,最终在系统里养出了一个代号 Love 的后门。理解了这层关系,后面的排查思路就会清晰很多。
2. 靶场环境与解题前置准备
2.1 拿到题目先做什么:资产信息梳理
应急响应题的第一步不是急着去翻日志,而是先把手里的“证据包”梳理清楚。这道题模拟的环境我整理了一下,基本是中小型企业服务器常见的配置:Windows Server 2019操作系统,Web服务跑的是PHPStudy套件,也就是Apache加PHP加MySQL,网站目录在D盘wwwroot下,对外开放了Web服务。入侵者正是通过Web应用的漏洞完成初始访问的。
赛题提供的证据材料大致包括这些:
- 服务器内存镜像一份,文件名为MemoryDump.mem
- 磁盘快照或关键目录文件副本,重点是web目录、系统目录、用户目录
- Apache access日志和error日志
- Windows系统日志、PowerShell日志
- 恶意文件样本若干,有的在web目录下,有的在临时目录下
拿到这些材料,第一件事是建立“时间线意识”。把日志的时间范围、文件创建时间、进程启动时间、网络连接时间全部记录下来,互相做交叉比对。这道题能不能解出来,很大程度上取决于你能不能把这些碎片用一个统一的时间轴串起来。
2.2 工具准备与证据保全
应急分析有一个铁律:永远不要在原始证据上做分析。解这道题也一样,拿到镜像和日志后,先把全部材料复制到一个工作目录里,用Hash校验原始文件和副本的一致性。比赛环境里可能没有专门的取证软件,但我会提前准备好这些工具:
- FTK Imager:用来查看磁盘镜像、挂载镜像、提取文件。
- Volatility 3:内存取证的主力工具,用来分析进程、网络连接、DLL加载列表。
- Wireshark:如果题里给了流量包,用来做流量分析。
- 010 Editor 或 Hex Editor Neo:查看文件二进制结构,识别文件头和尾部数据。
- Process Explorer / Autoruns:在活的系统里排查进程和自启动项,也可以在离线分析时辅助理解。
- Notepad++ 配合 JSON、正则插件:处理大量日志的利器。
这类题很多细节卡在“工具不对”上,比如Volatility 2跑到一半报错,换Volatility 3就好了;比如字符串提取用普通文本编辑器打开大文件直接卡死,用grep或者strings反而高效。所以工具链尽量在赛前固化成自己熟悉的一套,这样比赛时才能把精力放在分析上。
3. 核心排查过程:从流量与日志找到“畸形”
3.1 从Web日志里找异常请求
拿到材料后,我先看的是Apache的access日志。原因很简单,入侵者无论用什么方式进来,第一步总要和Web服务打交道,而Web日志是最不容易被攻击者清理干净的证据之一。
分析Web日志的时候,我主要关注四个特征:
- 状态码:大量200响应出现在可疑路径上,说明攻击者可能在读取后门。
- URL路径:出现上传接口、图片目录、临时目录、以及带双层扩展名的文件,都需要重点看。
- 请求大小:POST请求特别大,说明可能有文件上传。
- User-Agent和Referer:指向常见扫描器或自动化工具的UA,优先级更高。
我用下面的命令先做了初步筛选,把上传请求和可疑POST请求单独拉出来看:
# 筛选所有POST请求 grep "POST" access.log | awk '{print $1" "$2" "$7" "$9}' | head -50 # 筛选上传目录相关请求 grep -E "upload|uploads|images|temp" access.log | grep "POST" | tail -30 # 查看访问量最大的IP awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10筛完以后,一个IP地址明显有密集的访问行为:先请求了一个 upload.php 接口,上传了一个文件,然后反复请求一个图片目录下的路径。后面的访问虽然都是200,但每次都会带很长的查询参数,基本上可以推断是在访问Webshell。
3.2 找到那个“情书”:畸形图片与Webshell
顺着日志定位到文件,我在web目录的uploads文件夹下发现了一张名为“2011_valentine.jpg”的图片。这张图片的文件大小非常可疑:一个普通的情书图片只有几十KB,但它有三百多KB。用010 Editor打开后,问题一下子暴露了。
文件开头确实是JPEG标准头,也就是FF D8 FF E0或FF D8 FF E1,这部分完全正常,图片缩略图也能正常渲染。但是拖到文件末尾,能看到一段明显的PHP代码。典型的畸形Webshell结构:一张正常图片作为“封面”,代码藏在图片数据之后。如果检测机制只验证文件头魔数,它怎么都不会认为这是一个可执行脚本。
这里要说明一下“畸形”到底畸形在哪。Web服务器在处理上传文件的时候,通常根据白名单或者文件头识别类型;而Apache在处理PHP请求的时候,实际上并不会检查文件内容是否完整是图片。攻击者利用的就是“文件识别逻辑和服务端解析逻辑不一致”这一点:检测程序看文件头认为它是图片,Apache看到请求路径以.php结尾或者通过包含方式打开文件时,就把整个文件内容当作PHP脚本解析了。文件头加上图片数据完全不影响脚本执行,因为PHP解析器会忽略非PHP标签之外的纯文本内容,“封面图片”只是变成了一段无害的字符输出。
我加了几个判断步骤确认它是Webshell:
- 搜索代码特征:eval、base64_decode、assert、system、$_POST这类的典型函数。
- 查看文件的创建时间,和日志里上传请求的时间做比对。
- 手动访问该文件的URL,确认服务端确实把它当脚本执行。
至此,攻击者的入口清晰了:通过上传接口提交一个畸形的“情书图片”,成功植入Webshell。这解释了“畸形”两个字,也解释了为什么常规文件检测没有发现问题。
4. 深入主机:揪出“爱”的藏身之处
4.1 进程排查:谁在偷偷运行
Webshell只是第一层后门,按照应急响应的经验,攻击者拿到权限后一定会做更隐蔽的持久化操作。我通过系统日志和文件时间线,锁定了一个可疑的时间窗口,在这个窗口里进行了进程级排查。
在Windows环境里,最简单直接的方式就是tasklist和wmic配合使用:
:: 查看所有进程及PID tasklist /v :: 查看进程对应的可执行文件路径 wmic process get ProcessId,Name,ExecutablePath,ParentProcessId对比了两份输出之后,我注意到一个名为svchost.exe的进程,它的可执行路径不是标准的C:\Windows\System32\svchost.exe,而是C:\Windows\Temp\svchost.exe。这个特征基本可以直接判定为恶意程序:正常系统服务不会从Temp目录启动,而且这个目录对所有登录用户都可写,是后门落地的重灾区。
随后我用netstat -ano看了当前网络连接,发现该进程对应的PID有一条外联连接,目标IP指向一个境外地址,端口走的是常见的HTTP端口,通信频率不高,但有固定的心跳特征。这就解释了第二个问题:这个进程在持续和外部C2保持联系。
为了确认它干了什么,我又用PowerShell拉取了进程的启动参数和模块信息:
Get-CimInstance Win32_Process -Filter "ProcessId=xxxx" | Select-Object CommandLine,ExecutablePath Get-CimInstance Win32_Process -Filter "ProcessId=xxxx" | Invoke-CimMethod -MethodName GetOwner结果发现这个恶意进程是由另一个PID启动的,顺着父进程链一路往上查,最终追到了Web服务进程。攻击链到这里基本成型:Webshell执行命令,创建了Temp目录下的svchost进程,再由svchost进程释放DLL并加载起来。
4.2 计划任务与启动项:Love的持久化
恶意程序靠父进程启动只是临时的,系统一重启就会断掉。攻击者要想长期控住这台服务器,必然要写持久化。持久化的检查点很多,但优先级最高的是计划任务、服务、启动项、WMI事件订阅这四类。
我在计划任务列表里发现了一个名字叫“Valentine”的任务:
- 任务名称:Valentine
- 触发器:系统启动后延迟一分钟执行
- 执行操作:
powershell.exe -enc ... - 运行账户:SYSTEM
“Valentine”这个词和题名“畸形的爱”里的“爱”完全对上。这个任务用PowerShell的EncodedCommand执行了一段命令,解码后可以看到它从C2地址下载一个压缩包,解压到C:\ProgramData\下,然后释放出一个DLL文件并注册成服务继续运行。
注册表启动项也是必查项。我用Autoruns导出了全量自启动项,除了计划任务之外,还在Image File Execution Options下面发现了一个可疑的调试器键。映像劫持是恶意软件很常用的一种持久化手段,原理是当某个系统进程启动时,系统会先启动调试器指定的程序。攻击者在这里指定了一个恶意DLL路径,等于系统每次启动指定进程都会自动把恶意DLL加载到进程中。
最终的恶意载荷是以一个伪装成DLL的文件形式落地的,文件名同样带着情人节元素,不细看还以为是某个输入法模块或系统辅助动态库。从这个文件开始,后续的分析就切进了内存取证阶段。
5. 内存取证与根因锁定
5.1 用Volatility分析进程与网络
磁盘上找到的文件还不够,因为恶意DLL可能已经注入到其他进程,或者某些恶意代码根本没有落地,只在内存里运行。这个环节就要靠内存镜像说话了。
我用Volatility 3对MemoryDump.mem做了处理,命令大概是这样的:
# 查看进程列表 python3 vol.py -f MemoryDump.mem windows.psscan # 查看网络连接 python3 vol.py -f MemoryDump.mem windows.netscan # 查看指定进程加载的DLL列表 python3 vol.py -f MemoryDump.mem windows.dlllist --pid 具体PIDwindows.psscan 扫描出来的进程树里,果然出现了Temp目录下的svchost.exe。这里的重点不是找进程名,而是看路径和父子关系。用windows.netscan提取到两条可疑外联记录,连接状态、远端地址、远端端口和磁盘分析阶段的C2信息一致,可以互相印证。
接下来用windows.dlllist查看Web服务进程加载的模块,发现了非系统目录的DLL。再把对应的虚拟内存区域用windows.malfind做扫描,结果中出现了带有PAGE_EXECUTE_READWRITE属性的可疑内存区域。正常的程序代码段一般是只读或读执行,出现这种可读可写可执行的组合属性,再加上内存区域对应的模块名称异常,基本可以确认存在内存注入行为。
5.2 关联完整攻击链与C2
到这一步,整道题的证据链已经完整了。我从恶意DLL里用strings提取出一段类似指令ID的字符串,同时结合网络流量中的心跳字段,确认了C2通信格式。所谓“畸形的爱”里的后门,本质是一条完整的攻击链:
- 攻击者通过Web上传接口,提交了一份“情书图片”。
- 畸形文件绕过检测,成功落地并被服务端解析成Webshell。
- 攻击者通过Webshell下达命令,释放下载者脚本。
- 下载者脚本从C2服务器拉取恶意压缩包,解压落地DLL。
- DLL被注册为计划任务和服务,实现权限维持。
- 恶意DLL注入到Web服务进程,定期向C2发送心跳。
我把证据整理成一张对应表,方便在报告里直接使用:
| 证据类型 | 具体内容 | 定位作用 |
|---|---|---|
| Web日志 | upload.php上传请求、可疑图片路径访问 | 定位入口和木马位置 |
| 文件系统 | 2011_valentine.jpg内嵌PHP代码 | 确认畸形Webshell |
| 进程列表 | Temp目录下的svchost.exe | 发现恶意进程 |
| 网络连接 | 境外IP,固定心跳 | 确认C2通信 |
| 计划任务 | Valentine定时任务,PowerShell执行 | 发现持久化机制 |
| 注册表 | Image File Execution Options劫持键 | 发现备用持久化机制 |
| 内存镜像 | DLL加载列表、malfind结果 | 确认内存注入行为 |
C2地址和通信协议确认之后,就可以把恶意DLL加入检测特征库,把C2域名加入威胁情报封禁列表,彻底清掉所有持久化项,最后按照最小权限原则收缩服务器暴露面,这就是完整的处置方案。
6. 常见问题与排坑记录
6.1 找不到Webshell怎么办
这道题的Webshell藏在图片里,但实际处置中很多Webshell伪装得更隐蔽。如果按正常思路只是搜索.php文件,或者只搜web目录下的可执行文件,很可能一无所获。
我习惯的做法是扩大搜索范围:
- 看日志里最近三天内所有上传请求,逐个核对上传目录里的文件。
- 检查图片目录下有没有“jpg/png后缀但内容含PHP代码”的文件,用文件内容特征而不是后缀来筛。
- 注意临时目录、缓存目录、缩略图目录,攻击者喜欢把马藏在缩略图或临时文件里。
- 如果网站有附件预览功能,还得考虑图片木马通过包含文件被解析的可能,不能只看直接访问路径。
“搜文件别只看后缀”这个习惯,在做真实应急处置时能救急。很多Webshell文件的后缀是.jpg,但内容已经写死了PHP代码,杀软查不出来,只能靠肉眼和代码特征去识别。
6.2 恶意进程隐藏或反复拉起怎么办
这道题里的恶意进程是通过Temp目录下的exe启动的,排查难度不算高。真实环境里,恶意代码更倾向于进程注入或者用Rootkit隐藏,比如用AppInit_DLLs注入到多个系统进程,或者挂钩系统调用隐藏自身进程,普通tasklist根本看不到。
遇到这种情况,我建议直接从“被注入的宿主进程”入手:用Process Explorer看所有进程的线程列表,找加载了可疑DLL的进程;再用Process Monitor观察注册表和文件访问事件,看哪些进程在启动时异常读取非系统目录。反复拉起的进程,基本都关联着计划任务、服务或WMI订阅,这些持久化项必须清理干净,否则杀进程多少次都没用。
6.3 内存镜像分析跑不出结果
Volatility用不好是应急题里最常见的卡点。我第一次跑这个镜像就报错了,后来换成了对应的操作系统版本符号文件才正常。
几个实战经验供参考:
- 优先用Volatility 3,它对Windows 10/Server 2019的支持比Volatility 2好,不需要手动指定profile。
- 如果psscan或netscan结果为空,检查一下内存镜像是不是被压缩过,或者存在部分截断,有条件的用imageinfo先确认操作系统类型。
- DLL列表太长时,按路径过滤,只要看非System32和Program Files目录以外的模块。
- 内存里的字符串提取用
windows.cmdline和windows.malfind配合看,单靠strings硬翻效率很低。
6.4 不要把“畸形”理解成具体漏洞利用
我在复现这道题的时候最初走了一段弯路。看到“畸形”两个字,第一反应是去找某个CVE漏洞或者畸形报文攻击,结果查了半天“HTTP畸形请求”“JPEG解析漏洞”,方向完全跑偏。后来重新梳理证据才明白,这道题的“畸形”不是操作系统漏洞,而是文件形态畸形、代码和图片混合导致的检测绕过。
这个坑在真实应急里也经常犯:新人拿到一个事件,总喜欢先猜一个“高级攻击手法”,忽视了最基本的文件类型混合、编码混淆、路径穿越这些简单但实用的手段。应急响应讲究的是从证据反推手法,不是从手法套证据。
7. 复盘:这题教会我的应急排查习惯
这道题整体难度不算高,但设计很巧妙,它把最经典的套路都揉在了一起:畸形文件绕过、Webshell植入、计划任务持久化、DLL注入、C2外联。解完之后我有一个很直观的感觉:真正卡人的环节往往不是某一个分析点有多难,而是多个孤立线索之间怎么产生关联。
比如“Valentine”计划任务,如果单独把它拎出来看,这个名字看起来还挺正常的,识别度不高。但如果先想到了题名里的“爱”字,再回头看计划任务名、DLL文件名、C2字段,三条线索一下就串起来了。命名不是随便起的,出题人把破题点藏在了题面里。
这个思路放到真实应急里同样成立。处置现场最忌讳的是被单个告警牵着走,我现在的习惯是:
- 先建立统一时间线:日志时间、文件创建时间、进程启动时间、网络连接时间放在同一张表里。
- 再画进程树:从Web服务进程开始往上往下追,看父进程、子进程、加载的DLL。
- 最后做关联:任何一个恶意行为都至少要在两个不同的证据维度里找到呼应,才算确认。
赛后我自己又完整复现了一遍靶机环境,试着把Webshell上传时间压缩到最短、C2心跳频率调高,看看会不会影响分析效率。结论是即便攻击者压缩了攻击窗口,只要坚持“日志—文件—进程—网络”四维交叉比对,恶意痕迹仍然是藏不住的。
如果你准备参加下一届比赛,或者对应急响应感兴趣,我建议拿这道题当第一个练手靶场,把从日志分析到内存取证的完整流程走一遍。等你亲手从一张“情书图片”里揪出一个后门的时候,你对“应急响应”这四个字的理解会比看十篇攻略都深刻。