逆转、揭露、恢复:Windows Defender 隔离取证

admin 2024年9月10日22:48:24评论204 views字数 15875阅读52分55秒阅读模式
免责声明
由于传播、利用本公众号红云谈安全所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号红云谈安全及作者不为承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!请在授权的站点测试,遵守网络安全法!仅供学习使用,如若非法他用,与平台和本文作者无关,需自行负责!

总结

  • Windows Defender(Windows 标准安装附带的防病毒软件)在检测到恶意文件后会将其置于隔离区。

  • 逆向工程的mpengine.dll结果是,在 Windows Defender 隔离文件夹中找到了以前未记录的元数据,可用于数字取证和事件响应。

  • 提取隔离文件的现有脚本不会处理此元数据,即使它可能对分析有用。

  • Fox-IT 的开源数字取证和事件响应框架Dissect现在除了可以从 Windows Defender 隔离文件夹恢复隔离的文件之外,还可以恢复这些元数据。

  • dissect.cstruct

    允许我们在 Python 中使用类似 C 的结构定义,这使得可以轻松地在其他编程语言中继续研究或使用 IDA Pro 等工具进行逆向工程。

    • 想要继续使用 IDA Pro?只需复制粘贴结构定义!

介绍

在事件响应过程中,我们经常会遇到杀毒软件被威胁者部署的恶意软件正确触发的情况。最常见的情况是,Windows Defender 是 Microsoft Windows 默认附带的杀毒解决方案。Windows Defender 在检测到恶意文件后会将其隔离,以便最终用户可以决定恢复文件或永久删除它。当威胁者面对 Defender 的检测功能时,要么完全禁用杀毒软件,要么试图逃避检测。

从数字取证和事件响应 (DFIR) 的角度来看,Windows Defender 隔离文件夹很有价值。首先,它可以揭示有关 Windows Defender 检测到的文件的时间戳、位置和签名的信息。特别是在威胁行为者删除了 Windows 事件日志但保留了隔离文件夹的情况下,隔离文件夹具有很高的取证价值。此外,由于整个文件都被隔离(以便最终用户可以选择恢复它),因此可以从隔离区恢复文件以进行进一步的逆向工程和分析。

虽然已经有脚本可以从 Defender 隔离文件夹中恢复文件,但该文件夹中大部分内容的用途之前都是未知的。我们不喜欢未知的事物,因此我们对之前未知的元数据进行了进一步研究,看看能否发现更多取证痕迹。

除了展示我们的成果之外,我们还构建了这篇博客来描述我们实现这一目标的过程。如果您对结果感兴趣,而不是对逆向工程 Windows Defender 的技术细节感兴趣,请跳到最后。

深入探究 Windows Defender 内部原理

现有研究

我们首先研究了 Windows Defender 内部的现有研究。我们能找到的有关 Windows Defender 隔离文件结构的最详尽的文档是Florian Bauchs 的分析防病毒软件隔离文件的白皮书,但我们也查看了GitHub上的几个脚本。

  • 总之,每当 Defender 将文件放入隔离区时,它都会做三件事:将与 文件被隔离的时间、原因和方式有关的一堆元数据保存在 中QuarantineEntry。这些元数据QuarantineEntry经过 RC4 加密并保存到文件夹中的磁盘中/ProgramData/Microsoft/Windows Defender/Quarantine/Entries
  • 恶意文件的内容存储在一个QuarantineEntryResourceData文件中,该文件也经过 RC4 加密并保存到/ProgramData/Microsoft/Windows Defender/Quarantine/ResourceData文件夹中的磁盘中。
  • 文件夹内会生成/ProgramData/Microsoft/Windows Defender/Quarantine/Resource一个Resource文件。根据之前的研究以及我们在逆向工程过程中的发现,该文件似乎不包含无法从QuarantineEntryQuarantineEntryResourceData文件中获取的信息。因此,我们将Resource在本博客的其余部分中忽略该文件。

