昨天360補天發了這樣的一條微博:
然后打聽了一下細節,發現居然是我13年7月報給TSRC的漏洞,看今天大家玩的挺開心,與TSRC的人聊了兩句,說這個系列可以發幾個了,所以我也來湊個熱鬧,把原來的分析發出來給大家看一下(這里做個廣告,TSRC提交Discuz的漏洞,獎品棒棒噠~~)。這個漏洞原來我是作為前臺命令執行發個TSRC的,雖然有限制,但是個人感覺還是不錯的。只發一個怕各位不過癮,就再來一發這個點比較直觀的后臺命令執行和繞過前臺命令執行的修復。原TSRC上的漏洞標題是《Discuz! X系列遠程命令執行漏洞(二)》、《Discuz! X系列遠程命令執行漏洞(三)》和《Discuz! X系列遠程命令執行漏洞(四)》。
下面是原漏洞報告的概要部分:
“騰訊旗下Discuz! X系列cms存在遠程命令執行漏洞,經測試在其2013年6月20日發布的最新版本的Discuz! X3中仍存在此問題。目前這個漏洞尚未在網絡中流傳,屬于0day漏洞。 這個漏洞存在于圖片裁剪功能中,需要管理員啟用ImageMagick上傳圖片功能方可觸發。此漏洞只需一個可訪問論壇內容的賬號,即可利用。”
這個漏洞出現在\source\class\class_image.php文件中的Thumb_IM()函數,問題代碼如下:
#!php
function Thumb_IM() {
switch($this->param['thumbtype']) {
case 'fixnone':
case 1:
if($this->imginfo['width'] > $this->param['thumbwidth'] || $this->imginfo['height'] > $this->param['thumbheight']) {
$exec_str = $this->param['imageimpath'].'/convert -quality '.intval($this->param['thumbquality']).' -geometry '.$this->param['thumbwidth'].'x'.$this->param['thumbheight'].' '.$this->source.' '.$this->target;
$return = exec($exec_str);
if(!file_exists($this->target)) {
return -3;
}
}
break;
//省略部分代碼
從第一行紅色代碼中可以看出,程序通過一些變量的拼接形成一條系統命令,在第二行使用exec方法進行執行。若用戶可以控制這些變量中的任何一個,那么就可能導致任意命令的執行。
這個漏洞出現在\source\class\class_image.php文件中的Thumb_IM()函數,問題代碼如下:
#!php
function Thumb_IM() {
switch($this->param['thumbtype']) {
case 'fixnone':
case 1:
if($this->imginfo['width'] > $this->param['thumbwidth'] || $this->imginfo['height'] > $this->param['thumbheight']) {
$exec_str = $this->param['imageimpath'].'/convert -quality '.intval($this->param['thumbquality']).' -geometry '.$this->param['thumbwidth'].'x'.$this->param['thumbheight'].' '.$this->source.' '.$this->target;
$return = exec($exec_str);
if(!file_exists($this->target)) {
return -3;
}
}
break;
//省略部分代碼
從第一行紅色代碼中可以看出,程序通過一些變量的拼接形成一條系統命令,在第二行使用exec方法進行執行。若用戶可以控制這些變量中的任何一個,那么就可能導致任意命令的執行。
[email protected]! X系列遠程命令執行漏洞分析(二)》報告中提到過這個問題,也已經提交給騰訊修復了,使用的利用點是param['thumbwidth']和param[' thumbheight']。而這次我使用到param['imageimpath']這個參數,這個參數對應的是后臺配置中的“ImageMagick程序安裝路徑”。
這個利用點應該是最后一個可控點了,因為param['thumbwidth']和param[' thumbheight']在提交給騰訊后添加了整形校驗轉換,無法傳遞字符串。而source和target需要在前面進行文件和文件是否存在的驗證,無法自由發揮?。
下面來看一下傳遞param['imageimpath']這個參數的代碼,它的位置在\source\admincp\admincp_checktools.php文件中:
#!php
$settingnew = $_GET['settingnew'];
if(!empty($_GET['previewthumb'])) {
$_G['setting']['imagelib'] = $settingnew['imagelib'];
$_G['setting']['imageimpath'] = $settingnew['imageimpath'];
$_G['setting']['thumbwidth'] = $settingnew['thumbwidth'];
$_G['setting']['thumbheight'] = $settingnew['thumbheight'];
$_G['setting']['thumbquality'] = $settingnew['thumbquality'];
require_once libfile('class/image');
@unlink(DISCUZ_ROOT.$_G['setting']['attachdir'].'./temp/watermark_temp1.jpg');
@unlink(DISCUZ_ROOT.$_G['setting']['attachdir'].'./temp/watermark_temp2.jpg');
$image = new image;
//省略部分代碼
可以從紅色代碼出看到imageimpath參數沒有進行任何過濾,便傳入到全局變量中了。在后面的image類中使用它也是直接從全局變量中提取,沒有做任何的過濾和校驗,下面是image類的構造函數代碼:
#!php
function image() {
global $_G;
$this->param = array(
'imagelib' => $_G['setting']['imagelib'],
'imageimpath' => $_G['setting']['imageimpath'],
'thumbquality' => $_G['setting']['thumbquality'],
'watermarkstatus' => dunserialize($_G['setting']['watermarkstatus']),
'watermarkminwidth' => dunserialize($_G['setting']['watermarkminwidth']),
'watermarkminheight' => dunserialize($_G['setting']['watermarkminheight']),
'watermarktype' => $_G['setting']['watermarktype'],
'watermarktext' => $_G['setting']['watermarktext'],
'watermarktrans' => dunserialize($_G['setting']['watermarktrans']),
'watermarkquality' => dunserialize($_G['setting']['watermarkquality']),
);
}
[email protected]! X系列遠程命令執行漏洞分析(二)[email protected]! X系列遠程命令執行漏洞分析(三)》兩篇報告中,將視角盯死在了Thumb_IM這個方法上。在修復后,這個方法的命令執行失敗,導致利用后續操作停止。
但是如果讓這個方法正常完成它的工作,那么他后面的操作中還是有很多修復中沒有考慮到的利用點。下面我們來看下其中的一個利用點,source/class/class_image.php中的Cropper_IM方法:
#!php
function Cropper_IM() {
$exec_str = $this->param['imageimpath'].'/convert -quality 100 '.
'-crop '.$this->param['srcwidth'].'x'.$this->param['srcheight'].'+'.$this->param['srcx'].'+'.$this->param['srcy'].' '.
'-geometry '.$this->param['dstwidth'].'x'.$this->param['dstheight'].' '.$this->source.' '.$this->target;
exec($exec_str);
if(!file_exists($this->target)) {
return -3;
}
}
從上面代碼可以看出,這個方法先拼接命令,然后通過exec函數執行。在拼接的過程中很多變量的內容是可控的,而且在到達這個方法之前沒有做足夠的校驗和過濾。從而導致攻擊者可以通過傳遞一些帶有命令操作的內容,來達到命令執行的目的。
看到這個漏洞點后,我就開始嘗試通過關鍵字從源碼中尋找調用Thumb_IM函數的地方。搜索到使用此函數的文件很多,但是由于這個函數的前兩個參數都是和上傳文件的文件名相關,而且discuz對于上傳文件名做了隨機化命名和后綴白名單處理,所以導致前三個參數為不可控點。
所以這之后我將目標點瞄準到圖片寬度和高度這幾個點,從中篩選參數未被寫死的可控調用。這樣我找到了\source\module\misc\misc_imgcropper.php文件,之后大量的時間花費在對于這個文件調用的業務邏輯的查找上。
無
漏洞的觸發點在source/module/misc/misc_imgcropper.php中,部分代碼如下:
#!php
$cropfile = md5($_GET['cutimg']).'.jpg';
$ictype = $_GET['ictype'];
if($ictype == 'block') {
require_once libfile('function/block');
$block = C::t('common_block')->fetch($_GET['bid']);
$cropfile = block_thumbpath($block, array('picflag' => intval($_GET['picflag']), 'pic' => $_GET['cutimg']));
$cutwidth = $block['picwidth'];
$cutheight = $block['picheight'];
} else {
$cutwidth = $_GET['cutwidth'];
$cutheight = $_GET['cutheight'];
}
$top = intval($_GET['cuttop'] < 0 ? 0 : $_GET['cuttop']);
$left = intval($_GET['cutleft'] < 0 ? 0 : $_GET['cutleft']);
$picwidth = $cutwidth > $_GET['picwidth'] ? $cutwidth : $_GET['picwidth'];
$picheight = $cutheight > $_GET['picheight'] ? $cutheight : $_GET['picheight'];
require_once libfile('class/image');
$image = new image();
$prefix = $_GET['picflag'] == 2 ? $_G['setting']['ftp']['attachurl'] : $_G['setting']['attachurl'];
if(!$image->Thumb($prefix.$_GET['cutimg'], $cropfile, $picwidth, $picheight)) {
showmessage('imagepreview_errorcode_'.$image->errorcode, null, null, array('showdialog' => true, 'closetime' => true));
}
$image->Cropper($image->target, $cropfile, $cutwidth, $cutheight, $left, $top);
showmessage('do_success', dreferer(), array('icurl' => $cropfile), array('showdialog' => true, 'closetime' => true));
}
可以看到在最后調用了$image對象的Cropper方法,而這個過程中$cutwidth和$cutheight都是用戶可控的變量,并且在執行到Cropper方法前沒有進行過任何校驗和過濾。
在管理員開啟ImageMagick上傳圖片功能的前提下,攻擊者只需要一個普通用戶權限即可,后臺設置方法如下圖:
由于這個功能是對圖片進行處理的,所以,要在訪問時提供一個本站有效的圖片地址。而且最好是存放在data/attachment目錄下的,因為默認就是這個目錄,否則就要用“../”來修改路徑。
所以我的訪問路徑為:
http://localhost/Discuz_X3.0/upload/misc.php?mod=imgcropper&img=group/19/group_36_banner.jpg
然后使用chrome審查元素功能,修改picwidth的value為“||whoami&”,如下圖所示:
然后點擊網頁右下角的裁剪按鈕就觸發了。為了能直觀顯示,我添加了一行代碼將接受執行結果的$return變量,echo出來并exit來結束掉后面的語句,效果如下圖所示:
在后臺全局->上傳設置->ImageMagick 程序安裝路徑
由于這個功能是對圖片進行處理的,所以,要在訪問時提供一個本站有效的圖片地址。而且最好是存放在data/attachment目錄下的,因為默認就是這個目錄,否則就要用“../”來修改路徑。
所以我的訪問路徑為
http://192.168.188.142/DiscuzX3.1_1122/upload/misc.php?mod=imgcropper&img=forum/201312/05/094746ub04zw03jr4wi44m.jpg
然后直接點擊左下角的裁剪,雖然會返回圖片訪問錯誤,但是命令卻正常執行了,如下圖所示:
這個漏洞需要在后臺開啟ImageMagick功能,并確保ImageMagick功能正常運行。下圖是我在后臺中開啟的這個功能,并填寫ImageMagick安裝目錄。
如果這個功能開啟,我們甚至可以在沒有賬號的情況下,前臺完成命令執行的觸發。
首先訪問裁切圖片的這個功能,因為這個漏洞的觸發必須要有一張有效圖片,所以我們可以在論壇帖子中隨便找到一張圖片來引用。例如:我的發帖圖片是:
http://192.168.188.143/discuz20140604/data/attachment/forum/201406/13/155944d557e0dtcdpouoad.jpg
那么我要訪問的裁切功能的URL為:
http://192.168.188.143/discuz20140604/misc.php?mod=imgcropper&img=forum/201406/13/155944d557e0dtcdpouoad.jpg
然后修改POST數據包中cutheight或者cutwidth中的內容為“%26%26mkdir tsrc||”提交,就可以在discuz根目錄下創建一個tsrc的目錄。
修改輸入包如下圖所示:
提交后效果如下圖所示: