.NET高級代碼審計(第一課)XmlSerializer反序列化漏洞


0X00 前言

在.NET 框架中的 XmlSerializer 類是一種很棒的工具,它是將高度結構化的 XML 數據映射為 .NET 對象。XmlSerializer類在程序中通過單個 API 調用來執行 XML 文檔和對象之間的轉換。轉換的映射規則在 .NET 類中通過元數據屬性來表示,如果程序開發人員使用Type類的靜態方法獲取外界數據,並調用Deserialize反序列化xml數據就會觸發反序列化漏洞攻擊(例如DotNetNuke 任意代碼執行漏洞 CVE-2017-9822),本文筆者從原理和代碼審計的視角做了相關腦圖介紹和復現。

圖片1.png

0X01 XmlSerializer序列化

.NET 框架中 System.Xml.Serialization 命名空間下的XmlSerializer類可以將 XML 文檔綁定到 .NET 類的實例,有一點需要注意它只能把對象的公共屬性和公共字段轉換為XML元素或屬性,並且由兩個方法組成:Serialize() 用於從對象實例生成 XML;Deserialize() 用於將 XML 文檔分析成對象圖,被序列化的數據可以是數據、字段、數組、以及XmlElement和XmlAttribute對象格式的內嵌XML。具體看下面demo

圖片2.png

XmlElement指定屬性要序列化為元素,XmlAttribute指定屬性要序列化為特性,XmlRoot特性指定類要序列化為根元素;通過特性類型的屬性、影響要生成的名稱、名稱空間和類型。再創建一個TestClass類的實例填充其屬性序列化為文件,XmlSerializer.Serialize方法重載可以接受Stream、TextWrite、XmlWrite類,最終生成的XML文件列出了TestClass元素、Classname特性和其它存儲為元素的屬性:

圖片3.png

0x02 XmlSerialize反序列化

反序列過程:將xml文件轉換為對象是通過創建一個新對象的方式調用XmlSerializer.Deserialize方法實現的,在序列化最關鍵的一環當屬new XmlSerializer構造方法里所傳的參數,這個參數來自System.Type類,通過這個類可以訪問關於任意數據類型的信息,指向任何給定類型的Type引用有以下三種方式。

2.1、typeof

實例化XmlSerializer傳入的typeof(TestClass) 表示獲取TestClass類的Type,typeof是C#中的運算符,所傳的參數只能是類型的名稱,而不能是實例化的對象,如下Demo

圖片4.png

通過typeof獲取到Type之后就能得到該類中所有的Methods、Members等信息。下圖運行Debug時,彈出消息對話框顯示當前成員Name的值。

圖片5.png

2.2、object.Type

在.NET里所有的類最終都派生自System.Object,在Object類中定義了許多公有和受保護的成員方法,這些方法可用於自己定義的所有其他類中,GetType方法就是其中的一個,該方法返回從System.Type派生的類的一個實例,因為可以提供對象成員所屬類的信息,包括基本類型、方法、屬性等,上述案例中實例化TestClass,再獲取當前實例的Type,如下Demo

圖片6.png

2.3、Type.GetType

第三種方法是Type類的靜態方法GetType,這個方法允許外界傳入字符串,這是重大利好,只需要傳入全限定名就可以調用該類中的方法、屬性等

圖片7.png

Type.GetType傳入的參數也是反序列化產生的漏洞污染點,接下來就是要去尋找可以被用來攻擊使用的類。

 

0X03 打造攻擊鏈

首先放上攻擊鏈打造成功后的完整Demo,這段Demo可以復用在任意地方(這里不涉及.NET Core、MVC),如下圖

圖片8.png

只要XmlSerializer存在反序列化漏洞就可用下面Demo中的內容,涉及到三個主要的技術點,以下分別來介紹原理。

 

3.1、ObjectDataProvider

ObjectDataProvider類,它位於System.Windows.Data命名空間下,可以調用任意被引用類中的方法,提供成員ObjectInstance用類似實例化類、成員MethodName

調用指定類型的方法的名稱、成員MethodParameters表示傳遞給方法的參數,參考下圖

圖片9.png

再給TestClass類定義一個ClassMethod方法,代碼實現調用System.Diagnostics.Process.Start啟動新的進程彈出計算器。如果用XmlSerializer直接序列化會拋出異常,因為在序列化過程中ObjectInstance這個成員類型未知,不過可以使用ExpandedWrapper擴展類在系統內部預先加載相關實體的查詢來避免異常錯誤,改寫Demo

圖片10.png

生成data.xml內容如下:

圖片11.png

