内容是去年在挖xxcms的时候发现的此漏洞,主要是针对上传点的检测绕过。
复现
登陆后台->文件管理器->文件上传
先做正常文件的上传,传入<?php echo "i'm safe";?>,正常上传。
再做一般恶意代码的上传,<?php echo "i'm danger";phpinfo();?>。当防护规则匹配到添加的 phpinfo(),返回DedeCMS提示:当前上传的文件中存在恶意代码!
再次修改payload上传 /<?php echo "i'm safe again!";phpinfo();?>/。成功写入phpinfo()
分析
然后解释一下为什么使用此payload绕过,为什么此payload能绕过。
先定位处理文件上传逻辑的代码块:xx/file_manage_control.php
去到文件上传的分支
当$fmdo为upload的时候,进入该段代码,进行文件上传的逻辑处理。
155-177:常规处理上传文件的PHP代码。
我们重点关注处理安全的部分
if (is_file($file) && ($file_ext == "php" || $file_ext == "htm")) {
$fp = fopen($file, "r");
$content = fread($fp, filesize($file));
fclose($fp);
// 不允许这些字符
$content = preg_replace("#(/\*)[\s\S]*(\*/)#i", '', $content);
global $cfg_disable_funs;
$cfg_disable_funs = isset($cfg_disable_funs) ? $cfg_disable_funs : 'phpinfo,eval,assert,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fsockopen,fopen,fwrite,preg_replace';
$cfg_disable_funs = $cfg_disable_funs.',[$]GLOBALS,[$]_GET,[$]_POST,[$]_REQUEST,[$]_FILES,[$]_COOKIE,[$]_SERVER,include,require,create_function,array_map,call_user_func,call_user_func_array,array_filert';
foreach (explode(",", $cfg_disable_funs) as $value) {
$value = str_replace(" ", "", $value);
if(!empty($value) && preg_match("#[^a-z]+['\"]*{$value}['\"]*[\s]*[([{']#i", " {$content}") == TRUE) {
$content = dede_htmlspecialchars($content);
@unlink($file);
die("DedeCMS提示:{$file_base}文件中存在恶意代码!<pre>{$content}</pre>");
}
}
}
简单分为4个部分:a,b,c,d
文件后缀为php或htm
if (is_file($file) && ($file_ext == "php" || $file_ext == "htm"))
正则表达式:/xxxx/格式的注释内容会被替换成空字符串
$content = preg_replace("#(/\*)[\s\S]*(\*/)#i", '', $content);
保证$cfg_disable_funs的值为需检测的危险函数、危险变量等
$cfg_disable_funs = isset($cfg_disable_funs) ? $cfg_disable_funs : 'phpinfo,eval,assert,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fsockopen,fopen,fwrite,preg_replace';
$cfg_disable_funs = $cfg_disable_funs.',[$]GLOBALS,[$]_GET,[$]_POST,[$]_REQUEST,[$]_FILES,[$]_COOKIE,[$]_SERVER,include,require,create_function,array_map,call_user_func,call_user_func_array,array_filert';
对文件写入的内容检测上面的$cfg_disable_funs的值
if(!empty($value) && preg_match("#[^a-z]+['\"]*{$value}['\"]*[\s]*[([{']#i", " {$content}") == TRUE) {
$content = dede_htmlspecialchars($content);
@unlink($file);
die("xxCMS提示:{$file_base}文件中存在恶意代码!<pre>{$content}</pre>");
}
整体的逻辑没有问题。造成漏洞的原因有两个:1.当$content的时候,/<?php echo "i'm safe again!";phpinfo();?>/会在步骤b的时候逃出变量$content不参与后面的检测。2./*/格式的注释是php代码中的多行注释格式。位于其中的代码将会被标记为不执行代码。但是当此字符串不包含在php的标签中的时候,/和/会被视为两段无关的字符串,所以/<?php echo "i'm safe again!";phpinfo();?>/==<?php echo "i'm safe again!";phpinfo();?>。
/
code
*/
可惜,最后没找到组合拳。。。 =.=