虽然以前的脚本能够从ResourceDataQuarantineEntry文件中恢复某些属性,但大量数据尚未解析,这让我们预感到还有更多法医证据尚未被发现。

Windows Defender使用 中定义的硬编码 RC4 密钥加密QuarantineEntry和文件。此硬编码密钥最初由 Cuckoo 发布,对于隔离文件夹的离线恢复至关重要。ResourceData``mpengine.dll

借助公开脚本和 Bauch 的白皮书,我们将其加载mpengine.dll到 IDA 中,进一步检查 Windows Defender 如何将文件放入隔离区。使用 Microsoft 符号服务器提供的 PDB,我们先从已定义的一些函数和结构开始。

通过调查 QuarantineEntry 文件恢复元数据

让我们从QuarantineEntry文件开始。从这个文件中,我们希望恢复QuarantineEntry尽可能多的结构,因为其中包含各种有价值的元数据。该QuarantineEntry文件不是作为一个 RC4 密码流加密的,而是由三个块组成,每个块都使用 RC4 单独加密。

这三块就是我们所说的QuarantineEntryFileHeader和。QuarantineEntrySection1``QuarantineEntrySection2

  • QuarantineEntryFileHeader``QuarantineEntrySection1描述和的大小QuarantineEntrySection2,并包含两个部分的 CRC 校验和。
  • QuarantineEntrySection1包含适用于此文件QuarantineEntryResource内所有实例的宝贵元数据QuarantineEntry,例如与隔离操作相关的DetectionName和。ScanId
  • QuarantineEntrySection2``QuarantineEntryResource表示此文件中每个实例的长度和偏移量,QuarantineEntry以便可以单独正确地解析它们。

AQuarantineEntry具有一个或多个QuarantineEntryResource与之关联的实例。这包含其他信息,例如被隔离的制品的路径,以及被隔离的制品的类型(例如regkeyfile)。

QuarantineEntry图 1 概述了其中的不同结构:

逆转、揭露、恢复:Windows Defender 隔离取证图 1: 的示例概览QuarantineEntry。在此示例中,两个文件同时被 Windows Defender 隔离。因此,此单个 中包含两个 QuarantineEntryResource 结构QuarantineEntry

由于它主要是描述如何以及应该如何解析的QuarantineEntryFileHeader结构,我们将首先研究这两者由什么组成。QuarantineEntrySection1``QuarantineEntrySection2

QuarantineEntrySection1

在 IDA 中检查时,和mpengine.dll的内容似乎都在函数中确定。QuarantineEntrySection1``QuarantineEntrySection2QexQuarantine::CQexQuaEntry::Commit

该函数接收QexQuarantine::CQexQuaEntry类的一个实例。不幸的是,Microsoft 提供的 PDB 文件mpengine.dll不包含此结构的内容。但是,大多数字段可以使用与类关联的 PDB 中的函数名称派生CQexQuaEntry

逆转、揭露、恢复:Windows Defender 隔离取证图 2:从中检索属性的函数QuarantineEntry

Id`、、和字段是最重要的`ScanId`,因为它们将被写入文件中。`ThreatId``ThreatName``Time``QuarantineEntry

在函数开始时QexQuarantine::CQexQuaEntry::Commit,的大小Section1就已确定。

逆转、揭露、恢复:Windows Defender 隔离取证图 3:查看的反编译输出CqExQuaEntry::Commit显示的大小QuarantineEntrySection1设置为长度ThreatName加上 53。

这将设置section1_size为变量长度加上 53 的值。我们可以通过查看函数中为缓冲区ThreatName设置的值来确定这额外的 53 个字节由什么组成。QexQuarantine::CQexQuaEntry::Commit``Section1

