标题 | 简介 | 类型 | 公开时间 | ||||||||||
|
|||||||||||||
|
|||||||||||||
详情 | |||||||||||||
[SAFE-ID: JIWO-2024-2895] 作者: 闲云野鸡 发表于: [2021-06-12]
本文共 [560] 位读者顶过
Windows 打印组件自 Win2000时代就被引入,作为一个”历史悠久”的组件其安全问题也一直广受安全研究人员关注。近期围绕 printer port 相关机制就连续爆出安全问题,本文旨在对相关漏洞进行一个简要梳理介绍。 [出自:jiwo.org]
Windows 打印组件简介Windows 打印组件是一个典型的c/s架构,如图1-1所示: 图1-1 客户端通过 gdi api 经由 winspool.drv 模块,向 spoolsv 进程发送 rpc 请求,spoolsv 进程经由 spoolss.dll 将打印请求分发给不同的 print provider 处理,本地的打印请求由 localspl.dll 模块进行处理,而远程的打印请求则通常由win32spl.dll 模块进行处理如图1-2所示: 图1-2
漏洞分析需要注意的是作为 server 端的 spoolsv 进程是 system 权限,这也意味着如果spoolsv 没有正确处理 client 发送过来的打印请求,很容易产生 EOP 类漏洞。 当我们进行一次打印操作时,我们需要指定一个 printer port,这里的 printer port 不仅仅局限于通常的串口设备,也可以是一个文件路径,当 printer port 为一个文件路径时,我们打印的内容会被写入到指定的文件中,我们用 powershell模拟一下该过程: 我们使如上ps脚本创建了打印机”Printer1”,指定portname为路径:”c:\users\xxxx\1.txt”,接着我们向打印机发送打印内容: 我们可以看到相应内容已经被写入到文件中: 这时,对 Windows 逻辑漏洞熟悉的朋友想必会产生一个这样的想法,如果把portname 设定为一个只有高权限用户才可以写的路径那会怎么样呢,如果能成功的话就是一个典型的逻辑漏洞导致eop的场景了,用 processmonitor 观测到的测试结果如下: 很明显这是由于 spoolsv 进程 impersonate 当前用户进行文件操作而导致权限不足拒绝访问的错误,这样看起来似乎并没有什么问题,但微软可能是为了应对打印过程中有可能出现各种中断异常的状况,spoolsv 进程在重启后,会解析 shd 文件,并以 system 权限继续进行shd中记录的文件打印行为,如下是我们在processmonitor 中观察到的现象: 很明显,spoolsv 进程在重启后以 system 权限创建了c:\windows\system32\1.txt。 以上就是 CVE-2020-1048的漏洞原理。微软为了修补这个漏洞再添加 printport 的过程中引入了两个校验函数:IsValidNamedPipeOrCustomPort 和PortIsValid,第一个函数主要判断是不是命名管道和 customport,如果不是则进入 PortIsValid 的判断: PortIsValid 通过调用createfile判断对于目标路径是否有可写权限来决定用户提供的port是否有效,然后这又是一个典型的toctou的场景,我们可以构造一个合法路径使其通过 PortIsVaild 的校验,如:c:\temp\1.txt,在通过 PortIsValid 的校验后,我们创建一个 junction 将 c:\temp 指向 c:\windows\system32,这样在spoolsv 进程重启后依旧会往system32目录写入1.txt文件,这便是 CVE-2020-1337的漏洞原理,微软为了解决这个问题又引入了一个新的校验函数 IsPortAlink: 其主要内容如下(注不仅仅是在添加port时调用了该函数进行校验,在打印过程中也校验了): 主要逻辑时在 createfile 打开攻击者提供的portname后得到一个句柄A,通过GetFinalPathNameByHandlew 获得句柄A对应的路径,将其和攻击者提供的portname比较看是否一致,若不同的话则判定为 link,也就是该 portname 非法。然而 GetFinalPathNameByHandlew 函数在处理 unc 路径时存在一个问题,即使 unc 路径中包含 junction 也不会将其转换为最终地址,请看如下代码: 在上述代码中 c:\test 是一个 junction 目录,指向 c:\windows\system32,我们看看程序最后的输出: 也就是说在 unc 路径中即使包括 junction 目录 GetFinalPathNameByHandlew函数也不会将其转换为目标路径,这样也就绕过了 IsPortAlink 函数的校验,这便是 CVE-2020-17001 的漏洞原理。
启示可以说从 CVE-2020-1048 到 CVE-2020-17001 微软向广大安全研究员详细的展示了逻辑漏洞的经典场景: 1、特权进程没有 impersonate 便对普通用户可控制的资源进行访问 2、totou 即 check 和 use 的不一致性。
参考链接https://docs.microsoft.com/en-us/windows/win32/printdocs/documents-and-printing https://windows-internals.com/printdemon-cve-2020-1048/ https://safebreach.com/Post/How-we-bypassed-CVE-2020-1048-Patch-and-got-CVE-2020-1337 https://bugs.chromium.org/p/project-zero/issues/detail?id=2075 |