[SUCTF 2019]EasySQL
坚持第五天,又是SQL,看来这几天注入学的很透彻,先尽量手注,得找个时间把sqlmap好好学一学,自动化一点比较省事😜
我的思路
打开界面长这样
直接注入不搞花里胡哨的,先常规步骤
payload=1 //正常按照id查找回显
payload=1' //没有回显,可能存在注入
payload=1'# //也没有回显
这里最后一个没回显判断不是字符型注入,可能就是数字型
payload=2-1 //输出和1一样的结果,确定数字型注入
确定数字型之后,也就不需要引号闭合了,只需要在注入语句后搞个#注释后面的就行了,后面继续探究,可以肯定的是这里把报错输出给关掉了,可能是盲注之类的把,再试一下
payload=1 order by 1#

被拒绝了,看来是有过滤🙄尝试盲注
payload=1 and length(database())>=1#
也是nonono,看来是过滤了,这里究竟过滤了哪些字段,用burpsuite的intruder来fuzz一下过滤字段,这里强烈推荐burp suite的免费版,不要在网上搞那些花里胡哨的版本,一是容易有木马,二是不是正版安装麻烦,三是无法更新,什么pycharm,xshell也是一样,老老实实community他不香吗
拥有了它,你就拥有了最新版功能,专属浏览器抓包,无需设置代理,纯净无捆绑,官方正版,下载即可打开,永久免费,而且以我现在的水平用不到高级版的特殊功能🤪

咳咳,回归正题,看fuzz结果,这里的fuzz字典直接网上找即可

具体看包就知道507是nonono,也就是被过滤了,523是正常返回,500无回显也就是报错,盲注过滤了这些字段是不行的,盲注还没有认真研究过,以后碰到相关问题再研究一下,反正这里就得使用其它的方式,前几天搞了个堆叠注入,不知是否可行,试一试
payload=1;show databases;#
还真可以,这下舒服了,看来堆叠注入适用范围蛮广的嘛,看表,只有一个就叫flag,所以查表必然是这个表

不过这题牛逼了啊,把Flag这个字段给过滤掉了,任何对其的查询都不可用,,这里尝试了show columns和desc两种方式都不行,到这确实没啥思路了,百度一下,似乎大家都是这里卡住了去看大佬的方法,看来大家水平差不多嘛,我也不差👨🎓
大佬思路
去对后端语句进行猜测,猜测依据如下:
通过输入非零数字得到的回显1和输入其余字符得不到回显,由此来判断出内部的查询语句可能存在有或字段
select 输入的数据 或 现有列名 from 表名
即为select $a || flag from Flag
这是怎么猜到的呢,我觉得需要长时间的积累,但是对此的解释我一定要解释清楚,网上其它的wp大家好像水平都很高,对这个或是什么性质都没有解释,对后面那个现有列名怎么来的也没有解释,估计是觉得很简单把,我比较菜,就自己用MYSQL做了个小实验,如下,使用phpstudy中的MySQL直接进行实验
操作后建了如下表

算是满足了题目要求,现在开始验证题目中的或的性质


以下是我的猜测,不一定正确,经过上面的尝试可以发现,MySQL里面的默认||,作用是先将两边的值类型强制转换为bool值类型,非0数字的bool值为1,字符串的bool值为0,和数字0的bool值一样都是0,所以select +非0数字,回显值永远为1,算是“查到了”,而且或在其前面的已经查到的情况下,不会去执行或后面的语句,而或后面的值flag里的字段为字符串值,在bool值转换时被转换为0,所以当前面输入bool值为0的时候,后面也查不到,就会没有回显,这就是除了非零数字,其余输入看不到回显的原因。
知道构造之后就有两种解法
非预期解
原有的语句是
$sql = "select ".$post['query']."||flag from Flag";
这里构造payload
payload=*,1
改变后的结果是
$sql = "select *,1||flag from Flag";
直接一波全注出来了

但既然这题是考察堆叠注入,那应该不是想这么做的,应该是出题人忘记过滤*了,我在这也将 * 加入了fuzz词典里方便以后做题
预期解
其实这题难点在猜语句结构,猜出来了就没难度了,都可以堆叠注入了,那自然有的是办法绕过
在oracle 缺省支持 通过 ‘ || ’ 来实现字符串拼接,但在mysql 缺省不支持。需要调整mysql 的sql_mode模式:pipes_as_concat 来实现oracle 的一些功能
payload=1;set sql_mode=pipes_as_concat;select 1
这样语句就变成了查完1再查flag
select 1;set sql_mode=pipes_as_concat;select 1||flag from Flag
直接读出flag,这里不能使用预编译的原因是首先有长度限制,第二from等关键字也被过滤了,所以不能使用。
源码
可以看到过滤确实十分严格,再严格一点把*和concat都过滤掉,这样就更加安全了,也就没人能做出来了
<?php
session_start();
include_once "config.php";
$post = array();
$get = array();
global $MysqlLink;
//GetPara();
$MysqlLink = mysqli_connect("localhost",$datauser,$datapass);
if(!$MysqlLink){
die("Mysql Connect Error!");
}
$selectDB = mysqli_select_db($MysqlLink,$dataName);
if(!$selectDB){
die("Choose Database Error!");
}
foreach ($_POST as $k=>$v){
if(!empty($v)&&is_string($v)){
$post[$k] = trim(addslashes($v));
}
}
foreach ($_GET as $k=>$v){
}
}
//die();
?>
<html>
<head>
</head>
<body>
<a> Give me your flag, I will tell you if the flag is right. </ a>
<form action="" method="post">
<input type="text" name="query">
<input type="submit">
</form>
</body>
</html>
<?php
if(isset($post['query'])){
$BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"";
//var_dump(preg_match("/{$BlackList}/is",$post['query']));
if(preg_match("/{$BlackList}/is",$post['query'])){
//echo $post['query'];
die("Nonono.");
}
if(strlen($post['query'])>40){
die("Too long.");
}
$sql = "select ".$post['query']."||flag from Flag";
mysqli_multi_query($MysqlLink,$sql);
do{
if($res = mysqli_store_result($MysqlLink)){
while($row = mysqli_fetch_row($res)){
print_r($row);
}
}
}while(@mysqli_next_result($MysqlLink));
}
?>

浙公网安备 33010602011771号