这需要进行一些实验,并需要在 IDA 中尝试结构的不同字段、偏移量和大小QuarantineEntrySection1。每次更改后,我们都会检查这些更改会对函数的反编译 IDA 视图产生什么影响QexQuarantine::CQexQuaEntry::Commit

经过反复试验,我们得到了以下结构定义:

struct QuarantineEntrySection1 {
CHAR Id[16];
CHAR ScanId[16];
QWORD Timestamp;
QWORD ThreatId;
DWORD One;
CHAR DetectionName[];
};

在检查汇编代码(左)的最终反编译输出(右)时,我们注意到一个字段始终被设置为 1:

逆转、揭露、恢复:Windows Defender 隔离取证QuarantineEntrySection1图 4:始终被设置为值 1 的字段。

由于我们不知道该字段的用途,我们暂时选择将该字段命名为“One”。最有可能的是,它是一个布尔值,在QexQuarantine::CQexQuaEntry::Commit提交函数的上下文中始终为真。

QuarantineEntrySection2

现在我们已经有了 a 的第一部分的结构定义QuarantineEntry,现在我们转到第二部分。保存a 内的对象QuarantineEntrySection2数量,以及它们所在结构中的偏移量。QuarantineEntryResource``QuarantineEntry``QuarantineEntry

在大多数情况下,每次只检测到一个威胁,并且一个威胁QuarantineEntry与一个 相关联QuarantineEntryResource。但情况并非总是如此:例如,如果解压包含多个恶意文件的 ZIP 文件夹,Windows Defender 可能会将它们全部放入隔离区。ZIP 中的每个恶意文件都将是一个QuarantineEntryResource,但它们都被限制在一个 中QuarantineEntry

QuarantineEntryResource

为了能够解析QuarantineEntryResource实例,我们需要研究该CQexQuaResource::ToBinary函数。该函数接收一个QuarantineEntryResource对象以及一个指向缓冲区的指针,它需要将二进制输出写入该缓冲区。如果我们可以反转该函数中的逻辑,我们就可以在取证恢复过程中将二进制输出转换回已解析的实例。

查看该CQexQuaResource::ToBinary函数,我们发现两个非常相似的循环,与之前观察到的序列化 的循环非常相似ThreatNameQuarantineEntrySection1通过查看各种解密QuarantineEntry文件,我们很快发现这些循环负责在输出缓冲区中为DetectionPath和保留空间DetectionType,并DetectionPath使用 UTF-16 编码:

逆转、揭露、恢复:Windows Defender 隔离取证DetectionPath图 5:在DetectionType开头保留空间CQexQuaResource::ToBinary

字段

在检查该函数时,我们观察到一个有趣的循环,它(在研究函数调用和重命名变量之后)解释了和QexQuarantine::CQexQuaEntry::Commit之间存储的数据:DetectionType``DetectionPath

逆转、揭露、恢复:Windows Defender 隔离取证图 6:序列化字段的对齐逻辑

结构似乎QuarantineEntryResource有一个或多个QuarantineResourceField与之关联的实例,与 关联的字段数存储在和QuarantineEntryResource之间的单个字节中。将 保存到磁盘时,字段的对齐方式为 4 个字节。我们在之前的 Windows Defender 研究中找不到对结构的提及,尽管它们可以保存有价值的信息。DetectionPath``DetectionType``QuarantineEntry``QuarantineEntryResourceField

该类CQExQuaResource有几种不同的 实现AddField,接受不同类型的参数。查看这些函数显示,字段具有Identifier、和大小为 的Type缓冲区,从而形成了一种简单的TLV类格式:Data``Size

struct QuarantineEntryResourceField {
WORD Size;
WORD Identifier:12;
FIELD_TYPE Type:4;
CHAR Data[Size];
};

为了了解可能的类型和标识符,我们进一步研究了AddField函数的不同版本,它们都接受不同的数据类型:

逆转、揭露、恢复:Windows Defender 隔离取证图7:根据函数的不同实现查找不同的字段CqExQuaResource::AddField类型

