0X00 前言 在.NET 框架中的 XmlSerializer 類是一種很棒的工具,它是將高度結構化的 XML 數據映射為 .NET 對象。XmlSerializer類在程式中通過單個 API 調用來執行 XML 文檔和對象之間的轉換。轉換的映射規則在 .NET 類中通過元數據屬性來表示,如果程式 ...
0X00 前言
在.NET 框架中的 XmlSerializer 類是一種很棒的工具,它是將高度結構化的 XML 數據映射為 .NET 對象。XmlSerializer類在程式中通過單個 API 調用來執行 XML 文檔和對象之間的轉換。轉換的映射規則在 .NET 類中通過元數據屬性來表示,如果程式開發人員使用Type類的靜態方法獲取外界數據,並調用Deserialize反序列化xml數據就會觸發反序列化漏洞攻擊(例如DotNetNuke 任意代碼執行漏洞 CVE-2017-9822),本文筆者從原理和代碼審計的視角做了相關腦圖介紹和復現。
0X01 XmlSerializer序列化
.NET 框架中 System.Xml.Serialization 命名空間下的XmlSerializer類可以將 XML 文檔綁定到 .NET 類的實例,有一點需要註意它只能把對象的公共屬性和公共欄位轉換為XML元素或屬性,並且由兩個方法組成:Serialize() 用於從對象實例生成 XML;Deserialize() 用於將 XML 文檔分析成對象圖,被序列化的數據可以是數據、欄位、數組、以及XmlElement和XmlAttribute對象格式的內嵌XML。具體看下麵demo
XmlElement指定屬性要序列化為元素,XmlAttribute指定屬性要序列化為特性,XmlRoot特性指定類要序列化為根元素;通過特性類型的屬性、影響要生成的名稱、名稱空間和類型。再創建一個TestClass類的實例填充其屬性序列化為文件,XmlSerializer.Serialize方法重載可以接受Stream、TextWrite、XmlWrite類,最終生成的XML文件列出了TestClass元素、Classname特性和其它存儲為元素的屬性:
0x02 XmlSerialize反序列化
反序列過程:將xml文件轉換為對象是通過創建一個新對象的方式調用XmlSerializer.Deserialize方法實現的,在序列化最關鍵的一環當屬new XmlSerializer構造方法里所傳的參數,這個參數來自System.Type類,通過這個類可以訪問關於任意數據類型的信息,指向任何給定類型的Type引用有以下三種方式。
2.1、typeof
實例化XmlSerializer傳入的typeof(TestClass) 表示獲取TestClass類的Type,typeof是C#中的運算符,所傳的參數只能是類型的名稱,而不能是實例化的對象,如下Demo
通過typeof獲取到Type之後就能得到該類中所有的Methods、Members等信息。下圖運行Debug時,彈出消息對話框顯示當前成員Name的值。
2.2、object.Type
在.NET里所有的類最終都派生自System.Object,在Object類中定義了許多公有和受保護的成員方法,這些方法可用於自己定義的所有其他類中,GetType方法就是其中的一個,該方法返回從System.Type派生的類的一個實例,因為可以提供對象成員所屬類的信息,包括基本類型、方法、屬性等,上述案例中實例化TestClass,再獲取當前實例的Type,如下Demo
2.3、Type.GetType
第三種方法是Type類的靜態方法GetType,這個方法允許外界傳入字元串,這是重大利好,只需要傳入全限定名就可以調用該類中的方法、屬性等
Type.GetType傳入的參數也是反序列化產生的漏洞污染點,接下來就是要去尋找可以被用來攻擊使用的類。
0X03 打造攻擊鏈
首先放上攻擊鏈打造成功後的完整Demo,這段Demo可以復用在任意地方(這裡不涉及.NET Core、MVC),如下圖
只要XmlSerializer存在反序列化漏洞就可用下麵Demo中的內容,涉及到三個主要的技術點,以下分別來介紹原理。
3.1、ObjectDataProvider
ObjectDataProvider類,它位於System.Windows.Data命名空間下,可以調用任意被引用類中的方法,提供成員ObjectInstance用類似實例化類、成員MethodName
調用指定類型的方法的名稱、成員MethodParameters表示傳遞給方法的參數,參考下圖
再給TestClass類定義一個ClassMethod方法,代碼實現調用System.Diagnostics.Process.Start啟動新的進程彈出計算器。如果用XmlSerializer直接序列化會拋出異常,因為在序列化過程中ObjectInstance這個成員類型未知,不過可以使用ExpandedWrapper擴展類在系統內部預先載入相關實體的查詢來避免異常錯誤,改寫Demo
生成data.xml內容如下:
攻擊鏈第一步就算完成,但美中不足的是因筆者在測試環境下新建的TestClass類存在漏洞,但在生產情況下是非常複雜的,需要尋求Web程式中存在脆弱的攻擊點,為了使攻擊成本降低肯定得調用系統類去達到命令執行,所以需要引入下麵的知識。
3.2、ResourceDictionary
ResourceDictionary,也稱為資源字典通常出現在WPF或UWP應用程式中用來在多個程式集間共用靜態資源。既然是WPF程式,必然設計到前端UI設計語言XAML。 XAML全稱Extensible Application Markup Language (可擴展應用程式標記語言) 基於XML的,且XAML是以一個樹形結構作為整體,如果對XML瞭解的話,就能很快的掌握,例如看下麵Demo
-
第一個標簽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方法是我們關心和尋求的。
只需使用ObjectDataProvider的ObjectInstance方法實例化XamlReader,再指定MethodName為Parse,並且給MethodParameters傳遞序列化之後的資源字典數據,這樣就可以完成XmlSerializer反序列化攻擊鏈的打造。
0x04 代碼審計視角
從代碼審計的角度其實很容易找到漏洞的污染點,通過前面幾個小節的知識能發現序列化需要滿足一個關鍵條件Type.GetType,程式必須通過Type類的靜態方法GetType,例如以下demo
首先創建XmlDocument對象載入xml,變數typeName通過Xpath獲取到Item節點的type屬性的值,並傳給了Type.GetType,緊接著讀取Item節點內的所有Xml數據,最終交給Deserialize方法反序列化,這是一個近乎完美的利用點。再來看筆者在github上收集到的XmlSerializer反序列化類:XmlSerializeUtil.cs
此處值參數類型為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文件
2. 通過xmlHelper.GetValue得到root節點下的所有XML數據
3. 這步最關鍵,得到root節點的type屬性,並提供給GetType方法,XmlSerializer對象實例化成功
4. XmlSerializer.Deserialize(xmlReader)成功調出計算器
最後附上動圖
0x06 總結
由於XmlSerializer是系統預設的反序列類,所以在實際開發中使用率還是比較高的,攻擊者發現污染點可控的時候,可以從兩個維度去尋找利用的點,第一從Web應用程式中尋求可以執行命令或者寫WebShell的類和方法;第二就是本文中所說的利用ObjectDataProvider、ResourceDictionary、XamlReader組成的攻擊鏈去執行命令或者反彈Shell ,最後.NET反序列化系列課程筆者會同步到 https://github.com/Ivan1ee/、https://ivan1ee.gitbook.io/,後續筆者將陸續推出高質量的.NET反序列化漏洞文章,大致課程大綱如下圖