Razer Synapse 3 Windows客户端本地提权漏洞分析

 

一、漏洞简介

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执行完该操作后,目标服务就会将这个运行列表传递给SimpleInjectorRegisterPackages()函数。

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: 公布漏洞报告

(完)