GPT-Academic Report
我的浏览器是如何阻止RWX执行的?
回顾一款流行浏览器中类似EDR的机制
在测试载荷时,我偶然发现了一款流行浏览器中实现的安全功能,其作用类似于终端检测与响应(EDR)。通过挂钩一个关键的Windows API,它在运行时检查线程创建,然后决定是否允许该线程运行。
在常见浏览器中运行RWX shellcode失败
我一直在测试一种新型的进程注入技术。这项技术可能不久后会在本博客上发布,但这里的目的不是讨论这项具体的注入技术,而是我如何在无意间发现浏览器中一个类似EDR的机制。
虽然对像notepad.exe
这样简单的程序成功注入并执行是一个不错的开始,但真正的测试在于确认这项技术是否能在更复杂的应用程序(如.NET、大型多线程应用如浏览器等)中同样有效。
这对于注入技术尤为重要,因为它们会以某种方式干扰目标进程。因此,确保稳定性是关键。
为此,我编写了一段简单的shellcode,放在RWX
内存区域中,然后尝试使用新技术在许多常见应用程序中执行。除了一个浏览器外,所有测试的应用程序都成功了。
注:这可能过于谨慎,但为了避免法律问题,我不会提及该浏览器的名称或涉及的文件/函数
很明显,即使尝试以非常直接的方式(使用CreateRemoteThread()
)执行shellcode,它仍然静默失败,线程被创建但shellcode从未执行。
我最初认为这是由目标进程中的技术细节引起的(浏览器可以实现一些奇特的功能,修改原生DLL等),但后来意识到这实际上看起来像是一个有意为之的安全功能,非常类似于EDR所做的。
挂钩线程创建
与许多安全产品通过用户模式挂钩所做的类似,浏览器挂钩了BaseThreadInitThunk()
,以便对正在发生的事情有可见性(和控制权)。BaseThreadInitThunk
调用是线程创建的早期步骤之一,你会在Process Hacker中的大多数调用堆栈中找到它,例如这是我当前Sublime应用程序的调用堆栈:
这是kernel32.dll
中的正常API:
这是我们的浏览器使用的修改后的API:
任何线程创建都将通过该jmp
指令重定向,跳转到浏览器加载的某个自定义第三方DLL中,我在网上找到的关于这个DLL的信息非常少(这促使我深入研究)。
请注意,此时,预期的线程创建地址(我们的shellcode在内存中的位置)存储在rdx
寄存器中。
逆向工程挂钩
如上所述,浏览器中的任何线程创建都会首先通过其第三方DLL中的自定义检查。该DLL相当大,并且有很多导出函数。这让我非常好奇,但就我们这里关注的内容而言,只有其中一个例程真正重要。它相当简单,几乎只包含一个对VirtualQuery()
的调用(红色部分),以检索我们shellcode所在地址的内存属性:
提醒一下这个API调用的参数,它接受三个参数,将shellcode地址移动到rcx
(第一个参数,绿色部分)。
在进一步的分析中,一些检查将验证该内存地址是否为PAGE_EXECUTE_READ
(0x20
值,如这里所示)。
根据结果(这嵌入在cmovnz
指令中),会发生以下情况:
- 如果是,它不会干扰预期的执行流程,并且
rdx
(包含线程启动时要执行的shellcode)对于后续代码保持不变。 - 如果否,它会更改线程的启动点,并用
r8
中的内容覆盖rdx
,在这种情况下,r8
中的内容就像一个黑洞,会立即返回:
总结一下:如果尝试执行RWX
线程,它将被中和。
结论
这个检查相当简单且基础,可以绕过(不要在RWX
地址中运行),但我发现这种类型的特性非常出乎意料,值得写一篇博客文章,因为我在网上没有找到任何其他关于它的参考资料。我不完全确定这纯粹是一种安全控制,但这似乎是唯一合理的解释。浏览器是那些在内存中拥有RWX
部分的应用程序之一(请查看这篇之前的博客文章…),所以这可能是一种缓解措施,如果利用链试图利用其中一个RWX
区域进行执行,这将使漏洞开发变得更加困难。如果你有其他理论或假设…请告诉我!