创造力是渗透测试的核心,这也让我们的工作充满趣味。然而,一个常见的陷阱是倾向于“过度设计”攻击场景,并只专注于 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*)。