【通读审计】之DOYOCMS 安全响应中心

user 4天前 13


0x00 前言
首先大家看到我帖子的标题,我这里所谓的“通读审计”就是把整个代码审计过程一字不差的记录下来。因为审计过很多CMS,也看过很多代码审计文章,大多数都是把漏洞点提一下然后文章结束,对于不懂MVC的审计者来说,看其他人代码审计文章,很多细节问题搞不明白(因为我自己也是从那里过来的),所以读起来不是太舒畅,自己学习起来也有点吃力,尤其到MVC框架一块。然后这把就是给大家分享一下我个人的整个代码审计过程,以及如何发现漏洞,如何自己构造语句等等。这次审计的cms是DOYOCMS建站系统,因为我们学校使用的该系统,就随便拿一套过来记录吧。也是给自己做个记录,给大家分享个人经验。
(注意:由于是审计的整个过程,该文章比较啰嗦,大约2000多个字,大牛的话可以一略而过,像我这等小白就可以认真看一下,共同学习。)
0x01 MVC的了解
这里就不再废话了,直接就开始吧。


Index.php第二行,上来直接require进来一个config.php,我们来看一下该文件到底做了哪些操作。


OK,config.php文件定义了一些常量以及数据库信息。
Index.php第三行。

$doyoConfig['view']['config']['template_dir'] = APP_PATH.'/template/'.$doyoConfig['ext']['view_themes'];

如下图的变量值赋给

$doyoConfig['view']['config']['template_dir']



Index.php的第四行的

require(DOYO_PATH."/sys.php");

把config.php的两个定义常量

define("APP_PATH",dirname(__FILE__));
define("DOYO_PATH",APP_PATH."/include");

的DOYO_PATH与sys.php拼接起来并且包含(包含include/sys.php)
我们看一下包含进来的sys.php文件进行了哪些操作


Sys.php的第四行包含进来一个Functions.php文件。从文件名上来看来猜测是包含来的一些方法吧。
打开看一下


好,确定包含方法。回到sys.php文件继续通读
Sys.php文件的第五行

$GLOBALS['G_DY'] = spConfigReady(require(DOYO_PATH."/inc.php"),$doyoConfig);

Require进来inc.php文件并且使用spConfigReady函数处理(之前提到的functions.php方法文件还记得吧)
跟进spConfigReady方法


可以看到第218行进行判断传入进来的$useconfig是否为数组,如果为数组进行遍历该数组,并且把传递过来的$useconfig保存到$preconfig内(合并数组)。
从他调用该方法中给形参1传递一个require进来的文件可以判断。该文件直接return一个数组。
打开包含进来的require(DOYO_PATH."/inc.php")文件来看一番


直接return一个数组
好,把inc.php文件return的数组与之前在index.php中定义过的$doyoConfig['view']['config']['template_dir']保存到全局变量$GLOBALS['G_DY']
回来继续读取sys.php


在functions文件中,跟进import方法。


再来看一下传递进来的值


39-43行的

if( TRUE == @is_readable($sfilename) ){
        require($sfilename); 
        $GLOBALS['G_DY']['import_file'][md5($sfilename)] = TRUE; 
        return TRUE;
}

进行判断形参$sfilename是否可读,可读即包含。

OK,了解完import是用来读取文件之后,我们随着依次打开


这三个文件,看一下都有哪些操作。


哈哈,三个文件都是定义的类文件。
从字面上来看
syController:Controller的表面意思为控制器
syModel:Model的表面意思为模型
syView:View的表面意思为视图
MVC…
再回来读sys.php文件的第29-36行


