
本靶场的核心是服务器端参数污染 (Server-Side Parameter Pollution, SSPP) ,并通过路径遍历 (Path Traversal) 来实现。
一、核心理论知识
这个实验利用的漏洞链比单一的漏洞要复杂,主要涉及以下几个关键概念:
1. URL 路径注入 (URL Path Injection)
这是整个漏洞链的起点。当服务器接收到一个用户输入(如此处的 username
),并且没有经过充分的净化和编码,就直接将其拼接到一个 URL 路径中去构造一个新的、服务器端的请求时,就产生了路径注入。
-
正常情况 :服务器期望
username
是administrator
,于是它在后端请求的 URL 可能是https://api.internal/v1/users/administrator
。 -
漏洞情况 :如果我们输入
../admin
,且服务器没有对.
和/
进行编码,那么后端的 URL 就会变成https://api.internal/v1/users/../admin
,这可能会让我们访问到https://api.internal/v1/admin
这个意料之外的端点。
2. 服务器端参数污染 (SSPP)
这是本实验的核心。当攻击者能够通过一个输入点影响到服务器向另一个后端服务发起的请求中的多个参数时,就发生了参数污染。
在本实验中,前端只提供了一个 username
输入框,但我们发现的后端内部 API 端点是 /api/internal/v1/users/{username}/field/{field}
,它有两个参数:{username}
和 {field}
。
我们的目标就是用前端这一个 输入框,去控制后端那两个 参数。我们将构造一个恶意的 username
值,例如 administrator/field/passwordResetToken
,服务器在拼接 URL 时,就会把 administrator
赋值给 {username}
,把 passwordResetToken
赋值给 {field}
,从而实现了参数污染。
3. 通过错误信息进行盲探 (Blind Probing via Error Analysis)
由于我们看不到后端的代码,我们需要像侦探一样通过服务器的响应来推断其内部行为。
-
使用特殊字符:
-
#
(URL 片段):URL 中#
后面的内容通常不会发送到服务器。如果添加#
(%23
) 导致响应变化(例如从成功变为"路由无效"),这强烈暗示我们的输入被放在 URL 路径的末尾,#
截断了后面必要的部分。 -
?
(查询参数):?
用于分隔 URL 路径和查询参数。如果添加?
(%3F
) 同样导致路径错误,这也证明了我们的输入点在 URL 路径部分。
-
-
路径遍历探测:
- 使用
../
向上导航。通过观察错误信息的变化(例如从 "Invalid route" 变为 "Not found"),我们可以判断是否已经跳出了应用本身定义的 API 路由,到达了 Web 服务器的根目录级别。
- 使用
4. API 文档发现
一旦我们能通过路径遍历在服务器上"自由移动",一个常见的攻击目标就是寻找隐藏的 API 文档文件,例如 openapi.json
或 swagger.json
。这些文件就像是 API 的蓝图,会暴露大量未公开的、可能存在漏洞的内部 API 端点。
二、靶场具体解题步骤
阶段一:探测注入点并确认漏洞
-
触发功能 :首先,在忘记密码页面输入
administrator
并提交,以捕获一个合法的POST /forgot-password
请求。 -
发送到 Repeater :在 Burp 的
Proxy > HTTP history
中找到该请求,右键发送到 Repeater 以便进行修改和重放。 -
进行盲探 🕵️:
-
将
username
参数的值改为administrator%23
(administrator#
)。发送后,收到Invalid route
错误。推断: 我们的输入在 URL 路径中。 -
将值改为
administrator%3F
(administrator?
)。同样收到Invalid route
错误。再次确认: 输入点在 URL 路径中。 -
将值改为
./administrator
。响应恢复正常。确认: 服务器正在解析路径语法。 -
将值改为
../administrator
。收到Invalid route
错误。这说明路径遍历正在起作用,但可能目标路径不存在。
-
阶段二:路径遍历与 API 发现
我们的目标是向上遍历目录,直到跳出当前 API 的路径范围,然后寻找文档。
-
寻找 API 根目录 :持续在
username
参数前增加../
,并用#
(%23
) 结尾来截断 URL。-
../%23
->Invalid route
-
../../%23
->Invalid route
-
../../../%23
->Invalid route
-
../../../../%23 -> Not found
关键突破! 错误消息从应用的"路由无效"变成了服务器的"未找到"。这说明我们已经成功跳到了 API 路由之外的某个根级别。
-
-
发现 API 文档 🗺️:在这个根级别上,我们尝试访问常见的 API 文档文件名。
-
构造 Payload:
username=../../../../openapi.json%23
-
发送请求后,服务器返回了一条包含新 API 端点的错误信息。你成功发现了一个隐藏的内部 API:
bash/api/internal/v1/users/{username}/field/{field}
-
阶段三:利用参数污染泄露重置令牌
现在我们有了新的武器:一个内部 API 端点。我们要用它来泄露 administrator
的密码重置令牌。
-
探测新端点 :根据发现的结构,我们来污染
username
和field
两个参数。-
Payload 1 :
username=administrator/field/foo%23
-
这会污染后端请求为
/api/internal/v1/users/administrator/field/foo
。 -
服务器返回错误,提示只支持
email
字段。这是非常有用的信息!
-
-
Payload 2 :
username=administrator/field/email%23
- 发送后,响应恢复正常。这证明我们已经可以成功地控制
field
参数了。
- 发送后,响应恢复正常。这证明我们已经可以成功地控制
-
-
寻找有价值的字段 :我们需要一个比
email
更有用的字段。-
回到 Burp 的 HTTP 历史,查看加载的 JS 文件,比如
/static/js/forgotPassword.js
。 -
在 JS 代码中,你会发现密码重置功能引用了一个名为
passwordResetToken
的参数。这就是我们的目标!
-
-
尝试泄露令牌:
-
Payload 3 :
username=administrator/field/passwordResetToken%23
- 发送后,收到一个 API 版本不支持此参数的错误。这又是一个关键线索!说明
passwordResetToken
字段是存在的,只是不在v1
版本中。
- 发送后,收到一个 API 版本不支持此参数的错误。这又是一个关键线索!说明
-
-
最终攻击 Payload 🎯:结合路径遍历和参数污染,我们向上跳出当前错误的 API 版本路径,再进入正确的路径。
-
Final Payload :
username=../../v1/users/administrator/field/passwordResetToken%23
-
逻辑分解 : 假设原始后端请求是
.../internal/v1/users/{我们的输入}
。-
../
从users
上升到v1
。 -
../
从v1
上升到internal
。 -
我们现在位于
/api
级别(根据之前的探测)。 -
v1/users/administrator/field/passwordResetToken
则是我们想要访问的目标路径和参数。
-
-
发送这个请求,服务器响应中会直接包含
administrator
的密码重置令牌!
-
阶段四:获取权限并完成实验
-
重置密码 :复制泄露的令牌 (
passwordResetToken
)。 -
在浏览器中,访问密码重置链接,并附上令牌:
bash/forgot-password?passwordResetToken=ygkf8whspb1xb2j1oj0k5quba64t6517 #泄露的令牌
-
设置一个新密码。
-
登录并删除用户 :使用
administrator
和你的新密码登录,进入管理面板,删除carlos
用户
三、成功通关
