一、前言
在研究绕过UAC(用户账户控制)的新型技术时,我发现了一种新的绕过方法。虽然微软并不认为UAC属于安全边界范畴,但我们依然将这个问题反馈给微软,也愿意在此处与大家分享。我们已经在Windows 10 Build 17134上测试过这种方法。在深入分析绕过方法之前,本文首先将简要回顾一下UAC的背景知识,以便帮助大家了解UAC的内部工作原理。
二、背景知识
当隶属于Administrators组的用户想执行需要高权限的某个进程时,系统就会弹出UAC提示窗口,确认用户提升进程权限。然而,Windows系统中并非所有的特权可执行程序都会弹出UAC窗口,某些例外情况下并不会弹出该窗口,而会“自动提升”目标可执行文件的权限,这样就能绕过UAC机制(许多人肯定会感到非常惊讶)。
系统对这类受信任的可执行文件做了其他一些安全检查,以确保这些文件的确可信,不会被攻击者滥用。之前的UAC绕过技术中广泛利用了这种方法,本文的利用思路也不例外。然而,为了成功绕过UAC,我们需要在攻击过程中跳过一些“坑”。如果我们的可执行文件想实现“自动提权”,就必须遵循某些强制性条件。为了分析整个过程,这里我将给大家展示appinfo.dll(处理权限提升的AIS服务,属于UAC的核心组件之一)中的一些反汇编代码片段。
条件1:autoElevate为True
当出现程序权限提升情况时,系统就会对AIS(appinfo.dll)执行RPC调用,将目标可执行文件的路径作为调用参数。随后,该服务会映射目标可执行文件的内容以便后续读取,系统会尝试读取可执行文件的manifest信息,解析autoElevate字段(如果该字段存在)的值。
图1. 读取可执行文件的Manifest信息解析可能存在的autoElevate值
如果该字段存在并且值为True,那么系统会认为这是一个可以自动提升权限的可执行文件,随后执行权限提升,绕过UAC提示框(前提是该文件已通过另一个限制条件,下文会提到这个问题)。然而,这种autoElevate规则依然存在一个例外情况。无论manifest的具体内容是什么,如果文件名匹配某个白名单中的EXE名称,系统也会将其当成具备“自动提升权限的”可执行文件。如下图所示,代码中在manifest检查过程后有一个bsearch调用,检查文件名是否匹配白名单可执行文件名列表。如果文件名满足条件,那么就会自动提升权限,无视manifest数据。
图2. bsearch调用测试可执行文件名是否匹配自动提升权限的exe文件名
硬编码的白名单文件名部分列表如下:
cttunesvr.exe
inetmgr.exe
migsetup.exe
mmc.exe
oobe.exe
pkgmgr.exe
provisionshare.exe
provisionstorage.exe
spinstall.exe
winsat.exe’
条件2:经过签名
如果UAC请求所对应的二进制文件已配置为“自动提升权限”,那么系统就会使用wintrust!WTGetSignatureInfo来检查文件的签名。这意味着攻击者无法简单地构造manifest信息或者可执行文件名来实现自动权限提升,因为攻击者的文件很可能没有经过正确的签名处理,并且很有可能无法满足第3个条件(即从可信目录中执行)。
条件3:从可信目录中执行
实现自动权限提升的第3个条件:目标可执行文件位于“可信目录”中,比如C:\Windows\System32。如图3所示,AIS会检查权限提升请求所对应的文件路径,在本例中,“可信目录”为C:\Windows\System32。
图3. 检查可信路径
既然本文的名字为“如何通过模拟可信目录绕过UAC”,大家应该能猜到我们下文的内容了。
三、绕过UAC
在背景知识中我们提到过,如果满足如下条件,则可以实现权限自动提升(UAC绕过):
1、配置为权限自动提升;
2、经过正确签名;
3、从可信目录(C:\Windows\System32)中执行。
在检查可信目录时,Appinfo.dll(AIS)会使用RtlPrefixUnicodeString这个API来检查目标可执行文件路径是否以C:\Windows\System32\作为前缀。这是非常可靠的检查过程,因为系统会检查目标可执行文件的标准路径。为了绕过这个检查步骤,我构造了一个目录:C:\Windows \(请注意Windows后面跟着一个空格符)。当然这个路径并不能绕过RtlPrefixUnicodeString,并且由于Windows不允许在目录名末尾附加一个空格符,因此这并不是一个有效的目录名(或者至少不是一个“友好的”目录名)。然而,如果我们使用CreateDirectory API,然后在待创建的目录名前加上\\?\,那么我们就可以绕过系统的一些命名规则,将目录创建请求直接发送给文件系统。
图4. 调用API
这将导致系统中出现一个尴尬的目录,能够与真正的C:\Windows\目录和谐共存(然而我们不能在Windows资源管理器中对这个目录做任何操作)。
图5. 目录删除请求无法成功,也无法通过重命名来删除末尾的空格符
现在我们已经创建了一个C:\Windows \目录,我们可以在该目录中创建一个system32目录,然后将C:\Windows\System32目录中经过签名的、能够自动提升权限的可执行应用拷贝到该目录中。这里我选择拷贝winSAT.exe(这是位于自动提升权限白名单中的一个Windows可执行文件)。当我们尝试运行C:\Windows \System32\winSAT.exe时,appinfo.dll在检查可信目录时会先调用如下API(如图6所示)。这一点非常关键,这也是我们为什么能绕过UAC的原因所在。
图6. API调用过程
当请求权限提升过程将这个尴尬的目录发送到AIS时,该路径会传递给GetLongPathNameW函数,该函数会将该路径转换回C:\Windows\System32\winSAT.exe(删除了空格符)。非常完美,现在这个路径已经变成了可信目录(可以通过RtlPrefixUnicodeString的检查)。更加完美的是,当完成可信目录检查后,后续检查(以及最终的权限提升请求)依然会使用原始的文件名(带有空格符)。这样我们就能通过其他所有检查,最终让appinfo.dll执行我们复制的winSAT.exe,自动提升权限(因为该文件已经过正确签名,并且处于自动提升权限的白名单中)。
为了通过这种方法真正执行攻击者的代码,我将一个伪造的WINMM.dll(该dll会被winSAT.exe加载)释放到了C:\Windows \System32\中,通过本地dll劫持技术执行我们的代码。完整的流程如下图所示。
图7. 完整利用过程
完整代码可以访问Github下载。
文章原文链接:https://www.anquanke.com/post/id/164058