判断分支给$GLOBALS['G_DY']['url']["url_path_base"]进行赋值
再读取sys.php文件的第42-50行
看到!empty($_SERVER['PATH_INFO'],是用来实现伪静态化的操作的
下面的52-58行是作为重点。


判断分支给$GLOBALS['G_DY']['url']["url_path_base"]进行赋值
再读取sys.php文件的第42-50行
看到!empty($_SERVER['PATH_INFO'],是用来实现伪静态化的操作的
下面的52-58行是作为重点。


跟进syClass与spLaunch方法

function syClass($class_name, $args = null, $sdir = null, $force_inst = FALSE){
        if(preg_match("/^[a-zA-Z0-9_\-]*$/",$class_name)==0)syError("类定义不存在,请检查。");
        if(TRUE != $force_inst)if(isset($GLOBALS['G_DY']["inst_class"][$class_name]))return $GLOBALS['G_DY']["inst_class"][$class_name];
        if(null != $sdir && !import($sdir) && !import($sdir.'/'.$class_name.'.php'))return FALSE;
        $has_define = FALSE;
        if(class_exists($class_name, false) || interface_exists($class_name, false)){
                $has_define = TRUE;
        }else{
                if( TRUE == import($class_name.'.php')){
                        $has_define = TRUE;
                }
        }
        if(FALSE != $has_define){
                $argString = '';$comma = ''; 
                if(null != $args)for ($i = 0; $i < count($args); $i ++) { $argString .= $comma . "\$args[$i]"; $comma = ', ';}
                eval("\$GLOBALS['G_DY']['inst_class'][\$class_name]= new \$class_name($argString);"); 
                return $GLOBALS['G_DY']["inst_class"][$class_name];
        }
        syError($class_name."类定义不存在,请检查。");
}


0x02 PHP任意文件替换漏洞
紧接着上面继续往下分析
我们打开sysession文件看一下。


自改了session存储机制。注意看write方法。写入session操作。
看到在file_put_contents写入前拼接了” <?php die();?>”关键字。首先我们想到php://filter进行绕过。但是我们可以看到前面拼接了一个$filedir。$filedir变量是路径信息。不可控。则php://filter无法绕过。
file_put_contents在windows下我们可以进行写入冒号文件流的格式。例如:xxx.php:.php。可以逃逸<?php exit;?>。但是生成后的文件却是空的。再进行xxx.php<<<写入文件数据,思路是这样的,但是。我们还可以看到在拼接$name的同时。后面连接了一个’.php’。那么我们提交的数据就变成了xxx.php<<<.php。显然。打乱了文件流规则。也无法利用!
我们没有任何机会来通过session文件来getshell
但是从另一个角度讲,file_put_contents函数是由fopen函数与fwrite函数封装起来的。使用了’w’的写入规则。


比如,这里有一个1.php。文件内容为123,当你file_put_contents(‘1.php’,’xxx’);的时候,1.php文件内容”123”会被替换为我们传入的”xxx”
再来看看程序自写的session存储机制居然没有对路径做任何过滤。
那么就比较有趣了。我们可以通过修改session的值。来进行替换任何文件。


就可以成功把根目录下的 /include/inc.php 文件内容给替换掉。include/inc.php 文件内容为网站配置。该框架中有运行。再结合之前程序写入session文件内容所定义的<?php die();?>。可以导致网站无法正常访问。(被die暂停程序运行)。
验证漏洞结果:


0x03 继续观察MVC
我们再回到functions.php文件进行读取第11行

$handle_controller = syClass($__controller, null, $GLOBALS['G_DY']["controller_path"].'/'.$__controller.".php");

进行syClass函数处理。我们之前了解到syClass方法是用来处理类文件并包含的。


传入的第三个参数亮了。


根据我们传递的参数,则/source/$_GET[‘c’]是我们所包含的文件。我们在source文件夹下创建test.php进行验证一下


看来逻辑是没有问题的
接着我们继续往下看functions.php文件的第16行


这样来,我们整个的处理逻辑搞清楚开始挖掘漏洞
0x04 SSRF漏洞
当前我在functions.php这个文件。既来之,则安之。我通过危险自定义的一些方法来寻找漏洞
我从functions.php文件的第100-117行发现了


该函数传入的$url如果没有严谨的过滤,则会造成SSRF漏洞
全局搜索一下这个函数。


看到大名鼎鼎的a_sys类文件($_REQUEST[‘c’])与调用该漏洞的方法($_REQUEST[‘a’])


可是问题来了,我们所包含类文件的路径是 /source,而不是/source/admin/xxx.php
那么我们如何引入呢?不慌。


全局搜索一下哪里修改了$doyoConfig['view']['config']['template_dir']的值(该值影响包含路径)
在admin.php中重现了index.php的操作。只是略有一些不同


那么我们SSRF漏洞利用点也只能在后台了,有一定的局限性
登陆后台后
我们包含a_sys.php文件并实例化该类,然后调用template_cache方法


这里是传入的$y。我们再该文件内翻半天也不会翻到syArgs方法的。
原因很简单,继承了一个类。


这里构造方法有了和template_cache方法相同的步骤
我们之前所看过的这个类的一个syArgs方法
可是在syController中也没有找到syArgs方法,那么我们就需要看一下__call魔术方法的处理了


第48行
return syClass($name)->__input($args);
syClass我们之前了解到的功能就是进行包含syArgs这个类文件或实例化该类,随后调用__input方法,把我们__construct的$this->a=$this->syArgs('a',1);中’a’,1以数组的形式传递给__input方法


看一下get方法


调用了has方法,把字符串’a’传递进去了
跟进has方法


咦?多了一个成员属性$this->args
我们看一下构造方法是否初始化该属性

该类的构造方法


args成员属性是$_REQUEST进来的变量
如果我们传入的?a=template_cache,返回true
再回来看一下该分支结构


继续跟进filters方法


看到该方法是用来过滤传入数据内容的。在其中的 case 0,1,2,3,4,5,中,可以看到都对传入的数据进行过滤操作。可是在case 2中,漏洞出现了。这里涉及到array_walk_recursive函数利用问题。
看着arrays方法的代码块,好像过滤的还挺全的。不仔细想一想还真不知道。
array_walk_recursive函数并不会影响到数组本身。例如:


可以看到我们放入的 test=>’aaa,单引号并没转义。好了,现在了解到syArgs的第二个参数存在未过滤问题。
我们继续回到第102-105行的操作
再回到我们之前这一步


把我们传入的y进行实体化编码(htmlspecialchars)以及防注入(addslashes)操作,但是我们当前发现的漏洞输入SSRF类型的,所以对我们毫无影响~
好了,构造payload: admin.php?c=a_sys&a=template_cache&y=127.0.0.1:3306
验证漏洞结果:


0x05 SQL注入漏洞
那么我们知道syArgs(x,2)的第二个参数存在漏洞问题之后。我们全局搜索:,2)。来查找哪一地方使用了该函数。


可以看到update关键词。这东西对我们非常敏感。审计突然就像喝了红牛一样。精神杠杠的。
PS:这里if验证跟进就不再说了。直接看update。写了2000字了。内心有点恶心。。


果然,是自己封装的update语句。
那么构造
Payload:?c=a_classtypes&a=alledit&orders[]=1' or updatexml(2,concat(0x7e,(version())),0) or'
验证漏洞结果:



少客联盟- 版权声明 1、本主题所有言论和图片纯属会员个人意见,与少客联盟立场无关。
2、本站所有主题由该帖子作者发表,该帖子作者user少客联盟享有帖子相关版权。
3、少客联盟管理员和版主有权不事先通知发贴者而删除本文。
4、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者user少客联盟的同意。
5、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任。
6、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责。
7、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意。
8、官方反馈邮箱:chinasuc@chinasuc.cn


上一篇:今年处置7000余款违法违规App
下一篇:看的太慢飘了啊!
所有人都在等你长大 但没有人陪着你长大
最新回复 (0)
    • 少客联盟
      2
        登录 注册 QQ登录(停用)
返回