一、漏洞简介
Razer Synapse(雷云)软件在系统中安装了一个服务(Razer Synapse Service),该服务以NT AUTHORITY\SYSTEM
权限运行,会加载C:\ProgramData\Razer\
目录中的多个.NET assembly。C:\ProgramData\Razer\*
及其子目录/文件在权限方面没有严格控制,通过身份认证的任何用户可以具备这些目录/文件的完全控制权限(FullControl
)。攻击者有可能绕过签名检测机制,通过assembly侧加载方法提升至SYSTEM
权限。
该漏洞已在Windows 10 1803 (x64)系统上的Razer Synapse 3 (3.3.1128.112711)Windows客户端上复现成功。
二、漏洞分析
当Razer Synapse服务启动时,会加载C:\ProgramData\Razer\*
目录(包括子目录,比如C:\ProgramData\Razer\Synapse3\Service\bin
)中的.NET assembly。
观察C:\ProgramData\Razer\Synapse3\Service\bin
上设置的DACL权限时,我们可以发现Everyone
具备该目录的FullControl
权限(也包括该目录中的任何文件)。
从理论上讲,攻击者可以将该目录中已有的.NET assembly替换成恶意assembly,重启系统,就可以让Razer Synapse Service加载恶意assembly。这种方法需要考虑一些复杂情况,比如竞争条件,需要在服务加载源assembly前执行替换操作。此外,该服务中存在一些检查机制,攻击者在加载assembly前需要绕过这些机制。为了能成功利用漏洞,我们需要澄清成功加载assembly的具体条件。
首先要解决的第一个问题就是如何植入恶意assembly,使目标服务能够加载该assembly。对低权限用户来说,劫持已有的assembly可能是比较有挑战的一个任务,因为低权限用户无法停止或者启动Razer Synapse服务。这意味着为了触发加载assembly的代码路径,攻击者需要重启目标系统。攻击者可以通过这种方式在条件竞争中获胜,将合法的assembly替换成恶意的assembly。其实这个问题很容易解决,观察目标服务,我们发现该服务会递归枚举C:\ProgramData\Razer\*
中的所有DLL:
这意味着我们可以简单地将一个assembly存放在其中一个目录中(如C:\ProgramData\Razer\Synapse3\Service\bin
),这样目标服务就会将其视为一个已有的、有效的assembly。
递归枚举C:\ProgramData\Razer\*
中的所有DLL后,该服务会尝试确保这些assembly都带有Razer的签名。目标服务首先会获取“Razer.cer”的证书信息。然后在每个assembly上调用X509Certificate.CreateFromSignedFile(),接着比较“Razer.cer”证书链与待加载的assembly的证书链。
如果assembly的证书链与Razer.cer的不匹配,那么目标服务就不会加载该DLL。虽然在加载.NET assembly之前先检查证书是一个很不错的选择,但目标在具体实现上并不健壮,这是因为X509Certificate.CreateFromSignedFile()
只会提取证书链信息,并不会去检查待检查的文件签名是否有效(参考此处资料)。这意味着攻击者有可能使用类似SigPirate之类的工具,将合法的Razer assembly证书克隆到恶意的assembly中(因为目标服务并不会去校验签名是否有效)。
一旦assembly通过证书检测过程,目标服务就会利用Assembly.LoadFile(),将其加载到当前的app域中。目标服务在调用Assembly.LoadFile()
的过程中并不会执行任何恶意代码,但会在加载assembly后,检查assembly中是否实现了IPackage接口。
该接口是SimpleInjector
项目特有的一个接口(可参考该项目详细说明文档)。为了绕过这个检查过程,我们只需要在我们恶意的assembly中实现IPackage
接口即可。一旦目标服务通过assembly的证书链验证,并且验证assembly中存在IPackage
接口,就会将assembly加入运行列表中。对C:\ProgramData\Razer\*
中的所有assembly执行完该操作后,目标服务就会将这个运行列表传递给SimpleInjector
的RegisterPackages()
函数。
RegisterPackages()
会认为该列表中的assembly都已通过校验,然后调用每个assembly IPackage
接口中的RegisterServices()函数。
作为攻击者,这正是我们可以用来执行恶意代码的具体位置。我们只需要将恶意代码逻辑加入我们构造的恶意assembly IPackage
接口的RegisterServices()
方法中即可。
到目前为止,我们已经找到了攻击路径,可以滥用正常服务来提升代码执行权限,具体步骤如下:
1、编写自定义的assembly,其中实现了来自SimpleInjector
项目的IPackage
接口;
2、将恶意逻辑加入IPackage
接口的RegisterServices()
方法中;
3、编译assembly,然后使用类似SigPirate
的工具,从合法的Razer assembly中克隆证书链;
4、将最终生成的恶意assembly释放到C:\ProgramData\Razer\Synapse3\Service\bin
目录中;
5、重启服务或者重启主机。
三、漏洞利用
澄清如何提升代码执行权限后,我们现在可以着手利用这一过程。首先,我们需要创建自己的恶意assembly,实现所需的IPackage
接口。为了完成该任务,我们可以在代码中引用SimpleInjector项目中的SimpleInjector
以及SimpleInjector.Packaging
assembly。添加引用语句后,我们还需要实现对应接口、添加恶意逻辑。典型的PoC assembly代码如下所示:
由于Razer为32位服务,因此我们需要以x86架构来编译assembly。编译完成后,我们需要绕过证书链验证机制。由于服务使用的是X509Certificate.CreateFromSignedFile(),并且没有验证签名,因此我们可以使用SigPirate,从已签名的Razer assembly中克隆证书:
我们可以在PowerShell中使用Get-AuthenticodeSignature
,验证SigPirate
正确设置了lol.dll
的证书:
此时,我们已经生成了一个恶意的assembly,其中包含带有“后门”的IPackage
接口,并且从合法的Razer assembly克隆了证书链,那么最后一步就是将lol.dll
释放到C:\ProgramData\Razer\Synapse3\Service\bin
中,然后重启主机。一旦主机重启,我们可以看到以SYSTEM
权限运行的Razer Synapse Service.exe
会加载C:\ProgramData\Razer\Synapse3\Service\bin
目录中的lol.dll
,最终导致IPackage
接口中的RegisterServices()
方法成功运行cmd.exe
。
当服务加载lol.dll
时,由于该assembly克隆了证书,因此会将其当成合法的assembly,并且由于我们在IPackage
实现代码中添加了“恶意”逻辑,因此最终达到了EoP(权限提升)效果。
Razer实现了名为Security.WinTrust
的一个命名空间,修复了这个漏洞,该命名空间包含完整性检查功能。打上补丁后,现在当目标服务获取Razer目录中的所有*.dll
文件后,会立即调用WinTrust.VerifyEmbeddedSignature()
函数执行检查操作。
来分析一下WinTrust.VerifyEmbeddedSignature()
,该函数会使用WinTrust.WinVerifyTrust()
来验证待检查的文件的确具备有效的签名:
如果文件具备有效的签名,并且签发者为Razer,那么目标服务就会继续前文分析的代码流程,在加载assembly之前检查其中是否包含有效的IPackage
接口。添加文件完整性验证机制后,现在攻击者无法克隆合法Razer文件的证书信息,因为克隆出的新文件无法通过签名校验过程。
如果大家想进一步了解证书验证方面内容,我推荐大家参考Matt Graeber之前写的一份白皮书:Subverting Trust in Windows。
四、时间线
06/05/2018: 向Razer的HackerOne项目提交漏洞报告
06/08/2018: 厂商通过H1确认报告
06/08/2018: H1工作人员要求我们提供Synapse 3安装程序的具体版本号
06/08/2018: 将Synapse 3版本号提供给Razer
07/05/2018: 请求更新信息
08/06/2018: 报告已被分类
08/27/2018: 请求更新信息,厂商未回应
09/14/2018: 请求更新信息,也通过电子邮件直接联系以加快沟通进度,厂商未回应
12/14/2018: 通过Twitter联系Razer请求提供安全的联系方式
12/14/2018: H1项目经理开始调查H1报告
12/15/2018: Razer CEO Min-Liang Tan要求直接将电子邮件发送给安全团队
12/16/2018: 信息安全经理和软件高级副总裁通过邮件直接与我们联系,告诉我们将于几周内推出补丁
12/19/2018: 下载最新版的Synapse 3,研究存在漏洞的代码路径。向Razer的H1项目提交更多信息,也通知了Razer的信息安全经理
12/25/2018: Razer的某位员工联系我,提供了一个内部版本链接,以便后续漏洞反馈
12/27/2018: 根据他们的要求,我们通过H1报告提供有关环节措施的反馈
01/09/2019: 通过H1要求厂商给出更新版产品的公布时间线
01/10/2019: 厂商告知已公开新版本
01/10/2019: 报告流程已关闭
01/10/2019: 要求公开披露漏洞信息
01/10/2019: Razer同意公开披露漏洞信息
01/21/2019: 公布漏洞报告