看了很多⽹上关于weblogic t3协议解析,基本没⼈好好分析。 先说⼀下为什么要分析T3协议,主要是受朋友所托使⽤python模拟调⽤T3协议。⽬前的weblogic T3攻击⼯具,⼤体都是java或者python等编写,有两⼤特点:
1. java 编写的攻击⼯具⼀般集成weblogic的t3.jar,攻击者通过反序列化漏洞造成的任意代码执⾏向weblogic安装⼀个T3实例,攻击者调 ⽤这个实例去完成回显等复杂操作。
2. python等语⾔编写的⼯具为了实现weblogic反序列化攻击,⼀般直接替换weblogic t3流中aced 0005部分实现反序列化攻击。这种⽅式
的缺点在于⽆法完成T3协议的交互,导致⽆法回显等弊端。
⽽⽹上关于weblogic t3协议根本搜索不到任何相关信息,因为T3协议是oracle独有的,⾮开源协议。如果要是分析T3协议,只能对着weblogic 的源码,静态分析加动态调试。只知道T3也称为丰富套接字,是BEA内部协议,功能丰富,可扩展性好。T3是多⼯双向和异步协议,经过⾼度优化,只使⽤⼀个套接字 和⼀条线程。借助这种⽅法,基于Java的客户端可以根据服务器⽅需求使⽤多种RMI对象,但仍使⽤⼀个套接字和⼀条线程。这也为我们静态分析t3协议带来了很多⿇烦
T3的交互过程如下
协议协商
客户端⾸先发送下⾯的信息给weblogic服务器
t3 10.3.6
AS:255
HL:19
表明这是⼀个T3协议,⽽服务器接收到信息后,也会回复类似的消息。
HELO:12.2.1.4.false
AS:2048
HL:19
MS:10000000
PN:DOMAIN
代码分析有点复杂,这⾥不再赘述,只讲重点。
weblogic.rjvm.t3.MuxableSocketT3#connect(java.InetAddress, int, int)
服务端则在weblogic.rjvm.t3.MuxableSocketT3#readIncomingConnectionBootstrapMessage处理T3的启动。当然,每个头的含义可以在weblogic.rjvm.MsgAbbrevJVMConnection到详细的定义。
⼀般⽽⾔,AS与HL头⽐较常见,下⾯重点说⼀下这两个头pgm_430mei
1. HL头,标识⾃⼰后⾯发起的t3的协议头长度。
2. AS头,因为T3的反序列化使⽤了⼀个特别的数据结构Stack,AS头⽤来标识这个stack的容量,这个与T3协议反序列化分隔符
FFFE0001相关。现在我们先不理会这个头的具体含义
通信双⽅根据对⽅发来的协议协商信息,开始建⽴连接
T3协议分析
再协商完毕上⾯的信息后,由客户端向服务端发送⾃⼰的详细信息,这个叫peerinfo。从这⾥开始就全部是⼆进制流了,变得不可读。我们需要开始分析T3协议的每个bytes
每个T3协议前4位bytes标识本次请求的数据长度,这个没什么好说的
在weblogic.rjvm.MsgAbbrevJVMConnection#dispatch处,解读T3协议流,并处理
T3协议头处理
我们⾸先看⼀下协议头是怎么被处理的
相关代码在weblogic.rjvm.MsgAbbrevInputStream#init中。⾸先调⽤readHeader
motolora
这些内容加在⼀起正好是19字节,也就是上⼀阶段协商中HL的内容。每个字段的含义如下,因为我也是通过逆向分析得到的结果,有些可能不太准确。
cmd
表明本次请求的类型。请求类型⼀共有⼗余种,值分别如下
其中,T3交互peerInfo初始化,类型为CMD_IDENTIFY_REQUEST,执⾏rebind lookup等操作,cmd类型为CMD_REQUEST。t3也⽀持c#调⽤
flags
标志位
responseId
这个头的作⽤为标识每条流的请求顺序,是⾃增的,初始值为-1。⼀般⽽⾔,服务端的responseId设置与客户端在本次请求的responseId值相同,这个字段有点类似于tcp 的syn。相关代码如图
invokeableId
钋这个字段⽐较重要。客户端将根据这个字段的值去查响应的处理程序。t3与java rmi不同之处在于,java rmi协议在客户端的lookup查对象中返回该对象的动态代理。t3返回⼀个数字代表该对象。后续操作中,设置invokeableId为该对象代表的数字,完成rpc调⽤。
如果客户端执⾏的是rebind,交换彼此信息,lookup操作,则 invokeableId为9。不要问我为什么,wlc就这样设计的。具体有哪些invokeableid,可以在OIDManager中查看。
当然,如果是响应,这种情况下invokeableId将不再重要。weblogic⼀般设置为与responseid相同。
abbrevOffset
abbrev这个数据结构在本次请求中相对于开始部分的偏移
T3 Abbrev 处理
读取完头信息后,开始读取MsgAbbrevs。这个数据结构我不能很好的描述它,因为T3协议并没有全部实现java反序列化协议,⽽是⾃⼰由魔改了⼀部分。⽐如readClassDescriptor的class部分,T3协议在abbrevs中读取。这个后⾯将会讲到,现在你不理解也⽆所谓
这⾥将会跳转到abbrevOffset标识的部分并开始读取数据。代码如下
void read(MsgAbbrevInputStream in, BubblingAbbrever at) throws IOException, ClassNotFoundException {
int numAbbrevs = in.readLength();
for(int i = 0; i < numAbbrevs; ++i) {
int abbrev = in.readLength();
Object o;
if (abbrev > at.getCapacity()) {
o = adObject(in);
this.abbrevs.push(o);
} else {
o = at.getValue(abbrev);
this.abbrevs.push(o);
}
玻璃镀膜技术
}
}
⾸先读取msgAbbrev的数量。然后再读取length,如果length⼤于本次T3请求中存放abbrev的容量,则读取对象,否则读取值。⽽本次T3请求的abbrev的容量,就是由前⾯协议协商的AS标识的值,默认为255。
下⾯我们看⼀下简化后的readLength代码。这⾥我⽤python重写⼀遍,反编译的代码可读性很差,相关代码在
weblogic.utils.io.ChunkedDataInputStream#readLength
⽽客户端中,如果需要写⼊对象,则直接写⼊256,也就是FE01。
服务端调⽤readObject的代码如下
⽽⼀般来说,在协商T3协议部分中,交换的信息有限,所以这部分将会weblogic.rjvm.JVMID weblogic.rjvm.ClassTableEntry
weblogic.rjvm.ImmutableServiceContext组成。
这⾥很好地解释了⽹上流传的内容
T3 Context处理
读取完上⾯的内容后,开始读取context
这部分主要内容是在例如rebind,lookup请求中,恢复请求上下⽂。T3协议是不分rebind等请求的,在最终处理阶段由调⽤⽅法,也就是context去区分。
T3 协议内容处理
T3 协议在建⽴之初交换彼此的详细信息,这部分被称为PeerInfo。t3协议内容没有具体定义,由被调⽤的⽅法参数类型决定
CMD_IDENTIFY_REQUEST
这个就是交换信息时的cmd头。也就是1,响应则为2。
具体处理代码在
weblogic.rjvm.ConnectionManagerServer#handleIdentifyRequest
协议体的具体内容如下邢质斌广告
从这⾥可以看出,其实在每条T3流中,ACED标识前⾯也是反序列化流,只不过修改起来⽐较复杂罢了。
CMD_REQUEST
客户端在执⾏rebind,lookup操作以及调⽤实例等操作,cmd都为CMD_REQUEST。服务端只能通过invokeadbleId去区分客户端的具体操作。具体代码在weblogic.rjvm.RJVMImpl#dispatchRequest中
rid可⽤的列表如下
最终将请求包装为线程调⽤
rmi指令
这种指令⽤来绑定,查实例,这种操作的invokeableId为9。不要问我为什么,weblogic 就只这样设计的
客户端的代码
服务端最终调⽤weblogic.jndi.internal.RootNamingNode_WLSkel#invoke(int, i.spi.InboundRequest,
去处理每个具体的请求。在这⾥每⼀个case都对应rmi的⼀种操作。例如lookup对应16
这⾥将会读取t3协议体内容,处理完成后返回。
lookup操作,最终返回这个实例所代表的rid
实例操作
回到rid处理部分,在这⾥查到rid所代表的实例处理后,分发并处理。例如我现在通过weblogic的反序列化漏洞安装⼀个实例后,通过lookup查,该实例的rid为295。处理程序如下
CMD_ONE_WAY
这种请求与上⾯类似,只不过不需要向客户端显⽰执⾏后的结果。
T3内存马的构想
项目管理职业资格认证
前⾯我们说过,⽬前T3协议的反序列化攻击回显⼤多数都是通过反序列化漏洞绑定⼀个实例。例如我的攻击程序代码如下
绑定完成后,客户端再通过lookup查该实例去调⽤,实现T3的后门。但是这种攻击⽅式有⼀个弊端在于很容易被发现,管理员可以通过查看weblogic的jndi树去判断是否被植⼊基于t3协议的后门。
当然,分析⼀遍T3协议的处理⽅式,有助于我们发现新的乐趣
T3的CMD_REQUEST请求,包装为线程后,最终由i.internal.BasicServerRef#handleRequest去处理
代码如上所述,这⾥存在⼀个preInvoke,在权限校验之前。这个东西类似于java web的Filter功能。
在这⾥我们只需要向commonInterceptors插⼊⼀个⾃定义的,即可实现t3版本的内存马。
这种⽅法的缺点:需要实现⼀个T3协议,不过好在了解协议后,重写⼀个就⼗分⽅便了。
后⾯将分享⼀下python模拟调⽤T3的相关代码