访问这些函数时,我们查看了TypeSize变量,以了解可以为QuarantineResource实例设置的不同类型的字段。这产生了以下FIELD_TYPE枚举:

enum FIELD_TYPE : WORD {
STRING = 0x1,
WSTRING = 0x2,
DWORD = 0x3,
RESOURCE_DATA = 0x4,
BYTES = 0x5,
QWORD = 0x6,
};

由于这些AddField函数是类的虚拟函数表 (vtable) 的一部分CQexQuaResource,我们无法轻易找到AddField调用该函数的所有位置,因为它们不是直接调用的(这将在 IDA 中产生 xref)。因此,我们尚未穷尽导致调用的所有代码路径来AddField识别所有可能的Identifier值及其使用方式。我们的研究得出了以下字段标识符,这是最常见的,也是最具取证价值的:

enum FIELD_IDENTIFIER : WORD {
CQuaResDataID_File = 0x02,
CQuaResDataID_Registry = 0x03,
Flags = 0x0A,
PhysicalPath = 0x0C,
DetectionContext = 0x0D,
Unknown = 0x0E,
CreationTime = 0x0F,
LastAccessTime = 0x10,
LastWriteTime = 0x11,
};

尤其是CreationTimeLastAccessTime并且LastWriteTime可以在调查期间提供关键的数据点。

重新审视QuarantineEntrySection2QuarantineEntryResource结构

现在我们已经了解了字段的工作原理以及它们在 中的存储方式QuarantineEntryResource,我们可以为其推导出以下结构:

struct QuarantineEntryResource {
WCHAR DetectionPath[];
WORD FieldCount;
CHAR DetectionType[];
};

重新审视该QexQuarantine::CQexQuaEntry::Commit函数,我们现在可以理解该函数如何确定 everyQuarantineEntryResource在 中的偏移量QuarantineEntry。使用这些偏移量,我们稍后将能够解析单个QuarantineEntryResource实例。因此,QuarantineEntrySection2结构相当简单:

struct QuarantineEntrySection2 {
DWORD EntryCount;
DWORD EntryOffsets[EntryCount];
};

恢复的最后一步QuarantineEntryQuarantineEntryFileHeader

现在我们对 有了正确的理解QuarantineEntry,我们想知道它最终是如何以加密形式写入磁盘的,以便我们在取证恢复时能够正确解析文件。通过QexQuarantine::CQexQuaEntry::Commit进一步检查该函数,我们可以发现它最终是如何将QuarantineSection1QuarantineSection2传递给名为 的函数的CUserDatabase::Add

我们之前提到,包含QuarantineEntry三个 RC4 加密块。文件的第一个块是在函数中创建的CUserDatabase::Add,是QuarantineEntryHeader。第二个块是QuarantineEntrySection1。第三个块以 开头QuarantineEntrySection2,后跟所有QuarantineEntryResource结构及其 4 字节对齐的QuarantineEntryResourceField结构。

我们从 Bauch 的工作中得知, 的静态大小为 60 字节,并且包含和QuarantineEntryFileHeader的大小。因此,我们需要解密第一个。QuarantineEntrySection1``QuarantineEntrySection2``QuarantineEntryFileHeader

根据 Bauch 的工作,我们从以下结构开始QuarantineEntryFileHeader

struct QuarantineEntryHeader {
char magic[16];
char unknown1[24];
uint32_t section1_size;
uint32_t section2_size;
char unknown[12];
};

不过,这留下了相当多的未知字节,所以我们又回到了值得信赖的 IDA。检查该CUserDatabase:Add函数有助于我们进一步了解QuarantineEntryHeader结构。例如,我们可以看到硬编码的魔法页眉和页脚:

逆转、揭露、恢复:Windows Defender 隔离取证图 8:为QuarantineEntryHeader

可以看到和的缓冲区的 CRC 校验和QuarantineEntrySection1计算QuarantineSection2

逆转、揭露、恢复:Windows Defender 隔离取证图 9:CRC 校验和逻辑CUserDatabase::Add

这些校验和可以在恢复时用来验证文件的有效性。CUserDatabase:Add然后,该函数将三个块以 RC4 加密形式写入QuarantineEntry文件缓冲区。

根据对 Magic 页眉和页脚以及 CRC 校验的这些发现,我们可以修改以下结构定义QuarantineEntryFileHeader

struct QuarantineEntryFileHeader {
CHAR MagicHeader[4];
CHAR Unknown[4];
CHAR _Padding[32];
DWORD Section1Size;
DWORD Section2Size;
DWORD Section1CRC;
DWORD Section2CRC;
CHAR MagicFooter[4];
};

这是能够QuarantineEntry从磁盘形式解析结构的最后一部分。但是,我们想要的不仅仅是数据:我们还想恢复被隔离的文件。

通过调查恢复文件QuarantineEntryResourceData

现在我们可以正确解析QuarantineEntry文件了,是时候把注意力转向QuarantineEntryResourceData文件了。该文件包含被隔离的文件的 RC4 加密内容。

第一步:查看 hexdump 文件

首先,让 Windows Defender 隔离 Mimikatz 可执行文件,并在隔离文件夹中查看其输出文件。有人认为,仅使用 RC4 解密文件QuarantineEntryResourceData就能得到原始文件的内容。然而,解密QuarantineEntryResourceData文件的快速十六进制转储向我们展示了其中包含更多信息:

max@dissect $ hexdump -C mimikatz_resourcedata_rc4_decrypted.bin | head -n 20

00000000 03 00 00 00 02 00 00 00 a4 00 00 00 00 00 00 00 |…………….|
00000010 00 00 00 00 01 00 04 80 14 00 00 00 30 00 00 00 |…………0…|
00000020 00 00 00 00 4c 00 00 00 01 05 00 00 00 00 00 05 |….L………..|
00000030 15 00 00 00 a4 14 d2 9b 1a 02 a7 4f 07 f6 37 b4 |………..O..7.|
00000040 e8 03 00 00 01 05 00 00 00 00 00 05 15 00 00 00 |…………….|
00000050 a4 14 d2 9b 1a 02 a7 4f 07 f6 37 b4 01 02 00 00 |…….O..7…..|
00000060 02 00 58 00 03 00 00 00 00 00 14 00 ff 01 1f 00 |..X………….|
00000070 01 01 00 00 00 00 00 05 12 00 00 00 00 00 18 00 |…………….|
00000080 ff 01 1f 00 01 02 00 00 00 00 00 05 20 00 00 00 |………… …|
00000090 20 02 00 00 00 00 24 00 ff 01 1f 00 01 05 00 00 | …..$………|
000000a0 00 00 00 05 15 00 00 00 a4 14 d2 9b 1a 02 a7 4f |……………O|
000000b0 07 f6 37 b4 e8 03 00 00 01 00 00 00 00 00 00 00 |..7………….|
000000c0 00 ae 14 00 00 00 00 00 00 00 00 00 4d 5a 90 00 |…………MZ..|
000000d0 03 00 00 00 04 00 00 00 ff ff 00 00 b8 00 00 00 |…………….|
000000e0 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 |….@………..|
000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |…………….|
00000100 00 00 00 00 00 00 00 00 20 01 00 00 0e 1f ba 0e |…….. …….|
00000110 00 b4 09 cd 21 b8 01 4c cd 21 54 68 69 73 20 70 |….!..L.!This p|
00000120 72 6f 67 72 61 6d 20 63 61 6e 6e 6f 74 20 62 65 |rogram cannot be|
00000130 20 72 75 6e 20 69 6e 20 44 4f 53 20 6d 6f 64 65 | run in DOS mode|

从十六进制转储中可以看出,MZ 值(位于 Mimikatz 可执行文件缓冲区的开头)仅从偏移量0xCC开始。这让我们有理由相信它前面可能隐藏着有价值的信息。

文件末尾还有其他信息ResourceData

max@dissect $ hexdump -C mimikatz_resourcedata_rc4_decrypted.bin | tail -n 10

0014aed0 00 00 00 00 52 00 00 00 00 00 00 00 2c 00 00 00 |….R…….,…|
0014aee0 3a 00 5a 00 6f 00 6e 00 65 00 2e 00 49 00 64 00 |:.Z.o.n.e…I.d.|
0014aef0 65 00 6e 00 74 00 69 00 66 00 69 00 65 00 72 00 |e.n.t.i.f.i.e.r.|
0014af00 3a 00 24 00 44 00 41 00 54 00 41 00 5b 5a 6f 6e |:.$.D.A.T.A.[Zon|
0014af10 65 54 72 61 6e 73 66 65 72 5d 0d 0a 5a 6f 6e 65 |eTransfer]..Zone|
0014af20 49 64 3d 33 0d 0a 52 65 66 65 72 72 65 72 55 72 |Id=3..ReferrerUr|
0014af30 6c 3d 43 3a 5c 55 73 65 72 73 5c 75 73 65 72 5c |l=C:Usersuser|
0014af40 44 6f 77 6e 6c 6f 61 64 73 5c 6d 69 6d 69 6b 61 |Downloadsmimika|
0014af50 74 7a 5f 74 72 75 6e 6b 2e 7a 69 70 0d 0a |tz_trunk.zip..|

在十六进制转储的末尾,我们可以看到一个额外的缓冲区,有些人可能将其识别为“区域标识符”或“网络标记”。由于此区域标识符可能会告诉您有关文件最初来自何处的信息,因此它对于法医调查很有价值。

第二步:打开IDA

为了理解这些额外的缓冲区来自哪里以及如何解析它们,我们再次深入研究了 的内部结构mpengine.dll。如果我们查看该QuarantineFile函数,我们会看到它接收QuarantineEntryResourceQuarantineEntry作为参数。当跟踪代码路径时,我们会看到BackupRead调用该函数来写入缓冲区,我们知道该缓冲区稍后将由 Defender 进行 RC4 加密并写入隔离文件夹:

逆转、揭露、恢复:Windows Defender 隔离取证图10:函数内部调用BackupRead QuarantineFile

第三步:阅读手册

浏览一下文档BackupRead就会发现,该函数返回一个由 Win32 流 ID 分隔的缓冲区。存储的流包含BackupRead所有数据流以及有关文件所有者和权限的安全数据。在 NTFS 文件系统上,文件可以具有多个数据属性或流:“主”未命名数据流和可选的其他命名数据流,通常称为“备用数据流”。例如,区域标识符存储在Zone.Identifier文件的单独数据流中。用于备份数据的函数也保留这些备用数据流是有道理的。

保存这些流的事实BackupRead对于取证分析来说也是个好消息。首先,恶意负载可以隐藏在备用数据流中。此外,区域标识符和安全数据等备用数据流可以帮助了解文件的来源和内容。我们只需要恢复已保存的流BackupRead

无需深入研究 IDA,因为文档已告诉我们所需的一切。对于每个数据流,该BackupRead函数将一个写入WIN32_STREAM_ID磁盘,该磁盘表示(除其他外)流的大小。之后,它将流的数据写入目标文件并继续处理下一个流。WIN32_STREAM_ID结构定义记录在Microsoft Learn 网站上:

typedef struct _WIN32_STREAM_ID {
STREAM_ID StreamId;
STREAM_ATTRIBUTES StreamAttributes;
QWORD Size;
DWORD StreamNameSize;
WCHAR StreamName[StreamNameSize / 2];
} WIN32_STREAM_ID;

是谁在代码审查中忽略了这一点?

