第一次代码审计---消息果CMS

Posted qiuzhiyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第一次代码审计---消息果CMS相关的知识,希望对你有一定的参考价值。

今天选择了一个比较小的CMS---消息果吐槽,尝试审计一下:

源码地址:https://7alk.com/index.php

首先下载并且在虚拟机安装网站:

技术图片

 

 

 技术图片

 

 

 

  安装之后是这样的,先熟悉一下功能:主要功能有留言和评论留言、以及管理员登录对留言进行处理(回复、删除、隐藏),咳!!比较简单,刚开始学习还是先找简单点的练手吧。看到这个留言功能,我就先在心里多念叨了一下XSS

然后来看一下文件目录,这里需要提示的是:一般我们应该先安装完网站再把网站文件在sublime或者审计应用中打开,因为网站在安装的过程中可能会生成新的文件。这是一个小经验吧。

技术图片

 

 

 好吧,就这么大,其中

index.php属于前端显示+入口文件

Page.php 主要是对于留言页码的控制

Cfg.php 主要是基础配置如数据库路径、session赋值、基本路径

App.php 主要存放各种自定义的函数

Mail.php 主要是对于留言者邮箱的处理,支持邮件的发送,但是我安装测试的时候并没有发送邮件这一功能

Ajax.php 主要的控制文件,接受index主页的参数传入,根据传入的get或是post参数的不同进行不同的处理  

 

db文件是空的 应该和数据库有关,但是是空的我就不知道怎么回事了

Style 主要是样式,美化的,不用多看

 

  我感觉审计CMS有一个问题比较难受:就是经常无法将代码和网站的实际位置功能对应起来,就是代码找到了可能有问题的地方,但是实际想去测试却找不到在那个功能点,或者请求哪个网站页面。对此我的办法就是使用burp抓包。

 

我们从主页index.php发送一条留言请求:

技术图片

发现第一个请求文件是ajax.php,然后从这个入口文件开始通读:

 

