ADS 是不断让初级分析师感到意外的 NTFS 特性。一个文件可以拥有多个 $DATA 属性。无名的那个对应 dir 显示的文件大小。其它的,即标识符中带冒号的属性,对大多数人惯用的工具都是不可见的。攻击者知道这一点。Microsoft 知道这一点。审阅你 IR 报告的审计人员也应该知道。
机制简述
在 MFT 记录中,属性类型 0x80 即 $DATA。属性头携带一个可选名称。一条记录可以承载多个 $DATA 属性:一个无名的(主数据流)以及任意数量的命名属性。在用户态使用 filename:streamname 格式访问它们。
echo payload > readme.txt:nope
type readme.txt:nope
按资源管理器显示的任何度量,readme.txt 的大小都是 0 字节。命名为 nope 的数据流保存了真正的载荷。同一条 MFT 记录、同一个父目录索引项、两个独立的数据属性。
这不是 bug。这是 1993 年的设计决策,NTFS 从 HPFS 继承而来,用于让 Services for Macintosh 附加 resource forks。今天几乎没有人为此使用 ADS。包括 Microsoft 在内的其他人,将其用作临时元数据的载体。
Windows 自身在哪里放 ADS
Zone.Identifier 是最著名的例子。Edge、Chrome、Firefox 和 Outlook 都会在任何跨越安全边界的文件上添加它。内容是一段 INI 片段,包含来源 URL、引荐来源以及触发 Office 与 SmartScreen 的 Mark-of-the-Web 防护的 ZoneId=3 行。如果你在读 MFT,发现 Downloads\ 下的某个二进制文件没有 Zone.Identifier,那就要问为什么。可能有人运行了 Unblock-File,或者文件经过一个会丢弃数据流的归档工具被复制。
其他 Microsoft 认可、你会遇到的数据流:
- 亚洲语言键盘布局文件上的
$KSP。 - 保存到磁盘上的 Outlook 附件上的
OECustomProperty等。 - WIMBoot 和 CompactOS 下压缩文件上的
Wof重解析数据。 - Defender 已经指纹化的可执行文件上的
SmartScreen。 - ConfigMgr 缓存目录上的
encryptable。
这些非常常见。在把数据流标记为可疑之前,先了解你的基线长什么样。
攻击者实际上拿 ADS 做什么
反复出现三种模式:
- 载荷藏匿、独立加载。 可见文件是无害的(Word 文档、PDF、许可证文件)。命名数据流承载真正的 PE。WMI、
wmic process call create或rundll32直接启动legit.docx:payload.exe。如今 Defender 会扫描命名数据流,但许多 EDR 在数据流由受信任进程创建时仍然不会扫描。 - Living-off-the-land 藏匿点。 把 PowerShell 载荷写入
ads:script.ps1,然后用Get-Content -Stream执行。在红队驻场服务里出奇地常见,因为它能躲过仅基于路径的 IOC 搜索。 - 借助系统文件的备用数据流持久化。
C:\Windows\System32\drivers\etc\hosts:backdoor是教科书例子。hosts 文件看起来未被触动,时间戳甚至可能保持原样,而大多数文件完整性监控工具都不会看无名数据流之外的东西。
共同特征:用 dir 看不到,在资源管理器中看不到,对可见文件做哈希也看不到,但读 MFT 记录就能看到。
在在线主机上检测
dir /R 在 cmd 中枚举数据流。PowerShell 的 Get-Item -Stream * 也能做到,输出更整洁。Sysinternals 的 streams.exe 可以递归遍历整棵树。三者在所处卷上都能正常工作。但当卷处于离线状态,或者你不知道该检查哪些文件时,它们都帮不上忙。
dir /R C:\Users\bob\Documents\
Get-ChildItem -Recurse | ForEach-Object { Get-Item $_.FullName -Stream * } |
Where-Object Stream -ne ':$DATA'
第二条 one-liner 枚举一棵树上所有非默认数据流。在全新 Windows 安装上运行,你就已经能得到数百条命中,绝大多数是 Zone.Identifier。把范围缩到大于几百字节的数据流,噪声会迅速减少。
为什么 MFT 是更好的视角
主机上的命令按文件枚举。它们会漏掉你没想到要检查的东西。MFT 不会。每一个 $DATA 属性,无论是否命名,都以属性类型 0x80 写入记录。遍历表,把每条记录的所有 $DATA 属性连同名称和大小一并列出,你就用一次扫描得到了卷上所有数据流的详尽清单。
按文件枚举无法给你的几件事:
- 位于你根本不会去查的目录下的文件上的数据流,包括
C:\Windows\Temp\和C:\$Recycle.Bin\。 - 已删除文件上的数据流。MFT 记录在删除后仍然存在;ADS 依旧列在属性流中。Sleuth Kit 的
fls与istat会显示它们,MFTECmd 与omerbenamram/mftCLI 也会。 - 内容直接驻留在 MFT 记录中的常驻数据流。小载荷(PowerShell loader、base64 块、编码后的配置)常常能塞进 1,024 字节记录的空闲处。无需触碰数据区,即可直接从 MFT 提取读到它们。大小阈值见 resident data。
- 数据流名称。MFT 保留攻击者选择的名称。在文件大小列表里
:payload.exe看起来很无辜;当你对属性转储 grepexe时它就会跳出来。
我实际使用的取证过滤器
用 MFTECmd 或 mft_dump 解析完 MFT 之后,我导出所有拥有多于一个 $DATA 属性的记录。然后剔除:
- 额外数据流名称为
Zone.Identifier且小于 1 KB 的项。 - 位于
\Windows\WinSxS\下的项(清单存储,里面全是合法数据流)。 - 位于厂商签名应用目录下、数据流为已知缩略图或元数据键的项。
剩下的就是工作集。在一台干净工作站上通常少于一百条记录。在发生过有趣事件的主机上,差异会立刻浮现。
把剩余项与 USN journal 交叉比对,查看每个数据流何时被创建或最后修改。一个在 02:14 出现、之后未被触动的命名 $DATA 属性值得仔细看看。如果有的话,再配合 Sysmon 与 Security 事件日志:Sysmon Event ID 15(FileCreateStreamHash)是唯一一个在记录 ADS 创建的同时捕获数据流内容哈希的事件 ID。
关于 Defender 的提醒
现代 Defender 默认会扫描 ADS。对于"把已知恶意 PE 丢进数据流"这种简单粗暴的模式,它确实很擅长。对藏在数据流里的小段混淆 PowerShell,它表现较弱,而且并不总是触碰排除路径下的数据流。不要把 Defender 的沉默视作不存在的证据。
延伸阅读
- Microsoft,File Streams。官方机制说明,包括复制所有数据流的
BackupRead/BackupWriteAPI。 - Sleuth Kit 的
istat与fls,离线枚举可显出 ADS。 - Microsoft 的 Mark of the Web 文档,说明
Zone.Identifier的实际作用以及 Office 如何使用它。