在逆向分析 的部分内容时mpengine.dll,我们发现HandleThreatDetection函数中有一个看起来很有趣的调用。我们明白必须迅速、严格地处理威胁,但在命名这个特定函数时,我们忍不住嘲笑这种奇怪的措辞。逆转、揭露、恢复:Windows Defender 隔离取证* 图 11:对 的函数调用SendThreatToCamp,一个似乎相当严厉的“行动呼吁”。*

将我们的研究成果应用到 Dissect 中

现在,我们已经拥有从隔离文件夹恢复所有元数据和隔离文件所需的所有结构定义。只剩一步:编写实现。

在事件响应期间,我们不想依赖分散在主目录和 git 存储库中的脚本。这就是我们将研究整合到 Dissect 中的原因。

我们可以将解析磁盘、卷和证据容器等无聊的工作留给 Dissect,并将我们的实现作为框架的插件编写。因此,我们唯一需要做的就是解析工件并将结果反馈给框架。

上一节中深入研究 Windows Defender 后,我们得到了许多结构定义,我们需要这些结构定义才能从 Windows Defender 隔离文件夹中恢复数据。在实施时,我们希望代码尽可能贴近这些结构定义,以使代码既可读又可验证。这就是它的用武之地。它可以解析结构定义并使其在您的 Python 代码中可用。这消除了大量用于解析结构的样板代码,并大大提高了解析器的可读性。让我们回顾一下如何使用dissect.cstruct轻松解析文件:QuarantineEntry``dissect.cstruct

from dissect.cstruct import cstruct

defender_def= """
struct QuarantineEntryFileHeader {
CHAR MagicHeader[4];
CHAR Unknown[4];
CHAR _Padding[32];
DWORD Section1Size;
DWORD Section2Size;
DWORD Section1CRC;
DWORD Section2CRC;
CHAR MagicFooter[4];
};

struct QuarantineEntrySection1 {
CHAR Id[16];
CHAR ScanId[16];
QWORD Timestamp;
QWORD ThreatId;
DWORD One;
CHAR DetectionName[];
};

struct QuarantineEntrySection2 {
DWORD EntryCount;
DWORD EntryOffsets[EntryCount];
};

struct QuarantineEntryResource {
WCHAR DetectionPath[];
WORD FieldCount;
CHAR DetectionType[];
};

struct QuarantineEntryResourceField {
WORD Size;
WORD Identifier:12;
FIELD_TYPE Type:4;
CHAR Data[Size];
};
"""


c_defender = cstruct()
c_defender.load(defender_def)


class QuarantineEntry:
def __init__(self, fh: BinaryIO):
# Decrypt & parse the header so that we know the section sizes
self.header = c_defender.QuarantineEntryFileHeader(rc4_crypt(fh.read(60)))

# Decrypt & parse Section 1. This will tell us some information about this quarantine entry.
# These properties are shared for all quarantine entry resources associated with this quarantine entry.
self.metadata = c_defender.QuarantineEntrySection1(rc4_crypt(fh.read(self.header.Section1Size)))

# […]

# The second section contains the number of quarantine entry resources contained in this quarantine entry,
# as well as their offsets. After that, the individal quarantine entry resources start.
resource_buf = BytesIO(rc4_crypt(fh.read(self.header.Section2Size)))

如您所见,当结构格式已知时,使用 进行解析非常简单dissect.cstruct。唯一需要注意的是QuarantineEntryFileHeaderQuarantineEntrySection1QuarantineEntrySection2结构是使用硬编码的 RC4 密钥单独加密的。由于只有 的大小QuarantineEntryFileHeader是静态的(60 字节),我们首先解析它,然后使用其中包含的信息解密其他部分。

