Java动态加载字节码TemplatesImpl和BCEL

前言

Java字节码存储在.class文件在JVM虚拟机运行

TemplatesImpl加载字节码

Java文件经历ClassLoader#loadClass=>ClassLoader#findClass=>ClassLoader#defineClass

defineClass这一步用来处理前面传入的字节码,并处理成Java类

可用来直接加载字节码

defineClass加载字节码

image-20240121224237477

先准备一个.class文件

写一个DefineClass.java文件

1
2
3
4
5
6
7
8
9
10
11
public class DefineClass {

public DefineClass() {
System.out.println("New instance construct");
}

public void test(){
System.out.println("test123");
}

}

编译.java文件 生成DefineClass.class

1
javac DefineClass.java

将DefineClass.class文件进行base64加密

1
yv66vgAAADQAHgoABwAPCQAQABEIABIKABMAFAgAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAR0ZXN0AQAKU291cmNlRmlsZQEAEERlZmluZUNsYXNzLmphdmEMAAgACQcAGAwAGQAaAQAWTmV3IGluc3RhbmNlIGNvbnN0cnVjdAcAGwwAHAAdAQAHdGVzdDEyMwEAC0RlZmluZUNsYXNzAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAYABwAAAAAAAgABAAgACQABAAoAAAAtAAIAAQAAAA0qtwABsgACEgO2AASxAAAAAQALAAAADgADAAAAAwAEAAQADAAFAAEADAAJAAEACgAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEACwAAAAoAAgAAAAgACAAJAAEADQAAAAIADg==

直接使用defineClass加载字节码

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestDefineClass {
public static void main(String[] args) throws Exception {
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClass.setAccessible(true);
byte[] code = org.apache.commons.codec.binary.Base64.decodeBase64("yv66vgAAADQAHgoABwAPCQAQABEIABIKABMAFAgAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAR0ZXN0AQAKU291cmNlRmlsZQEAEERlZmluZUNsYXNzLmphdmEMAAgACQcAGAwAGQAaAQAWTmV3IGluc3RhbmNlIGNvbnN0cnVjdAcAGwwAHAAdAQAHdGVzdDEyMwEAC0RlZmluZUNsYXNzAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAYABwAAAAAAAgABAAgACQABAAoAAAAtAAIAAQAAAA0qtwABsgACEgO2AASxAAAAAQALAAAADgADAAAAAwAEAAQADAAFAAEADAAJAAEACgAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEACwAAAAoAAgAAAAgACAAJAAEADQAAAAIADg==");
System.out.println(ClassLoader.getSystemClassLoader());
Class hello = (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "DefineClass", code, 0, code.length);
Object o = hello.newInstance();
Method test = hello.getMethod("test");
test.invoke(o);

}
}

运行结果如下

image-20240121223814985

defineClass被调用类对象不会被初始化,需要调用构造函数才能执行代码

TemplatesImpl利用链

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl中的内部类TransletClassLoader重写了defineClass

image-20240121224817610

之前是protected修饰符现在是default 默认同包类可以访问

TemplatesImpl的利用链

TemplatesImpl#getOutputProperties=>TemplatesImpl#newTransformer=>TemplatesImpl#getTransletInstance=>emplatesImpl#defineTransletClasses=>TransletClassLoader#defineClass

首先getOutputProperties调用了newTransformer

image-20240123192559989

newTransormer调用getTransletInstance

image-20240123195936384

getTransletInstance调用defineTransletClasses

image-20240123200051209

最终defineTransletClasses调用defineClass

image-20240123200255005

POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.apache.xalan.xsltc.trax.TemplatesImpl;
import static ysoserial.payloads.util.Reflections.setFieldValue;

