专栏首页FreeBuf通读审计之DOYOCMS

通读审计之DOYOCMS

前言

首先大家看到我文章的标题,我这里所谓的“通读审计”就是把整个代码审计过程一字不差的记录下来。因为审计过很多CMS,也看过很多代码审计文章,大多数都是把漏洞点提一下然后文章结束,对于不懂MVC的审计者来说,看其他人代码审计文章,很多细节问题搞不明白(因为我自己也是从那里过来的),所以读起来不是太舒畅,自己学习起来也有点吃力,尤其到MVC框架一块。然后这把就是给大家分享一下我个人的整个代码审计过程,以及如何发现漏洞,如何自己构造语句等等。这次审计的cms是DOYOCMS建站系统,因为我们学校使用的该系统,就随便拿一套过来记录吧。也是给自己做个记录,给大家分享个人经验。

一、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保存到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."类定义不存在,请检查。");
}

二、 PHP任意文件替换漏洞

紧接着上面继续往下分析

我们打开sysession文件看一下。

自改了session存储机制。注意看write方法。写入session操作。

看到在file_put_contents写入前拼接了” <?php die();?>”关键字。首先我们想到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暂停程序运行)。

验证漏洞结果:

三、继续观察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行

这样来,我们整个的处理逻辑搞清楚开始挖掘漏洞

四、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=

看一下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

验证漏洞结果:

五、SQL注入漏洞

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

可以看到update关键词。这东西对我们非常敏感。审计突然就像喝了红牛一样。精神杠杠的。

PS:这里if验证跟进就不再说了。直接看update。

果然,是自己封装的update语句。

那么构造

Payload:?c=a_classtypes&a=alledit&orders[]=1’ or updatexml(2,concat(0x7e,(version())),0) or’

验证漏洞结果:

*本文作者:Heihu577,转载请注明来自FreeBuf.COM

本文分享自微信公众号 - FreeBuf(freebuf),作者:Heihu577

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-04-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 手把手教你编写一个简单的PHP模块形态的后门

    看到Freebuf 小编发表的用这个隐藏于PHP模块中的rootkit,就能持久接管服务器文章,很感兴趣,苦无作者没留下PoC,自己研究一番,有了此文 0x00...

    FB客服
  • 代码审计之php.ini配置详解

    apache在启动时php.ini被读取。对于服务器模块版本的php,仅在web服务器启动时读取一次。对于CGI和CLI版本,每次调用都会被读取

    FB客服
  • phpMydmin的GetShell思路

    phpMyadmin是一个以PHP为基础的MySQL数据库管理工具,使网站管理员可通过Web接口管理数据库 。

    FB客服
  • Emlog6 模板设置插件修复版

    首先找到路径/admin/views/template.php文件,替换一下全部代码

    似水的流年
  • PHP-fpm 远程代码执行漏洞(CVE-2019-11043)分析

    国外安全研究员 Andrew Danau在解决一道 CTF 题目时发现,向目标服务器 URL 发送 %0a 符号时,服务返回异常,疑似存在漏洞。

    Seebug漏洞平台
  • MongoDB Web界面 管理工具Rockmongo的安装

    RockMongo是PHP5写的一个MongoDB管理工具。

    似水的流年
  • MongoDB Web界面 管理工具Rockmongo的安装

    RockMongo是PHP5写的一个MongoDB管理工具。 通过 Rockmongo 你可以管理 MongoDB服务,数据库,集合,文档,索引等等。...

    似水的流年
  • 360安全扫描之WordPress 页面异常导致本地路径泄漏 的漏洞修补

    今天头脑一热到360安全检测那里去为自己的网站进行安全扫描了一番。上次扫描还是一年前,当初扫描一个网站是 94 分,那时候还不懂代码,就这么挂着,被360 公开...

    Jeff
  • CentOS下yum安装PHP,配置php-fpm服务

    # rpm -Uvh http://download.Fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8....

    双面人
  • 宿舍报修系统基于易班开发平台版本开源

    0、使用前需要将本目录下的sql文件导入数据库,注意,需支持utf8mb4编码推荐数据库版本mysql5.6

    雨落凋殇

扫码关注云+社区

领取腾讯云代金券