Java反序列化学习之CommonsCollections3

前言

在分析 yesoserial CommonsCollections3 在构造Runtime类时候并未调用transform方法中的反射类,而是使用javassist创建类并执行Runtime.exec(‘evil’);

环境依旧是jdk7;commons-collections3.1

javassist知识

pom.xml(Maven下载javassist)

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.19.0-GA</version>
</dependency>

Test.java

package com.bqt.test;
public class test {
    public void hello(String s) {
        System.out.println(s);
    }

}

Persion.java

其中需要关注的是调用makeClassInitializer().insertBefore()能够创建staic代码块,JVM加载类时会执行这些静态的代码块

import com.bqt.test.test;
import javassist.*;

import java.io.IOException;



public class Person {
    public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.bqt.test.test");
        //修改hello方法代码块
        CtMethod cm = cc.getDeclaredMethod("hello", new CtClass[] { pool.get("java.lang.String") });
        cm.setBody("{" + "System.out.println(\"你好:\" + $1);System.out.println(\"你好:\" + \"fuck\");" + "}");
        //创建static代码块
        String staticSrting = "System.out.println(\"evil run\");";
        cc.makeClassInitializer().insertBefore(staticSrting);


        cc.writeFile("d:/test");//保存到指定目录
        cc.toClass(); //加载修改后的类,注意:必须保证调用前此类未加载
        new test().hello("你大爷");
    }
}

Test.class反编译结果

image-20200217193908064

Ysoserial CommonsCollections3

CommonsCollections3 后半段构造与CommonsCollections1构造相同,这里主要分析框出的两处不同点。

InvokerTransformer类替换为InstantiateTransformer类,传入的object类为自建的templatesImpl。

new ConstantTransformer(TrAXFilter.class)会返回一个TrAXFilter.class对象

image-20200217194014339

跟进下CreateTemplatesImpl类具体构造

  1. java.lang.Runtime.getRuntime().exec载入static代码块

  2. 设置absTranslet(org.apache.xalan.xsltc.runtime.AbstractTranslet)为StubTransletPayload父类

  3. 调用自建·Reflections.setFieldValue·设置_bytecodes,_name,_tfactory保证反序列化正常执行,并将创建的StubTransletPayload类加载进templates

image-20200217203457739

反序列化 debug

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class exp {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("/Users/osword/Desktop/c3.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        //恢复对象
        ois.readObject();
        ois.close();
    }
}

执行tranfrom方法这里con为TrAXFilter类,iArgs参数为javassist字节

image-20200217203804394

newInstance执行后会执行TrAXFilter类构造方法,相当于PHP中__construct方法

image-20200217204108629

defineTransletClasses该方法可以看作对StubTransletPayload类(ysoserial)构造定义

image-20200217231152937

跟进defineTransletClasses代码

_class[i] = loader.defineClass(_bytecodes[i]);通过对_class层叠加载父类和接口类.

image-20200217232232317

但是defineTransletClasses方法并未实例化传入Javassist构造的类(ysoserial定义的StubTransletPayload类).

最后通过AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();就能成功加载Javassist构造的类.

并且Runtime.getRuntime.exec执行在static代码块中,在类加载后就会优先执行.

image-20200217232828408

总结一下javassist利用过程

TrAXFilter.TrAXFilter();
_transformer = (TransformerImpl) templates.newTransformer();
TemplatesImpl.newTransformer(); 
TemplatesImpl.getTransletInstance();
		defineTransletClasses(); //配置javassist构造的类
			loader.defineClass(_bytecodes[i]);
          /*ysoserial工具
          Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
            classBytes, ClassFiles.classAsBytes(Foo.class)
           });*/
(AbstractTranslet) _class[_transletIndex].newInstance();
 //ysoserial工具 final CtClass clazz = pool.get(StubTransletPayload.class.getName())
TemplatesImpl.loadClass();//加载后优先执行staic代码块中的Runtime.getRuntime.exec()

总结

两种类构造方法:

​ 1.反射类 2.javassist
分析时候可以先debug下ysoserial工具是怎么构造EXP搞清楚构造过程,在去分析反序列化就较容易了。

参考链接

https://www.anquanke.com/post/id/190461

https://baiqiantao.github.io/Java/aop/R77vuq/

https://github.com/jboss-javassist/javassist/wiki/Tutorial-1

https://blog.csdn.net/hao707822882/article/details/38666217