public class TemplatesImplClass {
public static void main(String[] args) throws Exception {
byte[] code = org.apache.commons.codec.binary.Base64.decodeBase64("yv66vgAAADIAMwoABwAiCQAjACQIACUKACYAJwoAJgAoBwApBwAqAQAJdHJhbnNmb3JtAQBQKExvcmcvYXBhY2hlL3hhbGFuL3hzbHRjL0RPTTtbTG9yZy9hcGFjaGUveG1sL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAHEx0ZXN0L2xvYWRlci9UZW1wbGF0ZXNJbXBsWDsBAANkb20BABxMb3JnL2FwYWNoZS94YWxhbi94c2x0Yy9ET007AQAVc2VyaWFsaXphdGlvbkhhbmRsZXJzAQAxW0xvcmcvYXBhY2hlL3htbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACsBAHMoTG9yZy9hcGFjaGUveGFsYW4veHNsdGMvRE9NO0xvcmcvYXBhY2hlL3htbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xvcmcvYXBhY2hlL3htbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAPZHRtQXhpc0l0ZXJhdG9yAQAkTG9yZy9hcGFjaGUveG1sL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAUc2VyaWFsaXphdGlvbkhhbmRsZXIBADBMb3JnL2FwYWNoZS94bWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAY8aW5pdD4BAAMoKVYBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQAKU291cmNlRmlsZQEAE1RlbXBsYXRlc0ltcGxYLmphdmEMABoAGwcALAwALQAuAQATdGVtcGxhdGVYIGNvbnN0cnVjdAcALwwAMAAxDAAwADIBABp0ZXN0L2xvYWRlci9UZW1wbGF0ZXNJbXBsWAEAL29yZy9hcGFjaGUveGFsYW4veHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAob3JnL2FwYWNoZS94YWxhbi94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABChJKVYAIQAGAAcAAAAAAAQAAQAIAAkAAgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAAA0ADAAAACAAAwAAAAEADQAOAAAAAAABAA8AEAABAAAAAQARABIAAgATAAAABAABABQAAQAIABUAAgAKAAAASQAAAAQAAAABsQAAAAIACwAAAAYAAQAAABIADAAAACoABAAAAAEADQAOAAAAAAABAA8AEAABAAAAAQAWABcAAgAAAAEAGAAZAAMAEwAAAAQAAQAUAAEAGgAbAAEACgAAAD8AAgABAAAADSq3AAGyAAISA7YABLEAAAACAAsAAAAOAAMAAAAUAAQAFQAMABYADAAAAAwAAQAAAA0ADQAOAAAACQAcAB0AAQAKAAAAOAACAAEAAAAKsgACEQKatgAFsQAAAAIACwAAAAoAAgAAABkACQAaAAwAAAAMAAEAAAAKAB4AHwAAAAEAIAAAAAIAIQ==");

TemplatesImpl tpi = new TemplatesImpl();
setFieldValue(tpi, "_bytecodes", new byte[][]{code});
setFieldValue(tpi, "_name", "TemplatesImpl");
setFieldValue(tpi, "_tfactory", new org.apache.xalan.xsltc.trax.TransformerFactoryImpl());

tpi.newTransformer();
}
}

.class文件必须继承自AbstractTranslet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.apache.xalan.xsltc.DOM;
import org.apache.xalan.xsltc.TransletException;
import org.apache.xalan.xsltc.runtime.AbstractTranslet;
import org.apache.xml.dtm.DTMAxisIterator;
import org.apache.xml.serializer.SerializationHandler;

public class TemplatesImplX extends AbstractTranslet {
@Override
public void transform(DOM dom, SerializationHandler[] serializationHandlers) throws TransletException {

}

@Override
public void transform(DOM dom, DTMAxisIterator dtmAxisIterator, SerializationHandler serializationHandler) throws TransletException {

}

public TemplatesImplX() {
System.out.println("templateX construct");
}

public static void main(String[] args) {
System.out.println(666);
}
}

image-20240125200549193

BCEL加载字节码

通过BCEL提供的两个类 Repository 和 Utility 来利用:

Repository 用于将一个Java Class 先转换成原生字节码,当然这里也可以直接使用javac命令来编译java文件生成字节码

Utility 用于将 原生的字节码转换成BCEL格式的字节码

1
2
3
4
5
6
7
8
9
10
11
12
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.Repository;

public class HelloBCEL {
public static void main(String []args) throws Exception {
JavaClass cls = Repository.lookupClass(evil.Hello.class);
String code = Utility.encode(cls.getBytes(), true);
System.out.println(code);
}
}

BCEL ClassLoader加载字节码

1
new ClassLoader().loadClass(code).newInstance();

__END__