记一次代码审计中后台getshell-bypass
雨下整夜 发表于 北京 漏洞分析 1060浏览 · 2024-01-25 13:57

内容是去年在挖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
*/

可惜,最后没找到组合拳。。。 =.=

0 条评论
某人
表情
可输入 255
目录