一、概述
日志能告诉你"有人请求了什么",但回答不了三个关键问题:他改了什么?他落地了什么?他删掉了什么?
这三个问题必须回到配置与文件系统层解决。
而这一层有个残酷的特点:痕迹会自己消失。 日志能被截断、文件能被删除、时间戳能被批量重置。等到你拿到镜像时,攻击者往往已经清过一遍现场。
但 NTFS 有一个决定性的优势:删除文件不会抹掉 MFT 条目。
把一个文件的 MFT 条目标记为"未使用"(flags 里的 0x06)只是修改了 $MFT 这个元数据文件自身的内容,没有产生第二个"删除 MFT 条目"的操作,因此 REDOP/UNDO 机制不会为这次删除生成一条可直接还原的撤销记录。
需要说清边界:$LogFile 记录的不只是非元数据文件的写事务,NTFS 的索引更新、元数据修改同样会以事务形式落进 $LogFile。区不要在于这次删除没有留下可供 fls/istat 复原的独立事务线索——它表现为元数据文件内部的一处字节改写。
所以准确的说法是:文件名、父目录引用、四个时间戳在 $MFT 的已删除记录里完整可读,$LogFile 则是补充而非主要来源。
这构成本篇的方法论基础:攻击者能删文件,删不掉"文件曾经存在过"这个事实在 MFT 里的投影。
在取证流程中的位置:本篇属于分析环的主机侧还原层,与日志侧互为闭环——日志产出"哪些请求可疑",本篇产出"主机上发生了什么",两边对上才构成一条可采信的证据链。
上游依赖 Web 访问日志深度分析 的可疑请求集与 IIS 配置与 W3C 日志 的站点映射;下游支撑归因结论与报告里的攻击链叙述。
涉及的证据形态(按攻击链六阶段组织,具体到文件):
| 阶段 |
痕迹落点 |
取证价值 |
| 配置篡改 |
Web.config(含上传目录里的子目录配置) |
★★★★★ 攻防焦点,被改过 ≠ 被攻击者改的 |
|
applicationHost.config + .bak |
★★★★★ diff 即篡改证据 |
| DoS / 藏身 |
App_Offline.htm |
★★★★ 内容与业务无关就是线索 |
| 加载链落地 |
bin\*.dll、Global.asax、App_Code\ |
★★★★★ 无源码的 DLL 是首要嫌疑 |
|
预编译 App_Web_*.dll |
★★★★ 原始文件被删也能反编译出 IL |
| 密钥泄露 |
Web.config 的 <machineKey>、machine.config 默认值 |
★★★★ 配置弱点 ≠ 已被利用 |
| 不常检查的面 |
Windows 上的 PHP 站点 |
★★★ 团队惯性盲区 |
| 清理痕迹 |
$MFT 的 0x06 条目、$UsnJrnl 删除记录 |
★★★★★ 删掉了什么,只有这里能答 |
| 反取证 |
Security 1102(清日志)、4719(改审计策略) |
★★★★★ 最强单点证据 |
| 时间戳 |
正常文件成簇,落地文件离群 |
★★★★★ 批量重置会破坏这个分布 |
★ 判读方法贯穿全篇:正常项目的文件时间戳会成簇(一次部署一批文件),而离群的单个文件——尤其"只有一个文件的时间戳与所有其他文件都差很远"——是篡改或落地的首要嫌疑。这个方法对整站目录都适用,不限于 bin。
Web.config 相对基线改了什么、是谁改的;bin 目录里哪个 DLL 没有对应源码;Global.asax 是否被植入了 Application_Start 逻辑;哪些文件被删除了(哪怕已删);时间戳是否被批量重置;攻击者清理现场的动作顺序与时间(4719 → 1102 → IIS 日志截断)。
攻击者执行了什么命令(文件层看不到执行过程,要看 4104 与事件日志);ViewState 反序列化是否真的发生过(明文 machineKey 是配置弱点,攻击是另一件事);已删文件的完整内容(MFT 给你元数据,不给数据流——内容要靠未分配空间与预编译 DLL);配置被改的具体操作者(要靠事件日志的 4672/4688 配合审计策略)。
内容边界(本篇的红线):本篇讲的是如何识别配置篡改、加载链异常与清理痕迹,以及如何把痕迹串成攻击链。不提供任何配置篡改方法、后门 DLL 的构造方式、ViewState 载荷的生成手法或清理现场的操作步骤。
提到"上传目录里的 web.config"时,必须同时写出两种截然不同的来源——防守方加的(关脚本执行)和攻击者加的(改解析行为),区不要在内容,不在存在本身。
读者前提:需要 NTFS 元数据与 MFT 结构(见 NTFS 结构与 MFT 解析)、IIS 应用池与 ASP.NET 生命周期、反取证基本概念。
建议按 3.6 拿到一个可疑 IIS 站点后的 Runbook 的顺序执行——本章的六个步骤有依赖关系,跳步会让"清理痕迹"那一步失去参照基线。
二、核心原理
2.1 攻击链六阶段与各自的痕迹落点
| 阶段 |
攻击者做什么 |
日志侧痕迹 |
文件/配置侧痕迹 |
注册表/事件日志侧痕迹 |
| ① 信息收集 |
扫端口、扫路径、扫备份文件 |
大量 404、404/1(虚拟主机扫描)、工具 UA |
无 |
4625(若扫到 RDP/SMB) |
| ② 漏洞利用 |
打 SQL 注入、文件上传、越权 |
注入载荷在 cs-uri-query、500/0 集中在某时段 |
无(或产生临时文件) |
应用日志的异常堆栈 |
| ③ 上传落地 |
传 webshell、后门 DLL |
PUT/201、对上传路径的后续访问 |
上传目录新增 .aspx/.config/.dll |
— |
| ④ 权限维持 |
改配置、建服务、建任务 |
站点行为突变 |
Web.config 被改、bin\ 新增 DLL、Global.asax 被改 |
7045 新服务、任务缓存新增 |
| ⑤ 横向移动 |
用同一凭据打内网其他主机 |
— |
落地凭据类文件 |
4624 LogonType=3、4648 |
| ⑥ 痕迹清理 |
删文件、清日志、改时间戳 |
日志文件体积异常、日期缺失 |
时间戳批量同值、文件消失但 MFT 留痕 |
1102(清日志)、4719(改审计策略) |
★ 关键认识:③ 之后的所有痕迹,取证价值高于 ①②。① ② 只留下"网络行为",③④ 留下"主机状态变更"——状态变更比行为更难伪造,因为它改变了系统本身。
2.2 ★★ Web.config:攻防焦点,且是双向的
Web.config 是 ASP.NET 应用的行为开关文件。它的特殊之处在于:它既是攻击者最常下手的目标,也是防守方最常加固的地方——因此"被改过"这个事实本身需要仔细分析是谁改的、改了什么。
取证时必须能回答"这个 Web.config 相对基线改了什么"。
判断基线有三个来源,按可靠性排序:
| 基线来源 |
可靠性 |
说明 |
同站点的历史副本(版本管理、备份、bin 内的历史文件) |
★★★★★ |
唯一能确定"改了什么" |
applicationHost.config.bak |
★★★★ |
只覆盖站点级配置(绑定、认证、日志目录),不覆盖应用内 Web.config |
| 同类站点的通用模式 |
★★ |
只能提示"这里有个不常见的元素" |
必须重点检查的配置元素:
| 元素 |
攻防含义 |
取证判读 |
上传目录里的 web.config |
子目录配置可关闭脚本执行(移除 handler 映射),也可改变该目录的解析行为 |
★★★★★ 上传目录出现它本身就是高危信号——业务没理由在那里放配置 |
<httpHandlers> / <handlers> |
扩展名重新映射到不同处理程序 |
正常文件突然被新 handler 接管 → 行为改变 |
<authorization> 段 |
某目录被设为允许匿名 |
原本需认证的管理路径变公开 |
<customErrors mode="Off"> |
关闭错误详情 |
异常页回显堆栈,这是攻击者的侦察手段 |
<compilation debug="true"> |
生产开调试 |
严重降低防护 |
| 连接字符串 |
<connectionStrings> 中的数据库账号 |
明文。被改过可能意味着账号变更或数据外传 |
★ 判读要点:上传目录里的 web.config 有两种截然不同的来源——防守方加的(关脚本执行)和攻击者加的(改解析行为)。区不要在内容,不在存在本身。必须读出内容再判断,不能看到 web.config 就定性。
2.3 App_Offline.htm:DoS 与藏身的双用途文件
App_Offline.htm 是 ASP.NET 的内置约定:一旦应用根目录存在这个文件,ASP.NET 会立即停用该应用(所有请求返回该文件内容或 503),并在文件被删除后自动重启应用。
| 用途 |
表现 |
取证识别 |
| 正常停机维护 |
放置公告页,站点暂时下线 |
内容是 HTML 公告,时间戳对应维护窗口 |
| ★ 攻击者 DoS |
短时间使站点完全不可用 |
内容不是公告,或时间戳落在攻击时段 |
| ★ 痕迹藏身 |
把敏感内容写进去——访问者以为在取公告,实际取走了内容 |
内容与业务完全无关 |
为什么它是理想的藏身处:它在应用根目录,本来就该有 HTML,不违和;且访问不需要认证。
★ LastAccessTimeUtc 是这里的额外价值点:App_Offline.htm 被访问意味着有人取走了它的内容,而NTFS 默认启用 Last Access Time 更新(虽然很多系统实际关闭,需确认)。
若 LastAccessTimeUtc 显著晚于 LastWriteTimeUtc,说明文件写好后被人取走过。
2.4 bin 目录与 Global.asax:加载链上的两个位置
ASP.NET 应用启动时会自动加载特定位置的程序集与文件,这条"加载链"是后门 DLL 的天然落点。
| 位置 |
加载时机 |
攻击用途 |
取证判读 |
bin\*.dll |
应用启动时全部加载 |
放后门 DLL,随应用启动自动执行 |
目录里出现无对应源码、无法归类、命名不符规范的 DLL |
Global.asax |
应用启动事件 |
在 Application_Start 里植入代码 |
文件 LastWriteTime 与其他源码文件明显不同批 |
App_Code\ |
目录里的 .cs 运行时编译 |
放后门源码,改完即时生效 |
正常项目极少用(除非明确开启 CodeDom) |
App_Data\ |
不编译,直接可下载 |
放配置文件、上传文件 |
存放了本该在配置文件里的敏感内容 |
预编译 *.dll(Temporary ASP.NET Files) |
运行时缓存 |
— |
%windir%\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\ 下的编译产物能反编译出源码 |
★ 预编译 DLL 的取证价值:bin\ 里的 App_Web_*.dll 是预编译应用程序集。即使原始 .aspx 和 .cs 都被删了,预编译 DLL 仍保留着可反编译的 IL 代码——这让"已删除的落地文件"有了恢复途径。
★ 判读方法:正常项目的文件时间戳会成簇(一次部署一批文件)。离群的单个文件——尤其是"只有一个文件的时间戳与所有其他文件都差很远"——是篡改或落地的首要嫌疑。这个方法对整站目录都适用,不限于 bin。
2.5 ★ ViewState:加密了,但密钥在配置文件里
ASP.NET 的 ViewState(视图状态)是页面状态数据的序列化载体,默认加密并带 MAC 校验。
但它的加密密钥(machineKey)保存在 Web.config 的 <machineKey> 元素里,或在 machine.config(全局默认)里。换句话说,密钥和被保护的数据放在同一套配置体系内,取证时要把它们当成一个整体看。
攻击链:
从某个途径获得 machineKey → 自行构造合法 ViewState 载荷
→ 触发 ViewState 反序列化 → 远程代码执行
这里描述的是链条的构成,不是构造方法。取证要判断的是这条链走完了哪几步。
取证要点:
| 观察项 |
意义 |
Web.config 中 <machineKey> 是明文自定义值而非 AutoGenerate |
说明密钥是硬编码的。若该文件被攻击者读取过,密钥即泄露 |
machine.config(%windir%\Microsoft.NET\Framework64\v4.0.30319\config\machine.config)中的默认值 |
全局默认密钥,发布后从未修改同样危险 |
请求中出现异常长的 __VIEWSTATE 参数 |
载荷体积远超正常页面状态 |
请求中出现 __VIEWSTATEARGUMENT |
少见,可能在探测不同 ViewState 处理路径 |
站点 500/0 集中爆发且响应体含序列化异常 |
反序列化失败的典型表现 |
★ 定性边界:不能仅凭"machineKey 是明文"就断言"发生了反序列化攻击"——明文密钥是配置弱点,攻击是另一件事。报告必须分两栏写:前者是事实,后者需要 __VIEWSTATE 异常载荷或 500 异常佐证。
2.6 PHP on Windows:不常被检查的面
服务器上跑的不只有 ASP.NET。PHP 也很常见,尤其是 CMS 和运维工具。它的几个配置项在取证上有意义:
| 配置项 |
作用 |
取证意义 |
auto_prepend_file |
每个 PHP 脚本前自动包含指定文件 |
被设置为非空且指向不明路径 → 高危,等效于全局植入 |
auto_append_file |
每个脚本后自动包含 |
同上 |
short_open_tag |
允许 <? 短标签 |
开启会扩大代码注入面 |
disable_functions |
禁用的 PHP 函数 |
被清空 → 攻击者已尝试提权 |
extension_dir / 加载的扩展 |
扩展路径 |
指向非标准目录的 .dll 可能是恶意扩展 |
# php.ini 的关键项(★ auto_prepend/append_file 非空且指向不明路径 = 高危)
Select-String -Path C:\php\php.ini,C:\inetpub\php\php.ini -Pattern 'auto_prepend_file|auto_append_file|short_open_tag|extension_dir'
# 全盘找可疑的 PHP 落地(★ 注意 web 根之外也可能被 include)
Get-ChildItem C:\ -Recurse -Include *.php -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -notmatch '\\inetpub\\wwwroot\\digiforensics-site\\' -and $_.LastWriteTimeUtc -gt (Get-Date).AddDays(-7) } |
Select-Object FullName, Length, LastWriteTimeUtc | Select-Object -First 30
2.7 ★ 清理痕迹的识别:删除动作的四种残影
攻击者清理现场有四种常见手法,每种都留下不同的残影。
| 手法 |
残影 |
提取方式 |
| ① 删除文件 |
MFT 条目仍在,flags 含 0x06(未使用),保留文件名与时间戳 |
fls -r -p 列出已删除文件;istat <inode> 读元数据 |
| ② 批量重置时间戳 |
一批文件时间戳完全相同(到秒) |
按时间戳分组,"只有 1 个分组的批"即异常 |
| ③ 清空后重建日志 |
事件日志** 1102**;文件创建时间晚于应有起始时间 |
查 1102;比对 .evtx 的 CreationTime 与最早记录 |
| ④ 改审计策略 |
事件日志** 4719** |
查 4719,通常紧邻 1102 |
★ 关于 ① 的技术细节(这是本篇最重要的一段):
- NTFS 删除文件时,只是把该文件在
$MFT 中对应的主记录($FILE)标记为"未使用",具体是 flags 字段置 0x06(0x02 | 0x04,即"未分配 + 已删除")。
- 关键点:这次修改是对
$MFT 这个元数据文件自身内容的修改。删除一个 MFT 条目不会生成"删除 MFT 条目"这个独立操作,因此 $LogFile 里不会有一条可直接还原本次删除的撤销记录——它体现为元数据文件内部的一处字节改写。但不能说 $LogFile 完全不记录元数据变更:索引更新、元数据修改同样会落成事务,只是本次删除不留下可复原的独立线索。
- 取证含义:MFT 是"已删除文件"的主要信息源。文件名、创建/修改/访问时间、父目录引用(指向父目录 MFT 记录)都还在。只有文件内容(
$DATA 属性的数据流)通常已被释放给其他文件复用,除非紧接着被恢复占用。
- 内容恢复路径见文件雕刻与签名恢复——用
$DATA 的未分配空间按签名雕刻。
# ★ 列出站点目录下的已删除文件(flags 含 0x06 的会带 *deleted* 标记)
fls -o 2048 -r -p /work/DigiForensics.dd | grep -A2 -i 'wwwroot/digiforensics-site' | grep -i deleted
# 读某个已删除 MFT 条目的完整元数据(含时间戳、父目录)
istat -o 2048 /work/DigiForensics.dd <inode>
# 在全盘未分配空间里按内容签名雕刻(寻找已删除的 .aspx/.asax 内容)
blkls -o 2048 /work/DigiForensics.dd > /work/unalloc.raw
foremost -i /work/unalloc.raw # 或 testdisk / scalpel
★ 报告表述要点:MFT 中存在已删除条目,只能证明"该文件曾存在过",不能证明"它的内容是什么"。若要断言内容,必须有雕刻出的字节数据或 $DATA 未被复用。两者是不同强度的结论,不能混用。
2.8 ★ 清日志本身会留下 1102:反取证的最强单点证据
| 事件 |
通道 |
含义 |
为什么是铁证 |
| 1102 |
Security |
Security 日志被清除 |
清理动作本身被记录进了另一个日志——清 Security 的人常忘了 System 里的这条 |
| 4719 |
Security |
系统审计策略被修改 |
通常先禁用审计再清理,4719 早于 1102 |
| 104 |
System |
事件日志被清除(非 Security 通道) |
覆盖 Application/System 等其他通道 |
| 6008 |
System |
异常关机 |
清理后可能直接断电,"没清干净"的痕迹 |
★★ 最有说服力的取证结论往往是反取证本身。 攻击者花大力气隐藏入侵痕迹,却在清理动作上留下了新的痕迹——4719 → 1102 这对组合在时间上紧密相邻,几乎不可能由正常运维产生。报告里"存在刻意反取证"这一条的证据强度,通常高于"发现了某个可疑文件"。
三、操作步骤
3.1 第 1