JBoss Remoting Connector 4446端口反序列化分析

Qu3een 2022-09-30 09:21:00

今年4月份爆出JBoss EAP/AS <=6.版本的4446*端口存在反序列化rRCE漏洞,本文对该漏洞进行复现并做了以下分析。

0x01 漏洞分析

测试版本:jboss-6.1.0.Final
运行jboss-6.1.0,发现开启了很多端口,包括3873,4446等。在bindings-jboss-beans.xml文件中描述了4446端口为实现JBoss Remoting Connector的套接字。
1.png
既然是JBoss Remoting Connector。定位到jboss-remoting.jar中的org.jboss.remoting.transport.socket.ServerThread类。该线程类处理接收到的socket数据流。分析关键方法durun()
2.png
通过this.socketWrapper获取socket的输入流和输出流,然后进入processInvocation()处理。在获取输入流的过程中,如果随便传输一字符串会报错,定位到:
org.jboss.remoting.transport.socket.ClientSocketWrapper#createInputStream
3.png
通过org.jboss.remoting.marshal.PreferredStreamUnMarshaller#getMarshallingStream(java.io.InputStream)处理socket接收的数据流is。中间经java.io.ObjectInputStream#readStreamHeader()进行处理。判断是否满足s0 == STREAM_MAGIC && s1 == STREAM_VERSION。如下图所示:
4.png
其中,STREAM_MAGICSTREAM_VERSION恰好是熟悉的反序列化头aced0005
6.png
因此向该socket发送序列化对象继续进行测试。但进行到org.jboss.remoting.transport.socket.ServerThread#processInvocation又抛出异常。具体跟踪问题到:this.readVersion(inputStream);
7.png
如上图所示,通过this.readVersion(inputStream);读取到的version为-1,因此抛出异常。
通过inputStream.read();读取version,因此可控制socket输入流定义version。那么如何设置输入流使读取到的version不为-1呢?
跟踪inputStream.read(),经过java.io.ObjectInputStream.BlockDataInputStream#read() -> java.io.ObjectInputStream.BlockDataInputStream#refill()
8.png
初始unread值为 0,进入java.io.ObjectInputStream.BlockDataInputStream#readBlockHeader,这里需说明,在readBlockHeader方法中,需构造in.peek();取到的值为TC_BLOCKDATA,即0x77,
final static byte TC_BLOCKDATA = (byte)0x77;
然后返回hbuf[1],即0x77后面的值,这里设置为0x01。即通过readBlockHeader()方法后返回值为1。
9.png
然后将读取到的值n赋值给unread,此时unread为1,再进入in.read(buf, 0, Math.min(unread, MAX_BLOCK_SIZE));进行处理,即继续读取数量为unread的数据流自位置0开始放入buf中。
10.png
重新回到java.io.ObjectInputStream.BlockDataInputStream#read()中:
11.png
返回(buf[pos++] & 0xFF),此时buf经以上处理后为0x01后1位的数据,即0x02pos为0,即返回buf[0]&0xFF——2。
综上,设置0xaced0005770102数据流进入以上流程处理后,返回的version为2。
然后进入org.jboss.remoting.transport.socket.ServerThread#completeInvocation()方法。
12.png
通过org.jboss.remoting.transport.socket.ServerThread#versionedRead->org.jboss.invocation.unified.marshall.InvocationUnMarshaller#read(java.io.InputStream, java.util.Map, int) -> org.jboss.remoting.serialization.impl.java.JavaSerializationManager#receiveObject进入receiveObject()方法。
13.png
读取上面读取到的version值,如果为2,则进入receiveObjectVersion1_2处理。继续跟进,看到熟悉的objInputStream.readObject();
14.png
但是,还没有结束!
这里简单的序列化”test”字符串,拼接到上述分析的0xaced0005770102数据后,直接反序列化会报错:
15.png
16.png
继续分析报错原因:定位到java.io.ObjectInputStream#readObject0方法:
通过tc = bin.peekByte()得到的数据进行分支判断,直接序列化后的数据头为-84,无法匹配以下任意值。
17.png
而正常反序列化流程需进入readOrdinaryObject(unshared)
18.png
TC_OBJECT值为0x73,即去掉序列化头aced0005后剩余的部分。
19.png
至此,整个反序列化流程分析完成。

0x02 漏洞复现(CB链)

Common/lib下存在commons-beanutils.jar包,因此利用cb链进行反序列化利用。如下图所示,经序列化去掉序列化头-84,-19,0,5(aced0005)后一位即为TC_OBJECT标志位——115(0x73),因此只要去掉反序列化标志头[-84,-19,0,5],从115(0x73)开始拼接到上述分析得到的acde0005770102后,即可实现反序列化利用。
20.png
整个流程调用链如下:
21.png
成功利用CB链反序列化从而执行任意命令。
22.png

0x03 其他利用(JNDI利用)

除了利用CB链,JBoss中自己的org.jboss.ejb3.mdb.ProducerManagerImpl类——jboss-ejb3-core.jar,可反序列化实现JNDI利用。关键代码如下:
23.png
同时,经测试,JBoss开放的3873端口,即Ejb3 Remoting Connector,也可触发该反序列化漏洞。

0x04 总结

该漏洞虽然是反序列化漏洞,但中间复现过程可谓一波三折。在构造输入数据流时需要对代码进行认真调试和分析,才能更好理解数据是如何被处理以及最终成功实现反序列化。

评论

Qu3een

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

逆向安全 文章:70 篇
Android 文章:89 篇
数据安全 文章:29 篇
后门 文章:39 篇
iOS安全 文章:36 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Yukong

🐮皮

H

HHHeey

好的,谢谢师傅的解答

Article_kelp

a类中的变量secret_class_var = "secret"是在merge

H

HHHeey

secret_var = 1 def test(): pass

H

hgsmonkey

tql!!!

目录