前言利用Windows内核漏洞存在高风险,常被用于逃避浏览器沙箱。多年来,发现的大多数漏洞都存在于Win32k.sys驱动程序中,该驱动程序处理来自GDI32.DLL和user32.dll的调用。为了缓解这些漏洞,微软在window10上实现了Win32系统调用过滤。总体思路是尽量避免在进程入口处向win32.sys发送大量的系统调用,从而防止未知的漏洞利用。我没有找到实现的相关细节,我不确定它的效果如何。我找到的唯一来源是PeterHlavaty在RainbowOvertheWindows上发表的这篇文章。系统调用101我了解过滤技术的第一个方法是研究系统调用是如何执行的。分析基于64位版本的Windows10周年更新。通常系统调用是在gdi32.dll或user32.dll的函数中初始化的,最后会在win32u.dll中调用真正的系统调用。但是,我们可以直接使用汇编来显示系统调用。在下面的POC中,我发现系统调用号为0x119E的WIN32K函数是NtGdiDdDDICreateAllocation。于是我简单地创建了如下测试应用:下图是NtGdiDdDDICreateAllocation的汇编代码:当运行syscall指令时,会转入内核态执行。真正的系统调用位于NT!KiSystemStartService。但是由于系统调用较多,我们需要在调试器中设置条件断点:运行POC,打开断点。首先显示的系统调用号是我们提供的0x119E。实际上,参数1、2、3、4保存在寄存器RCX、RDX、R8、R9中。看IDA中的相关代码:看代码,发现一个有趣的问题:RBX寄存器的内容是什么,从哪里来的。尝试引用KiSystemServiceStart,发现在如下函数中设置了RBX:MOVRBXGS:188将Win32SyscallFilter.exe的内核线程结构加载到RBX中,验证如下:研究算法接下来的问题是RBX+0x78代表什么,事实证明,它代表了一系列标志位。下图中引用的两个标志是GuiThread和RestrictedGuiThread,分别位于标志的第6位和第19位。在我们的例子中,标志的值如下:由于线程不是GUI线程,重定向将其转换为GUI线程并返回相同的指针。继续执行:Win32kSyscallFilter不执行任何操作。但接下来的检查很有趣。RestrictedGuiThread标志表示如果启用了系统调用过滤,将在进程级别进行检测:因此,对于当前进程和线程,系统调用过滤是没有启用的。查看进一步执行会体现这个标志的重要性:如果开启了系统调用过滤功能,KeServiceDescriptorTableFilter将取代KeServiceDescriptorTableShadow;如果未启用过滤,将使用KeServiceDescriptorTableShadow。接下来观察系统调用表的使用情况,如下图所示:经过计算,RDI包含了系统调用的次数。对于WIN32K系统,它的值为0x20。因此,根据是否启用系统调用过滤,不同的表将加载到R10中。两个选项是:那么这个表会被转移到实际的函数调用中:按照上面的算法,我们在调试器中发现:Sothis很明显,系统调用号指向W32pServiceTable结构,偏移量为负值,然后指向真正的NtGdiDdDDICreateAllocation函数。这很好,但是如果启用系统调用过滤有什么区别,可以使用W32pServiceTableFilter验证:我们看到之前没有区别,因为NtGdiDdDDiCreateAllocation不是过滤的API之一,如果我们选择其他系统调用,比如NtGdiDdDDiCreateAllocation,它的系统调用号是0x117E。我们根据是否启用系统调用过滤来比较以下两个输出。首先是系统调用过滤没有开启:然后是系统调用过滤开启:我们发现如果开启了系统调用过滤功能,会调用另外一个系统调用不允许的函数。此过滤器函数验证是否启用了系统调用过滤并简单地终止系统调用。利用的结果现在我们了解了系统调用过滤的工作原理,我们需要研究它如何防止内核利用。首先我们要看它保护的是什么进程。到目前为止,只有MicrosoftEdge可以启用此功能。目前,没有第三方程序启用它的接口。这意味着系统调用过滤只关心MicrosoftEdge漏洞并且仅限于内核漏洞。下面我们可以看到启用了系统调用过滤的MicrosoftEdgeCP.exe的进程结构:过滤,该方法中使用的内核模式函数是:NtUserCreateWindowExNtUserDestroyWindowNtUserSetWindowLongPtrNtUserDefSetTextNtUserInternalGetWindowTextNtUserMessageCallwin32k.sys中没有stub_*方法的函数意味着它们不会被过滤。结论是Win32K系统调用过滤不会阻止可能导致漏洞的系统调用,但当系统调用触发漏洞时它肯定会阻止,无论是write-that-where还是缓冲区溢出。WIN32K系统调用过滤提供的保护很巧妙,但关键在于是否有系统调用触发漏洞。
