0x00 前言 最近幾天國外安全研究員Soroush Dalili (@irsdl)公佈了.NET Remoting應用程式可能存在反序列化安全風險,當服務端使用HTTP通道中的SoapServerFormatterSinkProvider類作為通道接收器並且將自動反序列化TypeFilterLev ...
0x00 前言
最近幾天國外安全研究員Soroush Dalili (@irsdl)公佈了.NET Remoting應用程式可能存在反序列化安全風險,當服務端使用HTTP通道中的SoapServerFormatterSinkProvider類作為通道接收器並且將自動反序列化TypeFilterLevel屬性設置為Full的時候會造成反序列化漏洞,從而實現遠程RCE攻擊,本文筆者從原理和代碼審計的視角做了相關介紹和復現,並且歸納成.NET反序列化漏洞系列課程中的第五課
0x01 .NET Remoting概念
.NET Remoting是一種分散式應用解決方案,它允許不同AppDomain(應用程式域)之間進行通信,這裡的通信可以是在同一個進程中進行或者一個系統中的不同進程間進行的通信。.NET Remoting框架也提供了多種服務,包括激活和生存期支持,以及負責與遠程應用程式進行消息傳輸的通道。應用程式可在重視性能的場景下使用二進位數據傳輸,在需要與其他遠程處理框架進行交互的場景下使用 XML 數據傳輸。在從一個AppDomain向另一個AppDomain傳輸消息時,所有的XML數據都使用 SOAP 協議,總體看.NET Remoting有以下三點優勢:
0x02 .NET Remoting通道和協議
通道是Server和Client進行通信用的,在.NET Remoting中提供了三種通道類型,
IpcChannel提供了使用Windows進程間通信(IPC)系統在同一電腦上的應用程式域之間傳輸消息的機制。在同一電腦上的應用程式域之間進行通信時,IPC通道比TCP或HTTP通道要快得多。但是IPC只在本機應用之間通信。所以,在客戶端和服務端在同一臺機器時,我們可以通過註冊IpcChannel來提高Remoting的性能。但如果客戶端和服務端不在同一臺機器時,我們不能註冊IPCChannel,在此不多介紹。
TcpChannel提供了基於Socket 的傳輸工具,使用Tcp協議來跨越Remoting邊界傳輸序列化的消息流。預設使用二進位格式序列化消息對象,具有更高的傳輸性能,適用於區域網。
HttpChannel提供了一種使用Http協議,使其能在Internet上穿透防火牆傳輸序列化消息流,HttpChannel類型使用Soap格式序列化消息對象,因此它具有更好的互操作性。適用於廣域網,如圖
0x03 攻擊原理
研究漏洞之前先普及下HttpChannel的相關基礎知識,HttpChannel類使用 SOAP 協議在遠程對象之間傳輸消息,並且符合SOAP1.1的標準,所有的消息都是通過SoapFormatter傳遞,此格式化器會將消息轉換為 XML數據併進行序列化,同時向數據流中添加所需的SOAP標頭。如果指定了二進位格式化程式,則會創建二進位數據流。隨後,將使用 HTTP 協議將數據流傳輸至目標URI。HttpChannel分類如圖
下麵是從微軟文檔里摘取定義服務端的代碼片段:
每行代碼分別實現了創建服務端通道並且綁定本地埠9090;註冊服務端通道;以及通過訪問URI為RemoteObject.rem的地址調用遠程的對象,在.NET Remoting中有個激活方式的概念,表示在訪問遠程類型的一個對象實例之前,必須通過一個名為Activation的進程創建它併進行初始化。代碼中引入了服務端激活的WellKnown方式,看下圖
WellKnown理解為知名對象的激活,伺服器應用程式在激活對象實例之前會在統一資源標識符(URI)上來發佈這個類型。然後該伺服器進程會為此類型配置一個WellKnown對象,並根據指定的埠或地址來發佈對象,它的激活分為SingleTon模式 、SingleCall模式,SingleTon類所代表的類型規定每個AppDomain只能存在一個實例,當SingleTon類型載入到AppDomain的時候,CLR調用它的靜態構造器去構造一個SingleTon對象,並將它的引用保存到靜態欄位中,而且該類也沒有提供任何的公共構造器方法,這就防止了其他任何代碼構造該類的其他實例。具體到這兩種模式各有區別,都可以觸發漏洞,因不是重點所以不做過多介紹。
3.1、遠程對象
圖中的RemoteObject類,這是一個遠程對象,看下微軟官方的定義
RemoteObject繼承自MarshalByRefObject類,MarshalByRefObject類(按引用封送)支持遠程處理的應用程式中跨應用程式域(AppDomain)邊界訪問對象,同一應用程式域中的對象直接通信。不同應用程式域中的對象的通信方式有兩種:跨應用程式域邊界傳輸對象副本、通過代理交換消息,MarshalByRefObject類本質上通過引用代理交換消息來跨應用程式域邊界進行通信的對象的基類。
3.2、服務端
創建服務端的通道分為HttpServerChannel、HttpChannel,其中HttpServerChannel類有多個重載方法,需要知道和漏洞相關的兩個重載是發生在參數IServerChannelSinkProvider,它表示服務端遠程消息流的通道接收器
IServerChannelSinkProvider派生出多個類,例如BinaryServerFormatterSinkProvider、SoapServerFormatterSinkProvider類,如下圖
SoapServerFormatterSinkProvider類實現了這個介面,並使用SoapFormatter格式化器序列化對象,如下圖
SoapFormatter格式化器實現了System.Runtime.Serialization.IFormatter介面,IFormatter介面包括了Serialize、Deserialize方法,提供了序列化對象圖的功能。
在序列化的時候調用格式化器的Serialize方法,傳遞對流對象的引用和想要序列化的對象圖引用的兩個參數,流對象可以是從System.IO.Stream類派生出來的任意對象,比如常見的MemoryStream、FileStream等,簡單的說就是通過格式化器的Serialize方法可將對象圖中所有對象都被序列化到流里去,通過Deserialize方法將流反序列化為對象圖。
介紹完SoapFormatter之後回過頭來繼續看SoapServerFormatterSinkProvider類,它有一個重要的屬性TypeFilterLevel,表示當前自動反序列化級別,支持的值為Low(預設)和FULL。
當取值為Low的時候,代表.NET Framework 遠程處理較低的反序列化級別,只支持基本遠程處理功能相關聯的類型,而取值為Full的時候則支持所有類型在任意場景下遠程處理都支持,所以取值為Full的時候,存在著嚴重的安全風險。
梳理一下HTTP通道攻擊的前置條件,第一步實例化SoapServerFormatterSinkProvider類並且設置TypeFilterLevel屬性為Full;第二步實例化HttpServerChannel/HttpChannel類,
使用下列三種重載方法實現傳入參數SoapServerFormatterSinkProvider
- 滿足攻擊者需求的第1個攻擊重載方法是public HttpServerChannel(IDictionary properties, IServerChannelSinkProvider sinkProvider);
這裡筆者用VulnerableDotNetHTTPRemoting項目中的VulnerableDotNetHTTPRemotingServer類來改寫官方Demo 。IDictionary集合存放當前通道的配置信息,如圖
- 滿足攻擊者需求的第2個攻擊重載方法是public HttpServerChannel(string name, int port, IServerChannelSinkProvider sinkProvider);
- 滿足攻擊者需求的第3個攻擊方法是位於HttpChannel類下的 public HttpChannel(IDictionary properties, IClientChannelSinkProvider clientSinkProvider, IServerChannelSinkProvider serverSinkProvider)
VulnerableDotNetHTTPRemoting項目中用到就是第三種攻擊方法,由於.NET Remoting客戶端在攻擊中用途不大,故筆者不做贅述。
0x04 打造Poc
國外研究者發現Microsoft.VisualStudio.Text.UI.Wpf.dll 中的Microsoft.VisualStudio.Text.Formatting.TextFormattingRunProperties類實現了ISerializable介面,這個介面可以對序列化/反序列化的數據進行完全的控制,並且還避免了反射機制, 但有個問題Microsoft.VisualStudio.Text.UI.Wpf.dll需要安裝VisualStudio ,在非開發主機上不會安裝,但研究者後來發現Microsoft.VisualStudio.Text.Formatting.TextFormattingRunProperties類在Windows預設安裝的Microsoft.PowerShell.Editor.dll里也同樣存在,反編譯得到源碼,
實現了ISerializable介面,ISerializable只有一個方法,即 GetObjectData,如果一個對象的類型實現了ISerializable介面,會構造出新的System.Runtime.Serialization.SerializationInfo對象,這個對象包含了要為對象序列化的值的集合。
GetObjectData方法的功能是調用SerializationInfo類型提供的SetType方法設置類型轉換器,使用提供的AddValue多個重載方法來指定要序列化的信息,針對要添加的每個數據,都要調用一次AddValue,GetObjectData添加好所有必要的序列化信息後會返回到類型解析器,類型解析器獲取已經添加到SerializationInfo對象的所有值,並把他們都序列化到流中,代碼邏輯實現部分參考如下
TextFormattingRunProperties類中的ForegroundBrush屬性支持XAML數據,攻擊者可以引入《.NET高級代碼審計(第一課) XmlSerializer反序列化漏洞》同樣的攻擊載荷,如下
又因為SoapServerFormatterSinkProvider類用SoapFormatter格式化器處理數據,所以客戶端提交的數據肯定是SOAP消息,SOAP是基於XML的簡易協議,讓應用程式在HTTP上進行信息交換用的。為了給出標準的SOAP有效負載,筆者參考微軟官方給的Demo
結合Soroush Dalili (@irsdl)給出的有效載荷,元素a1指向的命名空間正是TextFormattingRunProperties類所在空間地址
在<a1:TextFormattingRunProperties></a1:TextFormattingRunProperties>元素內添加了屬性ForegroundBrush,在ForegroundBrush元素內帶入ResourceDictionary,這樣SOAP消息的攻擊載荷主體就完成了。@irsdl給出的有效載荷如下
由於.NET Remoting只支持SOAP 1.1,所以要指定SOAPAction,說來也奇怪這個SOAPAction的值是個URI,但是這個URI不必對應實際的位置。SOAPAction Header選項在SOAP1.2版本已經移除。另外一點圖上請求URI中的擴展名是rem,如果生產環境部署在IIS里,預設調用.NET應用模塊IsapiModule來處理HttpRemoting,所以在白盒審計或者黑盒滲透的時候遇到rem擴展名,就得考慮可能開啟了.NET Remoting應用。
還有一處需要註意,HTTP請求有個擴展方法M-POST,其中的其中的M表示Mandatory(必須遵循的,強制的),如果一個HTTP請求包含至少一個強制的擴充聲明,那麼這個請求就稱為強制的請求。強制請求的請求方法名字必須帶有“M-”首碼,例如,強制的POST方法稱為M-POST,這樣的請求方式或許能更好的躲避和穿透防護設備。
0x05 代碼審計
5.1、SoapServerFormatterSinkProvider
從SoapServerFormatterSinkProvider類分析來看,需要滿足屬性TypeFilterLevel的值等於TypeFilterLevel.Full,可觸發的通道包括了HttpChannel類、HttpServerChannel類,這個攻擊點的好處在於發送HTTP SOAP消息,可很好的穿透防火牆。
5.2、BinaryServerFormatterSinkProvider
從BinaryServerFormatterSinkProvider類分析來看,也需要滿足屬性TypeFilterLevel的值等於TypeFilterLevel.Full,可觸發的通道包括了TcpChannel類、TcpServerChannel類,這個攻擊點可反序列化二進位文件,筆者由於時間倉促,暫時不做分析跟進,有興趣的朋友可自行研究。
0x06 復盤
筆者將VulnerableDotNetHTTPRemoting項目部署到虛擬機,運行Server端,打開了本地埠1234
Burpsuite請求後成功彈出計算器,感謝Soroush Dalili (@irsdl)的分享。
0x07 總結
.NET Remoting技術已經出來很多年了,現在微軟主推WCF來替代它,在開發中使用概率越來越低,從漏洞本身看只要沒有設置SoapServerFormatterSinkProvider類屬性TypeFilterLevel=Full就不會產生反序列化攻擊(預設就是安全的)最後.NET反序列化系列課程筆者會同步到 https://github.com/Ivan1ee/ 、https://ivan1ee.gitbook.io/ ,後續筆者將陸續推出高質量的.NET反序列化漏洞文章,歡迎大伙持續關註,交流,更多的.NET安全和技巧可關註實驗室公眾號。