← 返回博客

MFT 的四个时间戳,以及 timestomping 在其中如何显形

· 阅读 2 分钟

每条 MFT 记录都把四个时间戳存放在两个独立属性里。也就是每条记录八个数字。它们并非总是同步。这两组之间的关系,以及它们崩坏的地方,是 NTFS 时间分析的基础。如果你只学一个 MFT 取证的细节,就学这个。

NTFS 记录的四个时刻

$STANDARD_INFORMATION(SI,属性类型 0x10)与 $FILE_NAME(FN,属性类型 0x30)都为同样四个事件记录时间戳:

  • Created:文件首次被写入卷的时刻。
  • Modified:文件内容最后变化的时刻。
  • Accessed:文件最后被读取的时刻。
  • MFT-modified:记录本身最后被更新的时刻(任何属性的改动,不仅是数据)。

时间戳以 64 位 Windows FILETIME 格式(自 1601-01-01 UTC 起的 100 纳秒计数)保存。粒度为 100 纳秒。位数足够,自然事件会在低位留下噪声。

为什么会有两组

$STANDARD_INFORMATION 是用户态看到的那组时间戳。Win32 API 的 SetFileTime 写入的就是这里。任何会触碰时间戳的常规文件操作都会更新 SI:写、读(取决于访问时间设置)、重命名、属性变更。

$FILE_NAME 是早期 NT 出于 POSIX 子系统兼容性引入的,并因历史原因保留下来。它仅在名称变化时更新:创建(首次设置名称)、重命名(名称变化)、跨目录移动(在 NTFS 意义上也是重命名)。此后只要文件保持那个名字,FN 就保持不变。

实际效果是:SI 时间戳一直在动,FN 时间戳保持稳定。正是这种不对称使得 timestomping 可被检测。

Timestomping 都做了什么

修改时间戳的工具通常会调用 SetFileTime。它写入 SI。不动 FN。timestomping 之后:

  • SI 显示攻击者想要的任何值。常常被回拨数年(把刚投放的工具伪装成系统文件),或者被向前调整(模糊真实活动时间)。
  • FN 仍然反映真实创建时间,可能与 SI 相差很大。

这就是教科书式的 timestomping 签名:一个文件 SI created 在 2018 年,FN created 在六分钟前。

取证时要留意两种模式:

  1. SI created 早于 FN created。 这在自然条件下不可能。文件不可能在被命名之前存在。任何 SI created 早于 FN created 的记录都被动过。
  2. 明显未被重命名过的文件上,SI modified 与 FN modified 相差甚远。 FN modified 只在重命名时变化;若 SI 显示 2017 而 FN 显示昨天,中间也没有重命名记录,那就是有问题。

双重重命名诡计

知道 SI/FN 不一致会被发现的操作员会用一种规避手段:先重命名文件(强制 NTFS 更新 FN),然后对 SI 与 FN 同时调用 SetFileTime,再改回原名。这样两个属性都显示回拨过的值。

这能击败 SI/FN 比较。它击败不了:

  • USN journal 每次重命名都会写下 RENAME_OLD_NAME / RENAME_NEW_NAME 这一对。该诡计所需的两次重命名都会出现在 journal 中。journal 里那两次的时间戳是真实时间,而不是被伪造的 FN 值。
  • $LogFile 重命名前后的事务记录就在日志里。
  • VSS 快照。 较旧的快照里 FN 是 stomp 之前的值。对同一记录把当前 MFT 与快照中的 MFT 做 diff 即可。

要把双重重命名做得无声很难。但在成熟红队工具集中仍很常见。

亚秒级粒度:暴露偷懒工具的线索

NTFS 用 100 纳秒粒度存时间戳。原生 Windows 操作会在低位留下噪声。写入时间戳时操作系统不会把亚秒部分清零;内核时钟返回什么就写什么。

多数 timestomping 工具会先把时间四舍五入到秒再写入。结果就是以 .0000000 UTC 结尾的时间戳。单个这样的时间戳没什么稀奇(偶尔的自然事件确实也会舍入)。但跨多个文件整齐排列的 .0000000 后缀就是一种指纹。

这个很好查。把你 MFT 解析中的 createdmodifiedaccessedMFT-modified 四列拿出来,过滤出 SI 四个时间戳全以 .0000000 结尾的记录,再与自然 Windows 活动的基线比较。对比立刻就显出来了。

Windows 自身对时间戳做了什么

要使用 SI/FN 对比,你得知道 Windows 自己会对这四个字段做什么。备忘单:

  • SI created:在创建时写入。一些安装器在"创建"(即解压)文件时也会更新它。
  • SI modified:每次写数据时写入。SetFileTime 会更新。仅修改元数据不会更新。
  • SI accessed:Windows 7+ 默认禁用(用 fsutil behavior set disablelastaccess 0 启用)。即便禁用,该字段也偶尔会被某些操作(备份软件、EDR 扫描)更新,因此作为"最后读取"信号在任何方向上都不可靠。
  • SI MFT-modified:任何属性变更(包括重命名、ACL 变更、ADS 创建、数据写入)都会写入。
  • FN created:在名称被首次设置时写入(创建、创建硬链接、重命名)。
  • FN modified、FN accessed、FN MFT-modified:在名称被设置时写入,之后基本稳定。

干净安装下,FN 的四个时间戳通常相等(它们都是在分配名称的同一瞬间设置的)。看到 FN 四个一致而 SI 四个发散,那是正常的。

Windows Update 与被打补丁的二进制

Windows Update 会更新被打补丁文件的 SI 时间戳。一次累积更新后,\Windows\System32\ 里有一半的文件 SI modified 都在几分钟之内,而 FN 自上一个 service pack 起就没动过。这是正常情况。把它当作基线模式,而不是异常活动。

对补丁窗口要与 CBS.logsetupapi.dev.log 交叉比对。如果 SI 说 svchost.exe 在 03:14 被修改,而 CBS 日志在 03:14 显示了一次组件安装,那就是 Windows Update,不是攻击者。

我对 timestomping 的筛查方式

我对一份新鲜的 MFT 抽取会跑的筛查:

  1. 拉出所有 SI created < FN created 的记录。逐条人工复核。
  2. 拉出 SI modified 比 FN modified 早超过 30 天,且文件位于用户可写目录的所有记录。人工复核。
  3. 拉出 SI 四个时间戳全以 .0000000 结尾、且文件不是 Windows 系统二进制的所有记录。人工复核。
  4. 将当前 MFT 与最近的 VSS 快照做 diff。把 SI 时间戳向后回退的记录高亮。人工复核。

这套流程能抓到野外大多数 timestomping。使用双重重命名或 VSS 删除的高级操作员需要 journal 与事件日志层来检测,但上面四项检查仍然能通过其他工件(USN journal 里的重命名、事件日志里的 VSS 删除)让他们浮出水面。

延伸阅读

外部资源