<?php
require_once ‘app.php‘;
$c = isset($_GET[‘act‘])?$_GET[‘act‘]:‘‘;       //c = act  未过滤
$t = isset($_POST[‘text‘])?$_POST[‘text‘]:‘‘;   //t == text 未过滤
$p = isset($_GET[‘p‘])?intval($_GET[‘p‘]):1;    //intval()这个函数是将参数转化为整型的,有这个函数过滤,这个参数一般就不能进行SQL注入了 后来我查了一下这个参数的绕过,但是比较繁琐会有%00截断,但是绕过无关SQL注入
$id = isset($_GET[‘id‘])?intval($_GET[‘id‘]):0;
$db = new DbHelpClass();
switch ($c) {
	 case "login":
		 $pass = $_POST[‘pass‘];                  //尝试伪造管理员登录?act=login&pass=md5(KEY.$pass)
	     if($config[‘pass‘] == md5(KEY.$pass)){       //$config这个值是可以从数据取得 应该是个固定值  此处应该管理员验证是将post获取的值与数据库取出的值比较,暂时没想到什么利用方法
			$_SESSION[KEY.‘admin‘] = 1; 
		    logmsg(1,‘登录成功‘);
		 }else{
		    logmsg(0,‘登录失败‘);
		 }
		 break;
下面是各种case情形:获取的act参数的值 /app/class/ajax.php?act=logout退出/login登录/vcode验证码/new最新留言/add留言加到数据库/locker隐藏评论/top置顶评论/del删除/mail回复留言

 

 一般我会粗略的读一读这些代码,将可能的地方注释标记,主要侧重点在是否对于参数有过滤、危险函数、数据库操作、身份验证等等

ajax.php开头包含了app.php,翻翻看看全是自定义的一些函数:

function get_config()  //要是没有config值,从数据库取出一个默认的config赋值
function hide()    //判断是否为管理员
function ckadm()   //不是管理员 返回未登录消息
function logmsg()     //服务器返回消息和状态码的函数
function view_ip()   //获取管理员邮箱+ip地址
function view_content()  //管理员登录审核留言
function get_ip()  //从请求头中获取ip地址
function get_captch()  //这个函数应该和验证码相关
function timeago()    //这个是留言计时的  
function arr_sql($tab, $run, $arr)   //往数据库的$tab表中插入或者更新消息 (特别关注可能存在的注入)
class DbHelpClass   //初始化的一个数据库连接操作
function total($sql,$params=array())//求总记录数目
function runsql($sql, $params = array())   //数据库中添加留言 PDO方式
function getdata($sql, $params = array())  //读取数据库留言   PDO方式

  一开始我有一个困惑是$config,这个参数是如何赋值的,因为我观察上面的代码,找了一会才发现,它是从数据库取到的,但是取得过程我们无法控制,也不能传入参数

function get_config()  //要是没有config值,从数据库取出一个赋值
{
    $db = new DbHelpClass();
    if (empty($_SESSION[KEY . ‘config‘])) {
        $rs = $db->getdata("select * from `config` where id=1");
        $config = $rs[0];
        $_SESSION[KEY . ‘config‘] = $rs[0];
    } else {
        $config = $_SESSION[KEY . ‘config‘];
    }
    return $config;
}

  我想去数据库里执行 select * from `config` where id=1  看看config的值是什么格式,但是我用PHPmyadmin打开竟然没有找到数据库,这就神了。。。但是我的留言确实可以持续显示

接着ajax.php往下走:

case "new":     //?act=new&r=
        $list = $db->getdata("select * from `content` order by top desc,lastime desc limit 0,$id"); //乍一看可能存在limit注入,但是想到上面intval($_GET[‘id‘])的过滤,此处就无法利用了  

  下面这个地方尝试绕过XSS,但是没成功:

     case "add":   //?act=add&author=xss
         $arr[‘content‘] = strip_tags(trim($_POST[‘content‘]));   //strip_tags()剥离html标签
         $vcode = strtolower(trim($_POST[‘vcode‘]));		 
		 if($config[‘captch‘] == 1 and $vcode != $_SESSION[KEY . ‘captch‘] and ADMIN==0){
		   logmsg(0,‘验证码错误!‘);
		 }
		 $arr[‘author‘] = ADMIN?$config[‘author‘]:mb_substr(strip_tags(trim($_POST[‘author‘])),0,20,‘utf-8‘);
         if(empty($arr[‘author‘])){$arr[‘author‘] =‘匿名网友‘;}
         $arr[‘mail‘] = strip_tags(trim($_POST[‘mail‘]));  //strip_tags()函数剥除HTML标签  而且邮箱这个地方有长度限制
		 if(!empty($arr[‘mail‘]) && filter_var($arr[‘mail‘], FILTER_VALIDATE_EMAIL)==false){
		   logmsg(0,‘Email格式错误!‘);
		 }
         $arr[‘ip‘] = get_ip(); //ip是从请求头部分获取的
		 $arr[‘lock‘] = ADMIN?0:$config[‘lock‘];   //ip在前端没有找到回显的地方
		 $arr[‘parent‘] = $id;
		 $arr[‘aid‘] = ADMIN;
		 $sql = arr_sql(‘content‘,‘insert‘,$arr);  //content表中插入内容
		 setcookie(‘pname‘,$arr[‘author‘],time()+3600*24*30,‘/‘);
	     setcookie(‘pmail‘,$arr[‘mail‘],time()+3600*24*30,‘/‘);
		 $b =  $db->runsql($sql,$arr);
		 $_SESSION[KEY.‘add‘] = $arr;
		 //$_SESSION[KEY.‘mail‘] = $arr[‘mail‘];
		 if($arr[‘parent‘]>0){$db->runsql("update `content` set lastime=:lastime where id=:id",array(‘lastime‘=>date(‘Y-m-d H:i:s‘),‘id‘=>$id));}  //$id经过了intval()过滤  绝了
		 logmsg($b,$arr);
		 break;

 后面一堆数据库找不到我就很难受,总的来说这次审计做的不太成功,但是确实学到了不少经验,比如拿到一个CMS,如何着手一步步来,怎么使用审计工具查各种参数和函数,各种函数的功能怎么去记忆(好记性不如烂笔头,在自定义函数旁边做注释是个不错的选择),PHP封装好的各种函数(看到陌生的就去菜鸟教程查,一定不能懒,一个地方看不懂,后面流程就会越来越乱,最后就不知道他的函数在做什么了,脑子就会开始烦躁。。。),

 

以上是关于第一次代码审计---消息果CMS的主要内容,如果未能解决你的问题,请参考以下文章

记一次小有成就的代码审计

从一个小众cms入门代码审计

网络安全审计之CMS代码审计

代码审计 熊海cms V1.0

从某cms的xss漏洞来学习代码审计

php代码审计熊海cms1.0