粗略的代码审计-phpcms(一)

版本:Phpcms V9.6.3 Release 20170515
环境:php7

简略总结

首先查看了入口文件,流程:index.php->base.php->加载模板。
然后查看了全局函数文件,后台管理结构等,对整个cms结构有了一定的了解,同时有了一点点发现。
如文件管理类,有多重防御,首先有后缀名检测,后缀名破坏(黑名单),文件重命名(但可以写脚本跑),上传是很难绕过的,但我发现文件删除执行过程有点可疑,删除文件似乎是传入sql语句,然后返回相应文件位置,并拼接删除。若修改数据库或控制sql语句,或许可以任意文件删除。(还没深入审计)
另外看了SQL执行文件:/phpcms/libs/classes/db_mysqli.class.php,发现SQL会直接执行接收的数据(虽然数据有可能在调用前经过了过滤),有很大的风险。

后台->扩展->IP禁止->存在SQL注入漏洞

执行文件:/phpcms/modules/admin/ipbanned.php
模板文件:/phpcms/modules/admin/templates/[ipbanned_add.tpl.php | ip_search_list.tpl.php | ipbanned_list.tpl.php]

添加IP禁止:
发现不管传入什么都能显示正确(至少加个IP正则限制吧),但插入到数据库中只会保留15个字符(数据库设置了对应列为char(15))。由于解封时间在数据库是以时间戳形式保存,故无法利用,暂不管。

IP搜索:
同样缺乏输入限制,但暂时不管,先看代码。位置:ipbanned.php L74-85

    public function search_ip() {
        $where = '';
        if($_GET['search']) extract($_GET['search']);  // 变量解析
        if($ip){
            $where .= $where ?  " AND ip LIKE '%$ip%'" : " ip LIKE '%$ip%'";
        }
        $page = isset($_GET['page']) && intval($_GET['page']) ? intval($_GET['page']) : 1;
        $infos = $this->db->listinfo($where,$order = 'ipbannedid DESC',$page, $pages = '2');
        $pages = $this->db->pages;
        $big_menu = array('javascript:window.top.art.dialog({id:\'add\',iframe:\'?m=admin&c=ipbanned&a=add\', title:\''.L('add_ipbanned').'\', width:\'450\', height:\'300\'}, function(){var d = window.top.art.dialog({id:\'add\'}).data.iframe;var form = d.document.getElementById(\'dosubmit\');form.click();return false;}, function(){window.top.art.dialog({id:\'add\'}).close()});void(0);', L('add_ipbanned'));
        include $this->admin_tpl('ip_search_list');
    } 

使用了extract()对传入的参数进行变量解析,同时可以看到$where是直接控制SQL语句的。随便抓了一个包:http://localhost:980/index.php?m=admin&c=ipbanned&a=search_ip&search[ip]=a&dosubmit=搜索&pc_hash=PiMwGT,自行添加参数search[where]从而在变量解析时覆盖掉$where,同时不传入search[ip],然后达到任意构造where语句。由于SQL语句执行时并没有检验数据的合理性,所以我们构造的where将被直接拼接执行。

检验:

关于利用:
首先这个页面本来admin才能看(所以你要先想办法登录进来),所以危害大打折扣。
然后原本打算直接控制$where直接查询的,但数据在页面上显示不了。就打算用布尔注入了,首先在添加IP禁止功能里添加完整套字符,如a,b,c…,它会正常添加的。然后通过?search[where]=ip=substr(SQL,num,1)进行布尔查询,实际情况请自行写脚本爆破。

分析:
通过控制ip=xxx,用数据库里的ip字段当数据匹配,进行布尔注入。测试数据库用户root@localhost:

修复:
extract()中声明EXTR_SKIP,即不允许变量覆盖。

后台->扩展->后台操作日志->存在SQL注入漏洞

执行文件:/phpcms/modules/admin/log.php
模板文件:/phpcms/modules/admin/templates/[log_search_list.tpl.php | log_list.tpl.php]

分析log.php:
这个模块有日志查询和日志删除功能,其中日志删除功能处暂无方法利用。先分析一下日志搜索代码,在log.php L49-74处:

    public function search_log() {
        $where = '';
        extract($_GET['search'],EXTR_SKIP);  // 设置了EXTR_SKIP,不会覆盖已有变量,即不会覆盖$where
        if($username){
            $where .= $where ?  " AND username='$username'" : " username='$username'";
        }
        if ($module){
            $where .= $where ?  " AND module='$module'" : " module='$module'";
        }
        if($start_time && $end_time) {  // 当进入条件且$username和$module为空时,会出现拼接错误,例图一
            $start = $start_time;
            $end = $end_time;
            $where .= "AND `time` >= '$start' AND `time` <= '$end' ";
        }

        $page = isset($_GET['page']) && intval($_GET['page']) ? intval($_GET['page']) : 1; 
        $infos = $this->db->listinfo($where,$order = 'logid DESC',$page, $pages = '12');
        $pages = $this->db->pages;

        ...
    }

例图一:

小结:
因为此处的extract()做了变量解析设置,导致无法利用变量覆盖控制$where。同时phpcms在底层对全局接收的表单参数做了特殊符号转义(如单引号),因此很难在页面通过传入单引号导致数据库出错,但在此处由于逻辑有点错误,可以在传入时使search[username]search[module]为空,search[start_time]search[end_time]不为空,使SQL语句拼接错误,带出报错语句。(鸡肋漏洞,但可以获得表前缀)

修复:
文件中L61改为:$where .= $where ? "ANDtime>= '$start' ANDtime<= '$end' ";

后台->扩展->木马查杀->爆破数据库配置文件

对应文件夹:/phpcms/modules/scan/

简述:该模板通过接收前端的参数,生成一个缓存配置文件,然后根据缓存配置进行文件遍历与匹配。其中可以查看相应文件(database.php和system.php不允许在线读取),且均可以跳至相应页面。由于database.php无法在线打开,所以不能直接读取其中的帐号密码信息。

通过Fuzz,发现特征代码处没有限制传入字符长度,因此可以传入单字母。过程:通过hostname能匹配到database.php,同时该文件默认只有一个hostname字符串。传入hostname|r,得到匹配次数为15,即共有14个r字符。不断重复,可以获得database.php中26个字符的数量。再与默认的database.php中的字符相减,可以得到变化后的字符。此时帐号密码字符与数量可知,顺序自行另外拼接,使得爆破数据库帐号密码难度大大下降。

通过木马查杀功能得知文件中匹配字符数量(显示匹配总数):

与database.php进行验证,为真:

其它小问题:
点击提交,抓包,dir[]会暴露绝对路径。

小结:特征代码应该设置至少2位长度,可自行在后端文件中添加。同时由于该页面需要进入后台,为鸡肋漏洞。(对于弱密码进入后台想获取数据库账密的有一定帮助)

发表评论

电子邮件地址不会被公开。 必填项已用*标注