代码审计-yzmcms


前言

这次审计的CMS是最新版的YzmCMS,该CMS采用MVC架构整体上安全做的非常不错几乎没有审计出来什么危害性比较大的洞,

本文记录一下审计的过程。

审计思路

分享一下本人MVC架构的代码审计思路:

  • 1.确定路由走向,了解网站的基本架构。
  • 2.了解底层的过滤情况
  • 3.分析底层数据库运行方式
  • 4.漏洞挖掘

确定路由走向,了解网站的基本架构

为了更清晰的了解MVC架构网站的基本架构我这里使用通读的方法阅读入口文件index.php,在开始前因为一般代码中会含有大量的常量我们可以在程序末尾添加以下代码获取使用到的常量:

foreach(get_defined_constants(true)['user'] as $k=>$v){
    echo $k.'---'.$v."\r\n";
}

然后我们开始阅读代码。

首先前面的定义常量不用管我们来看21行代码,加载框架的入口文件,包含./yzmphp/yzmphp.php我们跟进看看

require(YZMPHP_PATH.'yzmphp'.DIRECTORY_SEPARATOR.'yzmphp.php');

照旧前面一堆的是定义常量我们不用管,看到33行调用yzm_base的静态方法load_sys_func

yzm_base::load_sys_func('global');

我们根据去发现是一个文件包含的操作,包含了./yzmphp/core/function/global.func.php文件。跟进查看发现全是定义了一些方法无进行任何操作。

    public static function load_sys_func($func) {
        if (is_file(YZMPHP_PATH.'yzmphp'.DIRECTORY_SEPARATOR.'core'.DIRECTORY_SEPARATOR.'function'.DIRECTORY_SEPARATOR.$func.'.func.php')) {
            include YZMPHP_PATH.'yzmphp'.DIRECTORY_SEPARATOR.'core'.DIRECTORY_SEPARATOR.'function'.DIRECTORY_SEPARATOR.$func.'.func.php';
        }
    }

我返回继续往下看到70到72行加载了三个公用文件:

yzm_base::load_common('function/system.func.php'); //定义各种方法
yzm_base::load_common('function/extention.func.php');//什么都没有干
yzm_base::load_common('data/version.php'); 

跟进同样发现只是定义了各种方法和包含一些常量。此时读完后yzm_base类后没有了任何操作,这时我们回到index.php,看一下index.php文件的最后一行。

yzm_base::creat_app();

我们重返yzmcms.php中观察yzm_base类中creat_app静态方法都是怎么运行的:

上面我们看出来首先进入load_sys_class方法中然后在进入__load_class方法中,我们继续更进去看看

  • 首先进行路径拼接/yzmphp/core/class/application.class.php
  • 然后include该文件
  • 在进行new 该类

image-20201108153230743

我们继续看看/yzmphp/core/class/application.class.php的类初始化干了什么

代码如下:

    public function __construct() { 
        yzm_base::load_sys_class('debug', '', 0);
        register_shutdown_function(array('debug','fatalerror'));
        set_error_handler(array('debug','catcher'));
        set_exception_handler(array('debug', 'exception'));
        $param = yzm_base::load_sys_class('param');
        define('ROUTE_M', $param->route_m());
        define('ROUTE_C', $param->route_c());
        define('ROUTE_A', $param->route_a());
        $this->init();
    }

前面4行都是debug不用管,我们看看这一行代码$param = yzm_base::load_sys_class('param');该调用方法前面已经写过了这里就不罗嗦了,load_sys_class静态方法简单的来说就是实例化/yzmphp/core/class/xx.class.php中的类。

我们更进去看看,代码如下:

public function __construct() {
   $route_config = C('route_config');//加载配置文件
   $this->route_config = isset($route_config[HTTP_HOST]) ? $route_config[HTTP_HOST] : $route_config['default'];
   if(URL_MODEL){
      if(C('set_pathinfo')) $this->set_pathinfo();
      $this->pathinfo_url();
   }
   return true;
}

首先是调用C方法调用配置文件./common/config/config.php,从配置中我们可以看到默认路由规则

然后因为set_pathinfo配置文件默认为false所以我们会进入19行。

看到调用了一个pathinfo_url的方法操作,我们跟进去看看发生了什么,说句老实话这段码我没理解作者究竟要做什么,从代码意义上看这里就是添加多了一个$_GET[‘s’]路由指定方法。

然后我们放回进行阅读代码:

看到了第21-23行分别调用了param对象下的ruote_m(module)、ruote_c(controller)、ruote_a(action),我们再次回到param.class.php文件中看一下是如何定义的。

image-20201108155711386

这三个方法定义都一样,非常简单如果存在就获取它的参数如果不存在获取配置文件中默认参数,并且这里进行一个数据的安全检测safe_deal方法。

然后我们继续返回看最后一行代码:$this->init();

在init方法中首先我们看到看到其又又又调用了一个方法load_controller(),跟进看看

看以看到就是路径拼接,最后拼接成我们的控制器中的文件路径。然后init下面的代码就是调用$_GET[‘a’]的方法。

看到这里我们就看完了,基本对整个程序有了一定的了解。从上面分析可以看出我们可以用三种方法访问我们的资源:

简单测试一下:

在index控制器下的index.class.php里面创建一个方法

    public function test(){
        echo 233;
    }

用上面的方法进行访问

没有任何问题后开始下一步。

底层的过滤

原生 GET,POST,REQUEST

其实我们前面的代码阅读中就能发现该CMS没有对原生的GET,POST,REQUEST进行任何过滤,为了稳妥起见我们进行一下简单的测试;

把前面的test方法修改一下:

    public function test(){
        var_dump($_GET);
        var_dump($_POST);
        var_dump($_POST);
    }

可以看到没有任何过滤。

系统外部变量获取函数

程序有没有自定义方法来获取外部变量?例如:

函数_1:function get(

函数_2:function post(

函数_3:function request(

从上面阅读源码中我们发现并没有。

结论

从这里我们首先可以得到结论该程序获取外部变量都是使用原生没有任何过滤的函数。

了解数据库底层运行方式

对于MVC架构程序如果我们能够了解数据库底层的运行方式将会对我们挖洞起到很大的帮助。我们先随便找个存在数据交互的地方进行回溯看看该程序都是怎么运行的。

我们看看search类中是怎么查询数据的

            $db = D($dbname);
            $total = $db->where($where)->total();

首先看一下D方法,连接数据库然后实例化表对象。

这里会包含\yzmphp\core\class\db_factory.class.php文件,我们跟进看看,因为我们安装程序默认使用PDO,所以进入39行。

包含了\yzmphp\core\class\db_pdo.class.php文件,我们在跟进去看看。从这里我们看到该程序底层数据库的各种操作。

我们看看total方法:

可以看到其拼接sql语句后交给了execute方法进行执行sql语句

从上面我们看到其使用了预处理技术,看来想要找SQL注入基本上已经是无了,所以我们可以把重心转移到其他漏洞挖掘上。

漏洞挖掘

反射型XSS

首先看看其xss过滤机制,在文件yzmphp/core/function/global.func.php下

使用htmlspecialchars函数并且添加了ENT_QUOTES选项,唯一可能绕过的点就是没有对key值进行过滤。

毕竟这不是底层过滤我们可以看看有没有程序员疏忽没有添加过滤的点。因为这是MVC架构的程序所以我们可以在view下查找输出点。

在翻找中可以看到一处没有任何过滤的点:

我们可以使用if(isset($_GET[全局搜索寻找相同的漏洞点。

存储型XSS

首先我们要明确存储型XSS一般都发生在数据库的insert和update这种插入和更新数据库数据操作中。我们先看看这些操作底层有没有进行过滤处理。

在yzmphp/core/class/db_pdo.class.php中

我们可以看到使用safe_data进行检查,跟进看看:

image-20201108172932580

发现简直无解,但是认真观察发现htmlspecialchars实体编码默认是关闭的!!还有机会。

我们全局搜索insert(观察

这些使用了true参数都可以直接忽略了。最终我们发现好几处存储型xss

例如:application/adver/controller/adver.class.php下的广告管理

我们在看get_code方法,可以看出来code参数没有任何过滤就带进去了数据了。

payload:

code:javascript:alert('xss')

CSRF

从application/admin/controller/common.class.php中我们可以看到其权限认证的方法其中就存在其防止CSRF的检测方法

检查referer但是这里的绕过方法非常的简单,只需要referer为空即可绕过。

在构造POC的html中,我们只需要加一句meta标签即可将发出去的请求不会携带REFERER。

<meta name="referrer" content="never">

但是添加管理员处可以发现其添加了token认证。

但有趣的是并不是所有地方都添加token认证

通过全局搜索check_token关键字我们可以发现只有添加和修改管理员信息,以及修改URL规则这几处有添加token其他都没有添加token。

所以其他的地方例如添加删除会员还是会存在CSRF漏洞的。

无回显的SSRF

在后台我们可以看到有一个采集功能,一般这些功能都伴有网络请求功能很容易产生SSRF漏洞。其实这里还有一个存储行XSS原理同上分析这里就不多说了。

观察源码在/application/collection/controller/collection_content.class.php下

从数据库取出URL然后进行连接测试,跟进get_content方法中去。

可以看出来没有任何的过滤。

我们使用dnslog进行测试:

后言

除了XSS过滤没做好外该CMS的安全性还是不错的(其实就是自已菜什么都审计不出来),给该CMS点个star

参考文章

https://www.freebuf.com/vuls/248912.html


文章作者: EASY
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 EASY !
 上一篇
记一次有趣的支付漏洞挖掘 记一次有趣的支付漏洞挖掘
前言最近漏洞挖掘过程中遇到一个有趣的支付漏洞,开发者已经修复,这里记录一下挖掘过程。 支付逻辑分析产品界面 抓包分析: 主要几个参数: appname:购买产品名字 fee:支付金额,实际的支付金额是这个数除以100,所以这里是支付1
2020-11-12 EASY
下一篇 
我的Gitbook发布啦! 我的Gitbook发布啦!
前言博客添加功能Gitbook,这是我本人不定期更新的免费开源的Web安全相关知识归纳总结。该项目旨在归纳自已在大学或者大学后自已学习到的知识,记录自已曾经奋斗过的青春,当然如果对路过看到人有所帮助我也非常的开心! 尽管我在写文章的时候尽可
2020-10-19 EASY
  目录