反病毒软件的漏洞分析和利用: K7 安全软件中的本地权限提升漏洞(CVE-2019-16897)
导语:在过去的几个月里,我一直在研究利用逻辑错误来攻击反病毒软件。 我将以我在 K7 杀毒软件中发现的漏洞为例,简要讨论对这些安全产品执行漏洞研究的方法。
反病毒软件的漏洞利用
嘿,伙计们,好久没有发表文章了! 在过去的几个月里,我一直在研究利用逻辑错误来攻击反病毒软件。 我将以我在 K7 杀毒软件中发现的漏洞为例,简要讨论对这些安全产品执行漏洞研究的方法。
负责声明:我并不声称知道关于漏洞研究或利用的一切,所以如果在这篇文章中有错误,请让我知道。
目标选择
像杀毒软件这样的安全产品是一个很有吸引力的目标(至少对我来说是这样),因为它们在作为驱动程序的内核和作为特权服务的用户环境中都是可信任和拥有特权的。这意味着它们能够促进特权的潜在升级或以其他方式访问特权功能。它们在操作系统的低特权空间中占有一席之地。例如,可能存在用户可以与之交互的UI组件,有时允许更改选项,如启用/禁用反病毒功能、添加目录或文件排除、扫描文件以查找恶意软件。反病毒软件还必须访问和执行操作系统对象的操作,以检测恶意软件,如读取文件、注册表项、内存等,以及能够执行特权操作,以保持系统在一个受保护的状态,无论情况如何。在这个受信任的高特权空间和不受信任的低特权空间之间,有趣的事情发生了。
攻击面分析
正如前面提到的,抗病毒软件存在于权限边界的两端,如下图所示:
任何越过高特权和低特权之间的界限的都代表攻击面。
让我们看看如何解释这个图。用户界面与预期的服务流程共享公共操作。如果用户希望执行特权操作,则假定安全检查通过,服务将代表用户执行特权操作。如果用户希望更改设置,则打开用户界面并单击按钮。这是通过某种形式的进程间通信(IPC)传递给服务进程的,IPC将执行必要的操作,例如,杀毒软件将其配置存储在注册表中,因此,服务将打开相关的注册表项并修改一些数据。请记住,注册表项存储在HKEY_LOCAL_MACHINE 的 hive 中,该hive位于高特权空间中,因此需要高特权进程修改其数据。因此,来自低权限的用户能够间接地修改高权限对象。
再举一个例子。 用户可以通过用户界面扫描恶意软件(当然,如果不允许用户扫描恶意软件,那么反病毒软件对用户来说又有什么好处呢?) 。 一个简单的,运行良好的软件,会出什么问题? 由于执行恶意软件扫描是服务进程的职责,因此接口将信息传递给服务进程以锁定文件。 为了执行扫描,它必须与文件进行交互,也就是说,它必须在磁盘上定位文件并读取其内容。 如果在读取文件数据并扫描恶意软件时,反病毒软件没有将文件锁定在磁盘上,那么恶意软件就有可能被替换为一个指向高特权目录中的文件的符号链接(是的,这是可能的) ,让我们使用 notepad.exe做个说明。 当扫描完成并被确定为恶意软件时,服务进程可以删除该文件。 然而,这个恶意软件已经被 notepad.exe 的链接所替代! 如果反病毒程序没有检测到并拒绝符号链接,它将毫无疑问地删除 notepad.exe。 这是一个时间检查到使用时间(TOCTOU)1竞争条件错误的示例。 同样,来自低特权的用户能够间接修改高特权对象,因为服务进程充当了代理。
漏洞利用
该漏洞允许低权限用户通过反病毒设置修改(几乎)任意注册表数据。 但是,低特权用户(非管理员)不能更改反病毒设置。
绕过管理检查
为了缩小管理检查的执行范围,可以在再次访问设置页面时使用 procmon 标识操作系统活动。 这将触发反病毒程序重新检查当前用户的管理状态,同时在记录时与操作系统进行交互。 当然,由于我们是低权限的,并且 procmon 需要高权限,因此在实际环境中是不实用的。 但是,由于我们控制了测试环境,因此我们可以允许 procmon 运行,因为我们可以访问管理员帐户。 将 promon 中设置为 K7TSMain 作为进程名称的过滤器,将捕获由用户界面进程执行的活动。
当 procmon 开始记录日志时,尝试再次访问 UI 中的设置页面将触发 procmon 立即显示结果:
procmon 捕获的管理检查
可以看到,反病毒程序将管理检查存储在注册表中的AdminNonAdminIsValid。查看事件属性窗口中的值,返回为 0,这意味着不允许非管理员用户操作。但这里有个小问题。如果你能发现它,还有额外的加分。
现在我们知道了检查在哪里执行,下一步就是绕过它。 Procmon 显示进程正在低权限空间中运行,正如用户指示的那样,中等完整性意味着我们拥有进程权限。 如果它不受保护,我们可以简单地HOOK RegQueryValue 函数并修改返回值。
挂载到 K7TSMain 进程
使用 x32dbg 可以成功附加到K7TSMain.exe进程!当我们试图再次访问设置页面时,RegQueryValueExA上的断点已被设置。
触发 RegQueryValueExA 断点
x32dbg在单击设置页面时捕获断点。被查询的值的名名称是ProductType,但是我们希望是 AdminNonAdminIsValid,所以我们需要继续执行,触发下一个断点。
设置在 AdminNonAdminIsValid 上的断点
现在我们可以看到AdminNonAdminIsValid。要修改返回值,可以允许函数一直运行到返回为止。但是,调用函数看起来像RegQueryValueExA的包装:
因此继续执行,直到返回值显示执行检查的真正函数:
管理员检查函数
这里有一个值为 1 的检查,但是注册表数据当前返回的值是0。这决定了这个函数的返回值,所以我们可以改变[esp+4]或改变返回值来绕过检查:
绕过管理员检查
拦截行程间通讯
在 Windows 上有多个行程间通讯的方法,比如 mailslot、文件映射、 COM 和命名管道。 我们必须弄清楚哪些是在产品中实现的,以便能够分析协议。 一个简单的方法是使用 API Monitor 记录进程执行的选择函数调用。 当我们这样做的时候,然后应用一个改变的设置,我们可以看到对命名管道函数的引用:
注意,调用模块是K7AVOptn.dll,而不是k7tsm.exe。如果我们看一下通过TransactNamedPipe传输的数据,我们可以看到一些有趣的信息:
首先,它看起来像一个使用|符号分隔的扩展名列表(.ocx, .exe, .com),其中一些具有通配符匹配。这可能是扫描恶意软件的扩展列表。如果我们查看一下反病毒程序保存配置的注册表,我们可以在RTFileScanner键的值ScanExtensions下看到类似的东西:
继续往下看,其中一个调用包含了一些非常有趣的数据:
看起来似乎反病毒软件是通过指定(特权)注册表项和它们的值的完整键路径来应用某个值。下一个明显的步骤是查看更改其中一个键及其值是否有效。这可以通过在x32dbg中的TransactNamedPipe函数上设置断点来实现:
在这里,定位第二个参数中的输入缓冲区,并修改数据以在HKEY_LOCAL_MACHINE 的 hive中添加或更改密钥,如下所示:
如果可以更改这个注册表项的值,高权限进程将被迫加载AppInit_DLLs 中列出的dll,即我们控制的dll。 LoadAppInit_DLLs值也必须设置为1(默认为0)才能启用此功能。结果如下:
触发有效载荷
你可能已经注意到,注册表项驻留在 Wow6432Node 中,该节点是注册表的32位副本。 这是因为产品是32位的,所以 Windows 会自动重定向注册表更改。 在64位 Windows 中,进程通常是64位的,因此通过 AppInit_DLLs 加载有效载荷 DLL 的可能性不大。 一个可靠的方法是使用杀毒软件,因为它是32位的,假设一个有特权的组件可以启动。最简单的方法是重新启动机器,因为它会重新加载所有的杀毒程序。在UI上单击,会显示更新函数在NT AUTHORITY\SYSTEM用户下运行K7TSHlpr.exe:
因为它是一个32位应用程序文件夹,Windows 会将我们的AppInit_DLLs列表中的 DLL 加载到进程空间中。
使用系统的 cmd.exe 作为有效载荷,将通过UI0Detect服务在NT AUTHORITY\ SYSTEM 帐户上下文中提示用户一个交互式会话:
选择查看消息会出现以下结果:
我们得到了 SYSTEM 权限。
自动化漏洞利用
发表评论