为了解析 中包含的各个字段QuarantineEntryResource,我们还需要做更多工作。我们无法将QuarantineEntryResourceField直接添加到QuarantineEntryResource内的结构定义中dissect.cstruct,因为它目前不支持 Windows Defender 使用的对齐类型。但是,它确实支持结构定义QuarantineEntryResourceField,因此我们要做的就是遵循我们在 IDA 中看到的对齐逻辑:

# As the fields are aligned, we need to parse them individually
offset = fh.tell()
for _ in range(field_count):
# Align
offset = (offset + 3) & 0xFFFFFFFC
fh.seek(offset)
# Parse
field = c_defender.QuarantineEntryResourceField(fh)
self._add_field(field)

# Move pointer
offset += 4 + field.Size

我们可以使用dissect.cstructdumpstruct函数来可视化我们的解析,以验证我们是否正确加载了所有数据:

逆转、揭露、恢复:Windows Defender 隔离取证
upload_193c111d8639e63369484615023e25e8

就这样,我们的解析就完成了。利用它dissect.cstruct使解析结构更容易理解和实现。这也有助于快速迭代:我们在研究过程中已经多次修改了结构定义,如果没有能力将结构定义盲目地复制粘贴到我们选择的 Python 编辑器中,这将是纯粹的痛苦。

在 Dissect 框架内实现解析器带来了巨大的优势。我们完全不必担心提供取证证据的格式。将 Defender 恢复实现为 Dissect 插件意味着它只适用于标准取证证据格式(例如 或 ),E01或针对和ASDF之类的取证包,甚至在实时虚拟机上也能工作:KAPE``Acquire

max@dissect $ target-query ~/Windows10.vmx -q -f defender.quarantine



max@dissect $ target-query ~/Windows10.vmx -f defender.recover -o /tmp/ -v

2023-02-14T07:10:20.335202Z [info] : Saving /tmp/9EC21BB792E253DBDC2E88B6B180C4E048847EF6.security_descriptor [dissect.target.target]
2023-02-14T07:10:20.335898Z [info : Saving /tmp/9EC21BB792E253DBDC2E88B6B180C4E048847EF6 [dissect.target.target]
2023-02-14T07:10:20.337956Z [info] : Saving /tmp/9EC21BB792E253DBDC2E88B6B180C4E048847EF6.ZoneIdentifierDATA [dissect.target.target]

可以在 Github 上观察到 Windows Defender 隔离恢复的完整实现。

结论

我们希望能够证明逆向工程 Microsoft Windows 内部结构以发现取证文物具有巨大优势。通过逆向工程mpengine.dll,我们能够进一步了解 Windows Defender 如何将检测到的文件放入隔离区。然后,我们可以利用这些知识来发现以前未完全记录或理解的(元)数据。这样做的主要结果是恢复了有关原始隔离文件的更多信息,例如各种时间戳和其他 NTFS 数据流,例如Zone.Identifier,这些信息可用于数字取证或事件响应调查。

在进行本研究之前,尚无相关文档QuarantineEntryResourceField,我们希望其他人可以利用这些文档进一步研究哪些字段尚未发现。我们还记录了BackupReadDefender 如何使用该功能来保存 NTFS 文件中存在的不同数据流,包括区域标识符和安全描述符。

在编写解析器时,使用dissect.cstruct使我们能够将逆向工程的发现紧密集成到解析中,从而增强了代码的可读性和可验证性。这反过来可以帮助其他人从我们的研究中转向,就像我们从其他人的研究中转向 Windows Defender 隔离文件夹一样。

dissect.target这项研究已作为 Dissect 框架的一个插件实施。这意味着我们的解析器可以独立于它所针对的证据类型运行。此功能已于2023 年 1 月 2 日添加,并从3.4 版开始与 Dissect 一起安装。

原文始发于微信公众号(红云谈安全):逆转、揭露、恢复:Windows Defender 隔离取证

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月10日22:48:24
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   逆转、揭露、恢复:Windows Defender 隔离取证https://cn-sec.com/archives/3150819.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息