搜索全站

输入至少 2 个字符,查找相关内容。

↑↓ 选择 Enter 打开Esc 关闭

Web 攻击痕迹还原

日志能告诉你"有人请求了什么",但回答不了三个关键问题:他改了什么?他落地了什么?他删掉了什么?

最后更新 2026-10-09版本 v2.0维护 DigiForensics查看历史 1

一、概述

日志能告诉你"有人请求了什么",但回答不了三个关键问题:他改了什么?他落地了什么?他删掉了什么?

这三个问题必须回到配置与文件系统层解决。

而这一层有个残酷的特点:痕迹会自己消失。 日志能被截断、文件能被删除、时间戳能被批量重置。等到你拿到镜像时,攻击者往往已经清过一遍现场。

但 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