总结
这项研究由三部分组成,涵盖不同的领域,即开发自定义 RPC 客户端、逆向工程和一些加密技术。
当这项研究开始时,目标实际上是找到一种利用旧 CVE 的新方法,然而深入研究产品后出现了一个新的兔子洞,从而导致了一个新的漏洞。
本部分研究解释了 Windows 环境中远程过程调用 (RPC) 的复杂部分以及如何开发自定义客户端。
CVE:
https://nvd.nist.gov/vuln/detail/CVE-2024-36036
https://nvd.nist.gov/vuln/detail/CVE-2024-36037
背景
ManageEngine 是一家拥有约 50 种不同产品的公司,产品范围从完整的 SIEM 解决方案到移动设备管理系统,在全球拥有 280,000 多家客户。本文将深入介绍 ADAudit Plus,这是一款用于实时监控 Active Directory、Windows 文件服务器和 Windows 配置更改审计的产品。
ADAudit 可以使用 Windows 中提供的标准远程交互工具远程访问事件日志和其他统计信息。但是,为了在目标机器上实现全面的可视性和专门的功能,必须在受审计的 Windows 系统上安装代理,特别是 ADAuditPlusAgent。
当这项研究开始时,目标实际上是找到一种利用旧 CVE 的新方法,然而深入研究产品后出现了一个新的兔子洞,从而导致了一个新的漏洞。
由于旧的 CVE 已修复,因此研究必须在旧版本的 ADAudit 上进行,更准确地说是2021 年 12 月发布的 7050 版本
ManageEngine 倾向于使用 Java 在 Tomcat 上构建应用程序,这使得它成为练习源代码分析和逆向工程的良好目标。让我们开始挖掘吧。
方法
对于那些不熟悉 Java 和反编译的人来说,这里是 Shelltrail 的方法:
找到您要评估的所有 .jar 文件并将其下载到您的 Unix 主机。jar 文件很可能存储在正在评估的产品的安装目录中。
user@adpen1:~/adaudit/7$ ls -la
total 8036
drwxr-xr-x 2 user user 4096 Dec 1 08:36 .
drwxr-xr-x 5 user user 4096 Dec 1 08:35 ..
-rw-r--r-- 1 user user 997934 Dec 1 08:36 AdventNetADAPClient.jar
-rw-r--r-- 1 user user 12040 Dec 1 08:36 AdventnetADAPFilter.jar
-rw-r--r-- 1 user user 139792 Dec 1 08:36 AdventNetADAPJspClient.jar
-rw-r--r-- 1 user user 4534195 Dec 1 08:36 AdventnetADAPServer.jar
-rw-r--r-- 1 user user 15735 Dec 1 08:36 AdventnetADAPService.jar
-rw-r--r-- 1 user user 68066 Dec 1 08:36 AdventnetADAPStartUp.jar
-rw-r--r-- 1 user user 686147 Dec 1 08:36 AdventNetClientComponents.jar
-rw-r--r-- 1 user user 290027 Dec 1 08:36 AdventNetClientFramework.jar
-rw-r--r-- 1 user user 65094 Dec 1 08:36 AdventNetIdiomsGallery.jar
-rw-r--r-- 1 user user 294662 Dec 1 08:36 AdventNetNPrevalent.jar
-rw-r--r-- 1 user user 104252 Dec 1 08:36 AdventNetRssLibrary.jar
-rw-r--r-- 1 user user 157667 Dec 1 08:36 AdventNetTableComponents.jar
-rw-r--r-- 1 user user 781984 Dec 1 08:36 AdventNetUpdateManagerInstaller.jar
-rw-r--r-- 1 user user 22935 Dec 1 08:36 AdventNetWebClientCore.jar
-rw-r--r-- 1 user user 13413 Dec 1 08:36 AdventNetWebClientRangeNavigator.jar
-rw-r--r-- 1 user user 13809 Dec 1 08:36 AdventNetWebClientTree.jar
Jar 文件是 Java Archive 的缩写。它是一种基于流行的 ZIP 文件格式的文件格式,用于将多个文件归档为一个文件。
是的,可以使用 zip 工具提取 jar 文件并检索内容:
user@adpen1:~/adaudit/7$ 7z l AdventNetADAPClient.jar
[...]
2021-12-30 21:37:22 D.... 0 0 com/adventnet/sym/adsm/common/webclient/tracker
2021-12-30 21:37:22 D.... 0 0 com/adventnet/sym/adsm/common/webclient/tree
2021-12-30 21:37:22 D.... 0 0 com/adventnet/sym/adsm/common/webclient/util
2021-12-30 21:37:22 ..... 3836 1804 com/adventnet/sym/adsm/auditing/webclient/compliance/ComplianceDEReportHandler.class
2021-12-30 21:37:22 ..... 43842 17323 com/adventnet/sym/adsm/auditing/webclient/compliance/ComplianceReportHandler.class
2021-12-30 21:37:22 ..... 1857 764 com/adventnet/sym/adsm/auditing/webclient/ember/api/ADAPAPIServlet.class
2021-12-30 21:37:22 ..... 1884 774 com/adventnet/sym/adsm/auditing/webclient/ember/api/ADAPAgentAPIServlet.class
[...]
这将为您提供存储在 Jar 中的类文件,但是类文件是编译成 Java 字节码的 Java 代码,由 Java 虚拟机 (JVM) 解释,因此无法被人类阅读。
jd-gui是一个很好的工具,可以将单个 Jar 文件反编译为人类可读的格式以供分析,但是当您有许多文件时,它的扩展性不佳。
为了自动化此过程, 可以使用jd-cli来解压和反编译 Jar 文件 - 使其可搜索和查看。
user@adpen1:~/adaudit/7$ jd-cli *.jar
09:51:11.097 INFO com.github.kwart.jd.cli.Main - Decompiling AdventNetADAPClient.jar
09:51:11.107 INFO com.github.kwart.jd.output.ZipOutput - ZIP file output will be initialized - AdventNetADAPClient.src.jar
09:51:14.930 INFO com.github.kwart.jd.output.ZipOutput - Finished with 129 class file(s) and 1 resource file(s) written.
[...]
user@adpen1:~/adaudit/7$ mkdir src
user@adpen1:~/adaudit/7$ cd src
user@adpen1:~/adaudit/7/src$ find ../ -name '*src.jar' -exec unzip -o {} ;
Archive: ../AdventNetUpdateManagerInstaller.src.jar
inflating: META-INF/MANIFEST.MF
inflating: com/adventnet/tools/update/installer/images/context_help.png
inflating: com/adventnet/tools/update/installer/images/context_install.png
inflating: com/adventnet/tools/update/installer/images/error.png
inflating: com/adventnet/tools/update/installer/images/help_icon.png
inflating: com/adventnet/tools/update/installer/images/import.png
inflating: com/adventnet/tools/update/installer/images/info.png
[...]
从这一点开始,就可以开始对源代码的分析了。
发现
将焦点从最初目标转移的原因是,人们开始对与ADAuditPlusAgent
安装了 的 Windows 计算机交互并获取信息的功能感兴趣。ADAuditPlusAgentADAudit 服务器有许多功能可以激活,其中之一就是所谓的 SessionMonitoring功能。此功能启动一个进程,以设定的时间间隔截取屏幕截图,并将其发送到中央服务器。这允许服务器创建配置状态更改的视频并将它们与事件联系起来。例如,代理计算机中的低权限用户可以获得管理员删除关键配置文件的视频会话。
听起来这是一个有趣的功能,不是吗?
当从 ADAudit 服务器启用该SessionMonitoring功能并使用 wireshark 分析发送到代理的所有流量时,发现通过命名管道进行 RPC 交互。
嗯,很有趣!让我们首先深入了解 DCE RPC 的历史。
DCE RPC/MSRPC
DCE RPC 或分布式计算环境远程过程调用是客户端与服务器交互中使用的协议,是计算机科学中的通用术语,而不仅仅是 Windows 系统上可用的术语。
然而,微软想要自己的风格,并在 1993 年首次发布的 Windows NT 中开发了 MSRPC(微软远程过程调用)。RPC 是一种古老的协议,但它是 Windows 环境中计算机间通信工作方式的基础。
因此,RPC 的工作原理基本上是定义一个接口定义语言 (IDL),向客户端公开程序。IDL 还定义了客户端应如何与 UUID、版本和可选句柄等参数进行交互。
[
uuid(7a98c250-6808-11cf-b73b-00aa00b677a7),
version(1.0),
implicit_handle(handle_t ImplicitHandle)
]
interface hello
{
void HelloProc([in, string] unsigned char * pszString);
void Shutdown(void);
}
第一部分称为 IDL 标头。此标头应包含 UUID(以免与其他 RPC 接口冲突)、版本号,并且可以使用三种不同的句柄:显式、隐式和自动。
第二部分,也称为 IDL 主体,说明了客户端可以与之交互的过程。
连接到服务器时,必须初始化与 RPC 接口的绑定。这可以是ncacn_ip_tcp,ncalrpc或ncacn_np
ncacn_ip_tcp使用纯 TCP 通信与服务器交互。要使此绑定起作用,必须为 RPC 服务器配置一个可访问的静态 TCP 端口。这不需要身份验证即可建立与 RPC 的连接,将所有身份验证责任留给服务器上公开的过程。与 RPC 的初始连接是通过 TCP/135 完成的。
例子:ncacn_ip_tcp:100.64.5.212[49670]
ncalrpc用于不应通过网络暴露的本地 RPC 交互。
例子:ncalrpc:[LRPC-dfdb2238aff756a07c]
ncacn_np让客户端和服务器协商 TCP 端口以进行交互,协商通过 SMB 进行。这意味着,如果未启用匿名会话,则这种类型的绑定需要用户名和密码才能连接到 SMB。
例子:ncacn_np:100.64.5.212[pipehello]
开发
到目前为止,我们所知道的实际上只是 ADAudit 服务器正在使用 RPC 通过命名管道与代理进行交互,ADAPAgentRpcPipe如 wirehark 屏幕截图中所示。
我们现在需要做的是:
-
创建有效的 IDL 结构
-
煮一大罐咖啡
-
构建 RPC 客户端
-
猜了很多
-
创建输入数据,这些数据将被编组为服务器 RPC 接口将对其执行操作的有效存根。
让我们从 IDL 结构开始。RpcView拥有完成此步骤所需的一切,因为该工具可以从 RPC 接口反编译 IDL 结构。此工具应在公开 RPC 接口的服务器上执行:
从https://visualstudio.microsoft.com/downloads/获取 Visual Studio 2022 安装程序
选择安装使用 C++ 的桌面开发
确保选择了 Windows SDK,因为此包包含制作 RPC 客户端所需的组件:
启动 Visual Studio 2022 并启动 C++ 控制台应用程序项目。
首先将项目构建选项更改为Release,x64以便在构建机器之外运行已编译的二进制文件时不需要调试运行时库。
在 Visual Studio 中的源文件ADAPAgentRpcPipe.idl结构中创建一个名为的文件:
将RpcView导出的反编译的IDL结构复制到新创建的文件中。
[
uuid(7a98c250-6808-11cf-b73b-00aa00b677a7),
version(1.0),
]
interface DefaultIfName
{
void Proc0(
[in]long arg_0,
[in][out]long* arg_1);
void Proc1(
[in]long arg_0,
[in][string] wchar_t* arg_1,
[in][out]long* arg_2);
void Proc2(
[in][out]long* arg_0,
[in][out]long* arg_1);
}
在 Visual Studio 中右键单击该文件并选择编译- Visual Studio 将使用midl.exeIDL 文件编译为 C 代码,以处理适当的编组操作。
编组是将数据对象和参数转换为可通过网络传输的状态的过程。这在传输复杂数据结构时至关重要。
实际上,在将命令推送到 ManageEngine ADAudit Agent 时发送到 RPC 服务器的数据可以在部分中的 DCERPC 请求中查看Stub data。此可见性的一个例外是,如果服务器默认为 SMB3,则该Stub data部分会被加密。在下图中,服务器正在使用 SMB2:
回到项目;编译 IDL 结构后,Visual Studio 项目文件夹中应该有三个新文件:
-
ADAPAgentRpcPipe_c.c: _c 代表客户端
-
ADAPAgentRpcPipe_h.h: _h 为标题
-
ADAPAgentRpcPipe_s.c: _s 代表服务器
- 创建有效的 IDL 结构。完成。
接下来是coffee:
IDL 结构完成并且咖啡准备就绪后,下一步就是构建 RPC 客户端。Microsoft 提供了示例代码,指定如何构建 RPC 客户端和服务器应用程序。公共代码将用作我们的 ADAuditRPC 客户端的骨架,因此将其复制到 Visual Studio 中名为“源文件”adauditrpc-client.cpp的 文件中(使用任何现有的主 cpp 文件或创建一个新文件)
示例代码:
/* file: helloc.c */
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "hello.h"
#include <windows.h>
void main()
{
RPC_STATUS status;
unsigned char * pszUuid = NULL;
unsigned char * pszProtocolSequence = "ncacn_np";
unsigned char * pszNetworkAddress = NULL;
unsigned char * pszEndpoint = "\pipe\hello";
unsigned char * pszOptions = NULL;
unsigned char * pszStringBinding = NULL;
unsigned char * pszString = "hello, world";
unsigned long ulCode;
status = RpcStringBindingCompose(pszUuid,
pszProtocolSequence,
pszNetworkAddress,
pszEndpoint,
pszOptions,
&pszStringBinding);
if (status) exit(status);
status = RpcBindingFromStringBinding(pszStringBinding, &hello_ClientIfHandle);
if (status) exit(status);
RpcTryExcept
{
HelloProc(pszString);
Shutdown();
}
RpcExcept(1)
{
ulCode = RpcExceptionCode();
printf("Runtime reported exception 0x%lx = %ldn", ulCode, ulCode);
}
RpcEndExcept
status = RpcStringFree(&pszStringBinding);
if (status) exit(status);
status = RpcBindingFree(&hello_IfHandle);
if (status) exit(status);
exit(0);
}
/******************************************************/
/* MIDL allocate and free */
/******************************************************/
void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
return(malloc(len));
}
void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
free(ptr);
}
我们需要做的第一个修改是将第 1 行和第 5 行的文件名替换#include "hello.h"为, #include "ADAPAgentRpcPipe_h.h"因为我们已经编译了通过 IDL 文件创建的自己的头文件。此外,我们添加了一行以便#pragma comment(lib, rpcrt4.lib")能够创建 RPC 绑定。
结果:
- /* file: helloc.c */
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
- #include "hello.h"
#include <windows.h>
+ /* file: adauditrpc-client.cpp */
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
+ #include "ADAPAgentRpcPipe_h.h"
#include <windows.h>
+ #pragma comment(lib, "rpcrt4.lib")
下一步是替换一些数据类型并初始化用于 RPC 绑定的变量。这些可以在第 12-19 行找到。
RPC_STATUS status;
- unsigned char * pszUuid = NULL;
- unsigned char * pszProtocolSequence = "ncacn_np";
- unsigned char * pszNetworkAddress = NULL;
- unsigned char * pszEndpoint = "\pipe\hello";
- unsigned char * pszOptions = NULL;
- unsigned char * pszStringBinding = NULL;
- unsigned char * pszString = "hello, world";
unsigned long ulCode;
RPC_STATUS status;
+ RPC_WSTR pszUuid = NULL;
+ RPC_WSTR pszProtocolSequence = (RPC_WSTR)L"ncacn_np";
+ RPC_WSTR pszNetworkAddress = (RPC_WSTR)L"100.64.5.212"; // Target ADAudit agent
+ RPC_WSTR pszEndpoint = (RPC_WSTR)L"\pipe\ADAPAgentRpcPipe";
+ RPC_WSTR pszOptions = NULL;
+ RPC_WSTR pszStringBinding = NULL;
+ //unsigned char * pszString = "hello, world";
unsigned long ulCode;
在第 29 行我们替换:
- status = RpcBindingFromStringBinding(pszStringBinding, &hello_ClientIfHandle);
+ status = RpcBindingFromStringBinding(pszStringBinding, &DefaultIfName_v1_0_c_ifspec);
第 49 行:
- status = RpcBindingFree(&hello_IfHandle);
+ status = RpcBindingFree(&DefaultIfName_v1_0_c_ifspec);
该&DefaultIfName_v1_0_c_ifspec参数在我们的 IDL 编译的头文件中定义,就像我们命名我们的 RPC 接口一样DefaultIfName (ADAPAgentRpcPipe.idl 中的第 5 行)。
HelloProc()
和Shutdown()。我们将继续用替换它们Proc0并提供有效参数来测试我们的第一个 ADAuditAgentRpcPipe 程序。arg_0数据类型long为整型,arg_1数据类型long*为指向长整型的指针。arg_1定义的[in][out]含义是,这可以从应用程序中输出。
由于我们的 IDL 标头结构缺少隐式句柄,因此我们需要为 提供句柄(&DefaultIfName_v1_0_c_ifspec
)Proc0
,我们还将实例化arg_0和 的变量arg_1:
- RpcTryExcept
- {
- HelloProc(pszString);
- Shutdown();
- }
+ long arg_0 = 0;
+ long arg1_pointer;
+ long* arg_1 = &arg1_pointer;
+ RpcTryExcept
+ {
+ Proc0(DefaultIfName_v1_0_c_ifspec, arg_0, arg_1);
+ }
为了让 Visual Studio 引用DefaultIfName_v1_0_c_ifspec,我们需要将该 ADAPAgentRpcPipe_c.c文件包含在我们的源文件列表中:
还添加Header FilesADAPAgentRpcPipe_h.h下的文件。
Visual Studio 中的最终项目结构现在应如下所示:
如果每一步都正确执行,项目现在应该可以构建了。瞧…
但它真的有效吗?
从命令行运行二进制文件不会打印任何输出,除非抛出异常,但如果使用 Wireshark 分析网络流量,我们可以看到与 ADAudit Web 应用程序触发 RPC 交互时的流量类似的流量。
天哪,它似乎有效!
概括
在本部分研究中,我们成功创建了一个可运行的 RPC 客户端,用于与 ADAudit 代理进行交互。但我们仍然不知道Proc0或 Proc1的Proc2作用。在第 2 部分中,我们将深入研究代理软件的逆向工程,以期找出我们新创建的客户端实际上可以做什么。
关注我们的 LinkedIn 页面来支持我们,并在发布新研究时收到通知:https://www.linkedin.com/company/shelltrail
点击此 URL 阅读第 2 部分:https://www.shelltrail.com/research/manageengine-adaudit-reverse-engineering-windows-rpc-to-find-cve-2024-36036-and-cve-2024-36037-part2/
ManageEngine ADAudit - Reverse engineering Windows RPC to find CVEs - part
1
/ RPC
https:
//www
.shelltrail.com/research/manageengine-adaudit-
reverse
-engineering-windows-rpc-to-find-cve-
2024
-
36036
-
and
-cve-
2024
-
36037
-part1/
原文始发于微信公众号(Ots安全):反转 Windows RPC 以查找 CVE(CVE-2024-36036、CVE-2024-36037)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论