攻擊鏈第一步就算完成,但美中不足的是因筆者在測試環境下新建的TestClass類存在漏洞,但在生產情況下是非常復雜的,需要尋求Web程序中存在脆弱的攻擊點,為了使攻擊成本降低肯定得調用系統類去達到命令執行,所以需要引入下面的知識。

 

3.2、ResourceDictionary

ResourceDictionary,也稱為資源字典通常出現在WPF或UWP應用程序中用來在多個程序集間共享靜態資源。既然是WPF程序,必然設計到前端UI設計語言XAML。 XAML全稱Extensible Application Markup Language (可擴展應用程序標記語言) 基於XML的,且XAML是以一個樹形結構作為整體,如果對XML了解的話,就能很快的掌握,例如看下面Demo

圖片12.png

  • 第一個標簽ResourceDictionary,xmlns:Runtime表示讀取System.Diagnostics命令空間的名稱起個別名為Runtime

  • 第二個標簽ObjectDataProvider指定了三個屬性,x:key便於條件檢索,意義不大但必須得定義;ObjectType 用來獲取或設置要創建其實例的對象的類型,並使用了XAML擴展;x:Type相當於C#中typeof運算符功能,這里傳遞的值是System.Diagnostics.Process;MethodName用來獲取或設置要調用的方法的名稱,傳遞的值為System.Diagnostics.Process.Start方法用來啟動一個進程。

  • 第三個標簽ObjectDataProvider.MethodParameters內嵌了兩個方法參數標簽,通過System:String分別指定了啟動文件和啟動時所帶參數供Start方法使用。

介紹完攻擊鏈中ResourceDictionary后,攻擊的Payload主體已經完成,接下來通過XamlReader這個系統類所提供的XML解析器來實現攻擊。

 

3.3、XamlReader

XamlReader位於System.Windows.Markup空間下,顧名思義就是用來讀取XAML文件,它是默認的XAML讀取器,通過Load讀取Stream流中的XAML數據,並返回作為根對象,而另外一個Parse方法讀取指定字符串中的XAML輸入,也同樣返回作為根對象,自然Parse方法是我們關心和尋求的。

 

圖片13.png

只需使用ObjectDataProvider的ObjectInstance方法實例化XamlReader,再指定MethodName為Parse,並且給MethodParameters傳遞序列化之后的資源字典數據,這樣就可以完成XmlSerializer反序列化攻擊鏈的打造。

 

0x04 代碼審計視角

從代碼審計的角度其實很容易找到漏洞的污染點,通過前面幾個小節的知識能發現序列化需要滿足一個關鍵條件Type.GetType,程序必須通過Type類的靜態方法GetType,例如以下demo

圖片14.png

首先創建XmlDocument對象載入xml,變量typeName通過Xpath獲取到Item節點的type屬性的值,並傳給了Type.GetType,緊接着讀取Item節點內的所有Xml數據,最終交給Deserialize方法反序列化,這是一個近乎完美的利用點。再來看筆者在github上收集到的XmlSerializer反序列化類:XmlSerializeUtil.cs

圖片15.png

此處值參數類型為Type,代碼本身沒有問題,問題在於程序開發者可能會先定義一個字符串變量來接受傳遞的type值,通過Type.GetType(string)返回 Type對象再傳遞進DeserializeXml,在代碼審計的過程中也需要關注此處type的來源。

 

0x05 案例復盤

最后再通過下面案例來復盤整個過程,全程展示在VS里調試里通過反序列化漏洞彈出計算器。

  1.輸入http://localhost:5651/Default?node=root&value=type加載了遠程的(192.168.231.135)1.xml文件

圖片16.png

2.    通過xmlHelper.GetValue得到root節點下的所有XML數據

圖片17.png

 

3. 這步最關鍵,得到root節點的type屬性,並提供給GetType方法,XmlSerializer對象實例化成功

圖片18.png

 

4.    XmlSerializer.Deserialize(xmlReader)成功調出計算器

圖片19.png

 

最后附上動圖

 

0x06 總結

由於XmlSerializer是系統默認的反序列類,所以在實際開發中使用率還是比較高的,攻擊者發現污染點可控的時候,可以從兩個維度去尋找利用的點,第一從Web應用程序中尋求可以執行命令或者寫WebShell的類和方法;第二就是本文中所說的利用ObjectDataProvider、ResourceDictionary、XamlReader組成的攻擊鏈去執行命令或者反彈Shell ,最后.NET反序列化系列課程筆者會同步到 https://github.com/Ivan1ee/、https://ivan1ee.gitbook.io/,后續筆者將陸續推出高質量的.NET反序列化漏洞文章,大致課程大綱如下圖

image.png


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM