Freeze 是一款利用挂起进程、直接系统调用和替代执行方法来绕过 EDR 的载荷工具包。
如需查看最新版本的 Freeze 或提交问题,请参考 https://github.com/Tylous/Freeze。
如需了解本框架中所用技术的更多细节,请查看 SourceZero 博客
Freeze 是一款载荷生成工具,用于绕过 EDR 安全控制,以隐蔽方式执行 shellcode。Freeze 利用多种技术,不仅能移除用户态的 EDR 钩子,还能以规避其他终端监控控制的方式执行 shellcode。
当创建一个进程时,Ntdll.dll 是第一个被加载的 DLL。这发生在任何 EDR DLL 加载之前。这意味着 EDR 加载并开始钩取、修改系统 DLL 的汇编代码之前存在一段短暂的时间差。通过查看 Ntdll.dll 中的 Windows 系统调用,我们可以看到尚未有任何钩子。如果我们将进程创建为挂起状态(即冻结状态),那么除了 Ntdll.dll 之外,没有其他 DLL 被加载。同时也可以看到没有 EDR DLL 被加载,这意味着 Ntdll.dll 中的系统调用是未被修改的。
为了利用这个干净的挂起进程来移除 Freeze 加载器中的钩子,我们需要一种编程方式找到并读取该干净挂起进程的内存。这时地址空间布局随机化(ASLR)就派上了用场。ASLR 是一种安全机制,用于防止基于堆栈内存损坏的漏洞。ASLR 随机化进程内部的地址空间,以确保所有内存映射对象、栈、堆以及可执行程序本身都是唯一的。有趣之处在于:虽然 ASLR 有效,但它对位置无关代码(如 DLL)并不生效。对于 DLL(特别是已知的系统 DLL),地址空间仅在系统启动时随机化一次。这意味着我们无需枚举远程进程信息来找到其 ntdll.dll 的基址,因为该基址在所有进程中(包括我们控制的进程)都是相同的。由于每个 DLL 的地址在每次启动后都固定在同一位置,我们可以从自身进程中获取此信息,完全无需枚举挂起进程来寻找地址。
有了这些信息,我们可以使用 ReadProcessMemory API 来读取进程的内存。这个 API 调用通常与读取 LSASS 关联,属于基于凭据的攻击;但就其本身而言,它并非恶意操作,特别是在我们只是读取内存中任意一段内容时。只有当读取了本不该读取的内容(如 LSASS 的内容)时,ReadProcessMemory 才可能被标记为可疑。EDR 产品不应仅仅因为调用了 ReadProcessMemory 就发出警报,因为该函数有合法的操作用途,否则会导致大量误报。
我们还可以更进一步:只读取 Ntdll.dll 中存储所有系统调用的那部分——即其 .text 段,而不是读取整个 DLL。
结合这些要素,我们可以编程获取 Ntdll.dll 的 .text 段副本,用以覆盖自身已被钩取的 .text 段,然后再执行 shellcode。
ETW 利用内置的系统调用来生成遥测信息。由于 ETW 也是 Windows 的原生功能,安全产品无需“钩取”ETW 系统调用即可访问这些信息。因此,为防止 ETW,Freeze 会修补多个 ETW 系统调用,清空寄存器并将执行流返回到下一条指令。现在,修补 ETW 已成为所有加载器的默认操作。
由于只恢复了 Ntdll.dll,所有后续执行 shellcode 的调用都必须位于 Ntdll.dll 中。通过使用 Go(注意,你也可以用其他语言实现,但在 Go 中实现相对简单),我们可以定义并调用所需的 NT 系统调用来分配、写入和保护 shellcode,从而有效跳过位于 kernel32.dll 和 Kernelbase.dll 中的标准调用,因为这些 DLL 可能仍被钩取。
Freeze 使用 Golang 开发。
要安装 Freeze,请运行以下命令,或使用已编译的二进制文件:
go build Freeze.go
___________
\_ _____/______ ____ ____ ________ ____
| __) \_ __ \_/ __ \_/ __ \\___ // __ \
| \ | | \/\ ___/\ ___/ / /\ ___/
\___ / |__| \___ >\___ >_____ \\___ >
\/ \/ \/ \/ \/
(@Tyl0us)
Soon they will learn that revenge is a dish... best served COLD...
Usage of ./Freeze:
-I string
Path to the raw 64-bit shellcode.
-O string
Name of output file (e.g. loader.exe or loader.dll). Depending on what file extension defined will determine if Freeze makes a dll or exe.
-console
Only for Binary Payloads - Generates verbose console information when the payload is executed. This will disable the hidden window feature.
-encrypt
Encrypts the shellcode using AES 256 encryption
-export string
For DLL Loaders Only - Specify a specific Export function for a loader to have.
-process string
The name of process to spawn. This process has to exist in C:\Windows\System32\. Example 'notepad.exe' (default "notepad.exe")
-sandbox
Enables sandbox evasion by checking:
Is Endpoint joined to a domain?
Does the Endpoint have more than 2 CPUs?
Does the Endpoint have more than 4 gigs of RAM?
-sha256
Provides the SHA256 value of the loaders (This is useful for tracking)
Freeze 可以生成 .exe 或 .dll 文件。要指定类型,请确保 -O 命令行选项以 .exe(二进制)或 .dll(DLL)结尾。目前不支持其他文件类型。对于 DLL 文件,Freeze 还可以添加额外的导出功能。为此,请使用 -export 指定具体的导出函数名。
Freeze 采用一种技术:先创建进程,然后将其移至后台。这有两个作用:第一,有助于隐藏进程;第二,避免被任何 EDR 产品检测到。一创建进程就立刻将其移至后台可能非常可疑,是恶意的指示器。Freeze 通过在进程创建、EDR 钩子加载后调用 GetConsoleWindow 和 ShowWindow Windows 函数,将窗口属性更改为隐藏。Freeze 使用这些 API 而非传统的 -ldflags -H=windowsgui,因为后者已被大多数安全产品高度特征化并归类为入侵指标。
如果选择了 -console 命令行选项,Freeze 将不会隐藏后台进程,而是添加多条调试信息,显示加载器正在执行的操作。