行业解决方案查看所有行业解决方案
IDA 用于解决软件行业的关键问题。
发布时间:2022-10-10 15: 57: 34
几周前,微软检测到一个0-day远程代码执行漏洞被用来在有限且有针对性的攻击中攻击SolarWinds Serv-U FTP软件。微软威胁情报中心(MSTIC)根据观察到的受害者特征、策略和程序,高度自信地将这次攻击归咎于DEV-0322,这是一个在中国境外开展活动的组织。在此博客中,我们与SolarWinds分享了有关该漏洞的技术信息(编号为CVE-2021-35211),SolarWinds及时发布了安全更新以修复漏洞并缓解攻击。
该分析由Microsoft进攻性研究与安全工程团队进行,该团队负责支持MSTIC等具有漏洞利用开发专业知识的团队。我们团队的职责是让计算更安全。为此,我们利用我们对攻击者技术和流程的知识,通过逆向工程、攻击创建和复制、漏洞研究和情报共享来构建和改进Windows和Azure中的保护。
7月初,MSTIC向我们的团队提供了似乎表明针对SolarWinds Serv-U FTP服务器的SSH组件中新发现的漏洞的利用行为的数据。尽管intel包含有用的指标,但它缺少相关的漏洞利用,因此我们的团队着手重建漏洞利用,这需要首先找到并了解Serv-U SSH相关代码中的新漏洞。
由于我们知道这是一个远程的预授权漏洞,我们很快构建了一个专注于SSH握手的预授权部分的模糊器,并注意到该服务捕获并通过了所有访问违规而不终止进程。很明显,Serv-U进程将使隐蔽、可靠的利用尝试变得容易完成。我们得出的结论是,被利用的漏洞是由Serv-U最初创建OpenSSL AES128-CTR上下文的方式引起的。反过来,这可能允许在解密连续的SSH消息期间使用未初始化的数据作为函数指针。因此,攻击者可以通过连接到开放的SSH端口并发送格式错误的预授权连接请求来利用此漏洞。
我们通过Microsoft安全漏洞研究(MSVR)通过协调漏洞披露(CVD)与SolarWinds分享了这些发现以及我们创建的模糊器,并与他们一起解决了这个问题。这是情报共享和行业协作的示例,通过检测产品攻击和通过安全更新修复漏洞,从而为更广泛的社区提供全面保护。
Serv-U实现SSH的漏洞
Secure Shell(SSH)是一种广泛采用的协议,用于在不受信任的网络上进行安全通信。协议行为在多个评论请求(RFC)中定义,现有实现可在开源代码中获得;我们主要使用RFC 4253、RFC 4252和libssh作为此分析的参考。
通过枚举对“SSH-”字符串的引用,可以找到Serv-U中SSH的实现,该字符串必须存在于发送到服务器的第一个数据中。此类代码最可能的实例如下:
在上面的代码上设置断点并尝试使用SSH客户端连接到Serv-U证实了我们的假设,并导致断点被以下调用堆栈命中:
此时,我们注意到Serv-U.dll和RhinoNET.dll都禁用了ASLR支持,这使得它们成为ROP小工具的主要位置,因为它们中的任何地址对于给定Serv在互联网上运行的任何服务器实例都是不变的-U版本。
在逆向RhinoNET和Serv-U DLL中的相关代码后,我们可以在Serv-U处理它们时跟踪SSH消息的路径。为了处理传入的SSH连接,Serv-U.dll创建一个CSUSHSocket对象,该对象派生自RhinoNET!CRhinoSocket类。CSUSSHSocket对象的生命周期是TCP连接的长度——它可能在许多单独的TCP数据包中持续存在。底层CRhinoSocket为套接字提供缓冲接口,以便单个TCP数据包可以包含任意数量的字节。这意味着单个数据包可能包含任意数量的SSH消息(只要它们符合最大缓冲区大小),以及部分SSH消息。然后CSUSSHSocket::ProcessRecvBuffer函数负责从缓冲的套接字数据中解析SSH消息。
CSUSSHSocket::ProcessRecvBuffer首先使用ParseBanner检查SSH版本。如果ParseBanner成功地从横幅中解析SSH版本,则ProcessRecvBuffer会循环ParseMessage,它会在套接字数据中获取指向当前消息的指针,并从消息中提取msg_id和长度字段(稍后将详细介绍ParseMessage函数)。
被迭代的套接字数据在概念上是伪C结构ssh_msg_t的数组,如下所示。消息数据包含在有效负载缓冲区中,其第一个字节被认为是msg_id:
ProcessRecvBuffer然后根据msg_id分派对消息的处理。一些消息直接从消息解析循环处理,而另一些则传递给ssh_pkt_others,后者将消息发布到队列中,以供另一个线程提取和处理。
如果msg_id被推迟到备用线程,CSSHSession::OnSSHMessage会处理它。此功能主要处理需要与Serv-U管理的用户配置文件数据交互的消息(例如,针对每个用户凭据的身份验证)和UI更新。CSSHSession::OnSSHMessage在漏洞搜寻方面被证明是无趣的,因为其中的大多数消息处理程序都需要成功的用户身份验证(初始遥测表明这是一个预身份验证漏洞),并且在其余处理程序中没有发现漏洞。
当最初对带有调试器的Serv-U运行模糊器时,很明显应用程序正在捕获通常会使进程崩溃的异常(例如访问冲突),记录错误,修改状态足以避免进程终止,然后继续,好像没有问题一样。这种行为提高了文件服务器应用程序的正常运行时间,但也导致可能的内存损坏在进程中徘徊并随着时间的推移而累积。作为攻击者,这提供了诸如暴力破解代码地址或具有动态地址的数据的机会。
这种对访问违规的挤压有助于利用,但对于模糊测试,我们过滤掉了由读/写访问违规产生的“无趣”异常,并让模糊器运行直到遇到RIP已损坏的错误。这很快导致了以下崩溃上下文:
如上所示,libeay32.dll(OpenSSL的一部分)中的CRYPTO_ctr128_encrypt试图调用无效地址。使用的OpenSSL版本是1.0.2u,所以我们获得了源代码来阅读。下面展示了相关的OpenSSL功能:
同时,下面显示了传递的结构:
崩溃函数是通过以下路径从OpenSSL API边界到达的:EVP_EncryptUpdate->evp_EncryptDecryptUpdate->aes_ctr_cipher->CRYPTO_ctr128_encrypt。
进一步查看调用堆栈,很明显Serv-U从CSUSSSHSocket::ParseMessage调用EVP_EncryptUpdate,如下所示:
此时,我们手动最小化了fuzzer产生的TCP数据包缓冲区,直到只剩下触发崩溃所需的SSH消息。在RFC中使用的符号中,所需的SSH消息是:
请注意,以下描述引用了当崩溃代码路径明显试图解密缓冲区时调用的“加密”函数。这不是错误:Serv-U使用加密OpenSSL API,虽然对于代码清晰度来说不是最佳的,但它在行为上是正确的,因为高级加密标准(AES)在计数器(CTR)模式下运行。
在通过消息处理序列进行时间旅行调试跟踪和调试后,我们发现问题的根本原因是Serv-U最初使用如下代码创建了OpenSSL AES128-CTR上下文:
使用NULL密钥和/或IV调用EVP_EncryptInit_ex是有效的,并且Serv-U在这种情况下这样做是因为上下文是在处理KEXINIT消息时创建的,这是在密钥材料准备好之前。但是,在设置密钥之前不会执行AES密钥扩展,并且在执行密钥扩展之前,ctx->cipher_data结构中的数据保持未初始化状态。我们可以(正确地)推测我们的消息序列导致崩溃导致enc_algo_client_to_server->decrypt在初始化密钥材料之前调用。Serv-U KEXINIT处理程序为消息中给出的所有参数创建对象。
但是,在处理以下NEWKEYS消息之前,当前为连接活动的相应对象不会被新创建的对象替换。客户端总是在正常的SSH连接中完成密钥交换过程,然后再发出NEWKEYS消息。无论连接状态或密钥交换如何,Serv-U都会处理NEWKEYS(从而设置m_bCipherActive标志并替换密码对象)。由此,我们可以看出,我们的模糊序列中的最后一个消息类型无关紧要——在部分初始化的AES CTR密码对象被激活后,只需要在套接字缓冲区中处理一些数据即可触发解密。
开发
由于该漏洞允许从未初始化的内存中加载RIP,并且在过程中有一些没有ASLR的模块,因此利用并不那么复杂:我们可以找到一种方法来控制未初始化的cipher_data结构的内容,指向cipher_data->block函数指针在一些初始的ROP小工具上,并启动一个ROP链。由于异常处理程序会导致任何错误被忽略,我们不一定需要在第一个数据包上获得可靠的代码执行。可以重试利用,直到代码执行成功,但是这会在日志文件中留下痕迹,因此可能值得投入更多精力来避免记录日志的不同技术。第一步是找到密码数据分配,因为预填充缓冲区的最直接途径是喷射目标分配大小的分配并在尝试将地址回收为cipher_data之前释放它们。ctx->cipher_data在EVP_CipherInit_ex中使用以下行进行分配和分配:
使用调试器,我们可以看到我们案例中的ctx_size是0x108,并且这个分配器最终会调用ucrtbase!_malloc_base。从前面的逆向我们知道,CRhinoSocket和CSUSSSHSocket级别的数据包解析都调用operator new[]来分配空间来保存我们发送的数据包。幸运的是,这也以ucrtbase!_malloc_base结束,使用相同的堆。因此,预填充目标分配就像发送一个适当大小的TCP数据包或SSH消息然后关闭连接以确保它被释放一样简单。使用此路径进行喷洒不会触发其他相同大小的分配,因此我们不必担心会污染堆。
从调试器/反汇编中提取的另一个重要值是offsetof(EVP_AES_KEY,block),因为需要将喷射数据中的偏移量设置为初始ROP小工具。该值为0xf8。方便的是,EVP_AES_KEY结构的其余大部分可用于ROP链内容本身,并且在受控函数指针调用时,寄存器rbx、r8和r10中存在指向该结构基址的指针。
作为一个简单的概念证明,请考虑以下python代码:
以上结果在调试器中产生以下上下文:
结论:负责任的披露和行业合作提高了所有人的安全性
我们的研究表明,Serv-U SSH服务器存在预授权远程代码执行漏洞,可以在默认配置中轻松可靠地利用该漏洞。攻击者可以通过连接到开放的SSH端口并发送格式错误的预授权连接请求来利用此漏洞。成功利用后,该漏洞可能允许攻击者安装或运行程序,例如我们之前报道的针对性攻击。
我们通过协调漏洞披露(CVD)将我们的发现分享给了SolarWinds。我们还分享了我们创建的模糊器。SolarWinds发布了一个咨询和安全补丁,我们强烈鼓励客户应用该补丁。如果您不确定您的系统是否受到影响,请在SolarWinds客户门户中打开支持案例。
除了与SolarWinds共享漏洞详细信息和模糊测试工具外,我们还建议为Serv-U进程中加载的所有二进制文件启用ASLR兼容性。启用ASLR是一个简单的编译时标志,默认情况下启用,并且从Windows Vista开始可用。ASLR是针对暴露于不受信任的远程输入的服务的关键安全缓解措施,它要求进程中的所有二进制文件都是兼容的,以便有效地防止攻击者在其漏洞利用中使用硬编码地址,这在Serv-U中是可能的。
我们要感谢SolarWinds的及时响应。该案例进一步强调了软件供应商、安全研究人员和其他参与者之间需要不断协作,以确保用户计算体验的安全性。
原文地址:https://www.microsoft.com/security/blog/2021/09/02/a-deep-dive-into-the-solarwinds-serv-u-ssh-vulnerability/
展开阅读全文
︾
读者也喜欢这些内容:
逆向工程是什么意思?逆向工程核心原理是什么?
逆向工程是什么意思?逆向工程核心原理是什么?逆向工程作为一种复杂而又精细的技术手段,广泛应用于软件开发、硬件分析以及信息安全领域。通过分析产品的最终形态,逆向工程能够揭示出产品的设计理念、构造方法和运作原理,从而为技术创新或者产品改进提供参考和灵感。...
阅读全文 >
IDA怎么修改字符串内容?IDA修改后怎么保存?
在软件开发和逆向工程领域,IDA Pro是一种极其强悍的工具,广泛用于程序剖析、调试和修改。它不仅支持多种处理器架构,还提供了大量的作用,以适应高档讲解的必须。本文将围绕ida怎么修改字符串内容,ida修改后怎么保存这一主题,详细描述怎样在IDA中更改字符串内容,及其修改后的存放方式。此外,我们还将探讨IDA转变的应用场景,帮助读者更深入地了解IDA的实际应用价值。...
阅读全文 >
IDA Pro Wakatime插件 Wakatime插件下载
ida-wakatime-py是什么?它是为IDA Pro提供WakaTime集成插件。...
阅读全文 >
IDA Pro 键盘快捷键(Shortcut keys)
...
阅读全文 >