我赌一包辣条猜这是全网最详细的代码审计(没有之一)
前言
本篇是对极致CMSv1.7漏洞的一些新的发现,先从MVC开始到漏洞的发掘利用
MVC篇
首先,先打开index.php
<?php
// 应用目录为当前目录
define('APP_PATH', __DIR__ . '/');
// 开启调试模式
//define('APP_DEBUG', true);
//定义项目目录
define('APP_HOME','Home');
//定义项目模板文件目录
define('HOME_VIEW','template');
//定义项目模板公共文件目录
define('Tpl_common','');
//定义项目控制器文件目录
define('HOME_CONTROLLER','c');
//定义项目模型文件目录
define('HOME_MODEL','m');
//定义项目默认方法
define('DefaultAction','jizhi');
//取消log
define('StopLog',false);
//定义静态文件路径
define('Tpl_style','/static/');
// 加载框架文件
require(APP_PATH . 'FrPHP/Fr.php');
// 就这么简单~
先是define定义,然后加载框架,所以直接去加载的框架看看,是再FrPHP/Fr.php,打开后,拖到最后,发现
图片.png先加载了配置文件,因为配置文件里是用单引号包裹,就没什么好看的。然后再会触发FrPHP的eun方法,跟进查看
图片.png前三个很简单,是配置数据库,和对传入的数据的简单处理,需要注意的是route()路由处理,对它进行跟进
图片.png
在141行接收url传入的参数,然后在206行对控制器和方法名进行define赋值 图片.png
然后在212行对传入的url进行处理,把输入的url前面传入的index.php先舍去
图片.png
然后在231行开始,会对传入的url拆分,变为数组,如我url输入:index.php/Home/ce,会被拆分为Home和ce然后在243行赋值给控制器名和方法名
图片.png
图片.png
图片.png
然后再调用我们指定的类的方法
图片.png
漏洞篇
SQL注入
在\Home\c\HomeController.php中的jizhi中
看到在101行存在把url则是由这里:
图片.png
$url只是对url栏输入的进行接收,所以我们先来看下接收的方式:
图片.png
看出只是对接收index.php后面的。回到第101行,看到会先以/来进行分割,然后取出数组的第一个进入find中,执行sql语句,所以我们考虑怎么对其进行攻击利用,但是我们不能更换/Home/否则会无法执行jizhi,但是我们回到MVC篇中看到的,在第264行中,我们可以看到如果我们输入的不存在,会默认controllerName为Home
图片.png
所以这就意味着我们可以随意的写入,都会最终默认转换为Home,所以直接进行SQL注入:
然后因为没有过滤,就是正常的sql注入了,HomeController.php中的jizhi_details中还有一处sql注入,是通过传值来进行sql注入的,难度不打,有兴趣的可以自行去看看
XSS
1
漏洞在\Home\c\UserController.php的release中,主要利用代码如下:
if(data = $this->frparam();
$w = get_fields_data($data,$w['molds']);
switch($w['molds']){
case 'article':
if(!$data['body']){
if($this->frparam('ajax')){
JsonReturn(['code'=>1,'msg'=>'内容不能为空!']);
}else{
Error('内容不能为空!');
}
}
if(!$data['title']){
if($this->frparam('ajax')){
JsonReturn(['code'=>1,'msg'=>'标题不能为空!']);
}else{
Error('标题不能为空!');
}
}
$data['body'] = $this->frparam('body',4);
$w['title'] = $this->frparam('title',1);
$w['seo_title'] = $w['title'];
$w['keywords'] = $this->frparam('keywords',1);
$w['litpic'] = $this->frparam('litpic',1);
$w['body'] = $data['body'];
$w['description'] = newstr(strip_tags($data['body']),200);
break;
...
}
...
if($this->frparam('id')){
$a = M($w['molds'])->update(['id'=>$this->frparam('id')],$w);
if(!$a){
if($this->frparam('ajax')){
JsonReturn(['code'=>1,'msg'=>'未修改内容,不能提交!']);
}else{
Error('未修改内容,不能提交!');
}
}
if($this->frparam('ajax')){
JsonReturn(['code'=>0,'msg'=>'修改成功!','url'=>U('user/posts',['molds'=>$w['molds']])]);
}else{
Success('修改成功!',U('user/posts',['molds'=>$w['molds']]));
}
}else{
$a = M($w['molds'])->add($w);
if(!$a){
if($this->frparam('ajax')){
JsonReturn(['code'=>1,'msg'=>'发布失败,请重试!']);
}else{
Error('发布失败,请重试!');
}
}
if($this->frparam('ajax')){
JsonReturn(['code'=>0,'msg'=>'发布成功!','url'=>U('user/posts',['molds'=>$w['molds']])]);
}else{
Success('发布成功!',U('user/posts',['molds'=>$w['molds']]));
}
}
}
很简单,就是先赋值给data会经过get_fields_data函数,该函数代码如下:
function get_fields_data($data,$molds,$isadmin=1){
if($isadmin){
$fields = M('fields')->findAll(['molds'=>$molds,'isadmin'=>1],'orders desc,id asc');
}else{
//前台需要判断是否前台显示
$fields = M('fields')->findAll(['molds'=>$molds,'isshow'=>1],'orders desc,id asc');
}
foreach($fields as $v){
if(array_key_exists($v['field'],$data)){
switch($v['fieldtype']){
case 1:
case 2:
case 5:
case 7:
case 9:
case 12:
$data[$v['field']] = format_param($data[$v['field']],1);
break;
case 11:
$data[$v['field']] = strtotime(format_param($data[$v['field']],1));
break;
case 3:
$data[$v['field']] = format_param($data[$v['field']],4);
break;
case 4:
case 13:
$data[$v['field']] = format_param($data[$v['field']]);
break;
case 14:
$data[$v['field']] = format_param($data[$v['field']],3);
break;
case 8:
$r = implode(',',format_param($data[$v['field']],2));
if($r!=''){
$r = ','.$r.',';
}
$data[$v['field']] = $r;
break;
}
}else if(array_key_exists($v['field'].'_urls',$data)){
switch($v['fieldtype']){
case 6:
case 10:
$data[$v['field']] = implode('||',format_param($data[$v['field'].'_urls'],2));
break;
}
}else{
$data[$v['field']] = '';
}
}
return $data;
}
因为是先经过sql执行,所以可以构造data的值赋值给$w。
然后看到源代码的这一句this->frparam('body',4);,我们可以知道4是没有对html实体话的,所以可以对body进行XSS的注入,所以构造payload:
tid=2&article=asd&body=<script>alert(1)</script>&id=4&molds=article&title=qwe&sad=qwe
2
漏洞在\Home\c\ErrorController.php中
图片.png
很简单,直接输入
?msg=<script>alert(1)</script>
任意文件删除
漏洞在A\c\SysController.php中的deletePicAll处:
图片.png首先,frparam函数功能是可以通过在浏览器传值进行对data数组中litpic检测是否为文件名,然后再删除文件。
首先,我们先来一段测试代码:
function ce(){
$data = $this->frparam('data',1);
if($data!='') {
$pictures = M('pictures')->findAll('id in(' . $data . ')');
var_dump($pictures);
$isall = true;
foreach($pictures as $v){
if(strpos($v['litpic'],'http')===false){
var_dump('.'.$v['litpic']);
}else{
$isall = false;
}
}
}
}
然后先随意输入:
图片.png
看到拼接后执行的语句,然后再来进行构造,因为过滤了单引号和双引号,所以用16进制,构造payload:
图片.png
这样就能先实现任意文件删除 图片.png 图片.png 图片.png