问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
Apache ActiveMQ jolokia 远程代码执行漏洞分析(CVE-2022-41678)
漏洞分析
Apache ActiveMQ jolokia 远程代码执行漏洞利用与分析(CVE-2022-41678)
一、漏洞分析 ====== 漏洞点位于/api/jolokia接口,通过webapps/api/WEB-INF/web.xml可得知对应的Servlet是`org.jolokia.http.AgentServlet`  1.1 调用栈分析 --------- 跟进到该类看代码,在handle中调用了handleSecurely()  跟进handleSecurely方法,这里并没有传递`org.jolokia.jaasSubject`属性,所以执行了`pReqHandler.handleRequest(pReq, pResp);`  继续跟进handleRequest方法,这里创建了`ServletRequestHandler`对象返回给`json`来处理POST类型的HTTP请求    这里extractJsonRequest方法的确就是从POST请求的输入流中,读取输入数据,然后解析出JSON格式的数据,转换为JSON对象,然后利用JmxRequestFactory来创建一个JmxRequest对象,其中封装了对JMX管理功能的相关请求信息。然后调用executeRequest处理。 继续跟进executeRequest  这里调用了backendManager.handleRequest,继续跟进  调用callRequestDispatcher方法,传入JmxRequest请求对象,进行请求调度处理,继续跟进  通过一层层的调度和分发,最终将JMX请求交给了对应的`ExecHandler`来处理EXEC类型的请求。 ```java 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) { // 使用 OpenTypeConverter 处理复杂类型 params[i] = this.converters.getToOpenTypeConverter().convertToObject(types.paramOpenTypes[i], args.get(i)); } else { // 使用 ToObjectConverter 处理基本类型 params[i] = this.converters.getToObjectConverter().prepareValue(types.paramClasses[i], args.get(i)); } } // 5. 利用JMX API调用目标MBean对象的操作方法 return server.invoke( request.getObjectName(), types.operationName, params, types.paramClasses); } ``` 总结就是: 1. request.getObjectName() 获取了请求中指定的目标MBean的对象名(ObjectName) 2. types.operationName 获取了请求中指定的目标MBean中的方法名 3. params 包含了请求中传入的方法参数 4. types.paramClasses 包含了目标方法的参数类型 5. 然后利用JMX服务器的MBeanServerConnection对象的invoke方法,按照JMX的反射调用机制,动态调用了目标MBean中的指定方法 通过这种方式,请求可以构造一个任意已注册在JMX服务器中的MBean对象以及其方法,然后动态执行其中的方法 1.2 构造RCE --------- 根据补丁来看,修改了jolokia-access.xml文件。在 ActiveMQ 中,`jolokia-access.xml` 文件用于定义哪些 MBean 和操作是允许或拒绝的。 其中新增了对jdk.management.jfr:type=FlightRecorder的限制,拒绝了对该 MBean 的所有属性和操作的访问。 在Apache的官方通告中,也说明了RCE是通过FlightRecorder来实现的,并且告诉了步骤: 1. 调用newRecording 2. 调用setConfiguration,其中包含Webshell数据 3. 调用startRecording 4. 调用copyTo方法来写入WebShell  jdk.management.jfr.FlightRecorderMXBeanImpl类是JDK Flight Recorder管理接口的实现类 关于JDK Flight Recorder: - 它是JVM内置的事件记录和分析工具,可以用来收集JVM的内部运行时信息,分析JVM和Java应用程序的行为。 - 通过它收集到的数据,可以找到各种性能问题并优化Java应用程序。 关于jdk.management.jfr.FlightRecorderMXBeanImpl类: - 它实现了jdk.management.jfr.FlightRecorderMXBean接口。 - FlightRecorderMXBean接口定义了一系列管理Flight Recorder的方法。 - FlightRecorderMXBeanImpl实现了这些管理方法来控制Flight Recorder的行为。 - 这些方法包括:开始和停止recording,获取recording数据,设置recording的配置等。 - JConsole和其他管理工具可以通过这些MXBean接口来管理Flight Recorder。 接下来我们来跟进代码,看看这几个方法的实现 **newRecording()** 这里就是new一个Recording对象,返回ID,后续根据ID访问这个recording实例,并进行后续的配置、开始/停止记录等操作 ```java public long newRecording() { MBeanUtils.checkControl(); getRecorder(); // ensure notification listener is setup return AccessController.doPrivileged(new PrivilegedAction<Recording>() { @Override public Recording run() { return new Recording(); } }, null, new FlightRecorderPermission("accessFlightRecorder")).getId(); } ``` **setConfiguration()** 传入Recorder ID和配置文件,解析配置文件,如果符合格式,就会采用该配置文件 ```java 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); } } ``` 在[Oracle官方文档](https://docs.oracle.com/en/java/javase/17/jfapi/flight-recorder-configurations.html#GUID-51B499C9-CF15-48EE-9AAA-47BB69CBDABF)中有介绍默认模版在`<java_home>/lib/jfr/default.jfc`   构造包含WebShell的配置文件,特殊字符经过html实体编码  **startRecording** 传入Recording ID,然后开始录制 ```java public void startRecording(long id) { MBeanUtils.checkControl(); getExistingRecording(id).start(); } ``` **stopRecording**传入Recording ID,结束录制 ```java public boolean stopRecording(long id) { MBeanUtils.checkControl(); return getExistingRecording(id).stop(); } ``` **copyTo** 传入Recording ID和保存路径,这个路径没做限制,我们可以路径穿越到web目录去 ```java public void copyTo(long recording, String path) throws IOException { Objects.requireNonNull(path); MBeanUtils.checkControl(); getExistingRecording(recording).dump(Paths.get(path)); } ``` 二、利用完整思路 ======== 1. 构造恶意请求,传入目标MBean为FlightRecorderMXBean,调用FlightRecorderMXBean的newRecording方法创建一个新的Recording对象 2. 调用setConfiguration方法,设置Recording使用的配置文件,在配置文件中的键名中插入WebShell。由于导出的数据会包含键名,所以当我们在键名中插入WebShell时,导出的数据也会包含该WebShell 3. 调用startRecording开启事件记录 4. 调用stopRecording停止事件记录 5. 调用copyTo方法,将记录的数据保存到指定文件,这里我们可以指定为web应用目录下的一个jsp文件 6. 这样最终就实现了写入一个包含恶意命令执行代码的WebShell文件,之后访问这个WebShell,命令就会被执行 三、漏洞复现 ====== 从[stackoverflow](https://stackoverflow.com/questions/27707190/activemq-jolokia-api-how-can-i-get-the-full-message-body)找到了jolokia api的使用案例  **构造数据包:** 1、调用newRecording,获取ID为1 ```http POST /api/jolokia/ HTTP/1.1 Host: 172.23.80.196:8161 Origin:172.23.80.196:8161 Authorization: Basic YWRtaW46YWRtaW4= Content-Type: application/json { "type": "EXEC", "mbean": "jdk.management.jfr:type=FlightRecorder", "operation": "newRecording", "arguments": [] } ```  2、调用setConfiguration构造包含WebShell的配置文件,注意需要把xml数据的双引号转义并去除换行  3、开始录制数据  4、结束录制  5、调用copyTo,将文件写入webapps/admin/le1a.jsp  6、访问/admin/le1a.jsp,成功弹出计算器,漏洞验证成功 
发表于 2024-01-16 10:19:19
阅读 ( 30586 )
分类:
漏洞分析
1 推荐
收藏
0 条评论
请先
登录
后评论
jweny
16 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!