0x00 前置知识
1. classloader defineClass
该方法允许将符合class格式的bytes数组,作为类去加载。该方法返回一个Class对象。
一般情况下,通过defineClass加载的类,不允许同名,否则会报错,切记
2. java asm& javaassist
既然上面说了,defineClass不允许加载同名类。所以我们需要动态构建class,并生成bytes数组。在java中一般使用asm或者javaassist通过jvm字节码,直接生成一个class。两种各有优缺点。
- asm语法复杂,需要熟悉jvm底层知识。运行速度快
- javaassist 语法简单,运行速度慢
在这里推荐一个asm插件,可以将class一键转换为asm的java代码,并输出bytes。然后再按需修改class的相关参数
3. java反序列化的transient参数
protected transient Class<? extends Remotable> m_clz;
protected transient MethodHandle m_mhCtor;
transient参数修饰的变量,是不参与反序列化的。最终变量值为null
0x01 cve-2020-14644 漏洞分析
简单来讲,该漏洞在反序列化时通过defineClass直接加载类。下面是待加载的类
下面我们从RemoteConstructor
的newInstance开始分析,断点设置以及调用堆栈如下
newInstance的代码如下
public T newInstance() {
RemotableSupport support = RemotableSupport.get(this.getClassLoader());
return support.realize(this);
}
RemotableSupport可以认为是coherence自定义的Classloader。RemotableSupport中实现了defineClass。下面我们跟入RemotableSupport的realize方法,看一下realize方法都做了什么
public <T> T realize(RemoteConstructor<T> constructor) {
ClassDefinition definition = this.registerIfAbsent(constructor.getDefinition());
Class<? extends Remotable> clz = definition.getRemotableClass();
if (clz == null) {
synchronized(definition) {
clz = definition.getRemotableClass();
if (clz == null) {
definition.setRemotableClass(this.defineClass(definition));
}
}
}
Remotable<T> instance = (Remotable)definition.createInstance(constructor.getArguments());
instance.setRemoteConstructor(constructor);
return instance;
}
首先从RemoteConstructor中获取m_definition,类型为ClassDefinition。随后调用m_definition的getRemotableClass方法。这一句是关键。我们去看一下m_definition的getRemotableClass方法
可以很明显的看到getRemotableClass方法返回的m_clz,是通过transient修饰的。所以在反序列化后,这里一定返回null。
于是就会走到if条件的另外一个分支,调用this.defineClass(definition)去加载类。通过RemotableSupport去根据给定的参数,创建一个类。
RemotableSupport的defineClass方法就很简单了。从ClassDefinition中获取类名,类的bytes字节,调用系统的classloader去创建一个类
protected Class<? extends Remotable> defineClass(ClassDefinition definition) {
String sBinClassName = definition.getId().getName();
String sClassName = sBinClassName.replace('/', '.');
byte[] abClass = definition.getBytes();
definition.dumpClass(DUMP_REMOTABLE);
return this.defineClass(sClassName, abClass, 0, abClass.length);
}
所以payload代码如下
在这里我们只需要通过iiop协议,直接将RemoteConstructor对象发送给weblogic服务即可
0x02 回显利用
在cve-2020-2551 回显利用中,因为classloader的问题,我们需要上传一个jar包,并区分windows与linux操作系统才可以通过URLClassloader去正确加载上传的jar包。但是通过该漏洞,我们发现,可以直接写好类,并转换为bytes字节,通过反序列化发送给weblogic服务器即可。
至于回显,无非就是给服务器安装一个JNDI实例即可。所以我们需要加载的类的代码如下
运行结果如下
目前代码已上传至 GitHub - potats0/cve_2020_14644