gists

q

攻撃者は、クライアントサイド・パストラバーサルを応用すれば Self XSS を XSS に昇格できるらしいけど、どういう意味?

a

ここで言う「クライアントサイド・パストラバーサルを応用して Self XSS を XSS に昇格」は、だいたい次の意味です。

「昇格」というのは、本来は被害者の能動的な協力が必要だった“コード実行”を、リンク1つ/閲覧だけで起こせる形に変える、ということです。


どう繋がるのか(典型的な成立パターン)

鍵は「クライアント側で ../ などを混ぜて、読み込むべき“スクリプト/HTML/テンプレ/JSON”の場所を攻撃者が変えられる」点です。

1) 前提:フロントが“動的に”何かを読み込む

例(イメージ):

この name/page/langURLパラメータ等から取っていて、正規化・許可リストがないと、../ でパスをずらせます。

2) パストラバーサルで「攻撃者が用意した資産」や「意図しない資産」を読ませる

たとえば

ここで重要なのは、攻撃者が“被害者に何か貼らせる”必要がなく、URLを踏ませるだけで読み込み先を変えられることです。

3) 読み込まれた内容が“実行される流れ”になっているとXSS化

結果として、攻撃者は

  1. どこかに 悪性ペイロード(HTML/JS/JSON)を置く(アップロード、既存の公開ファイル悪用、CDN上の取り込み等)
  2. パストラバーサルを含むURLを被害者に踏ませる
  3. 被害者のブラウザがそれを読み込み、アプリのロジックにより実行される

という「被害者操作不要のXSS」へ持ち込みます。これを「Self XSS → XSSへ昇格」と表現します。


“Self XSS”と言っているけど、実際は何がSelfなの?

この手の話で「Self XSS」と呼ばれているものは、厳密には次のどちらかが多いです。


まとめ(1行で)

クライアントサイド・パストラバーサルによって「どの資産を読み込む/表示するか」を攻撃者がURLだけで操作でき、かつその資産がアプリの都合で“実行される”経路に乗ると、被害者の協力が必要だったSelf XSSが、リンク踏ませるだけのXSSに変わる、という意味です。


もし、対象が「SPAでのテンプレ読み込み」「i18n JSONの動的ロード」「dynamic import」「Electron」など、どのパターンか分かれば、典型コード例と“どこが境界突破点になるか(allowlist/正規化/innerHTML等)”をそのケースに合わせて整理できます。

q

え、わからない

これってユーザーが入力フォームとかに /uploads/evil.json を入れないといけないってことだよね? 入力フォームのあるページにアクセスしただけで /uploads/evil.json にアクセスさせるわけじゃないよね?

a

「入力フォームに手で打ち込まないと無理なのでは?」という疑問はもっともで、ポイントはここです:

以下、成立する典型パターンを具体的に分けて説明します。


1) URLパラメータ由来(リンクを踏むだけで発火)

SPAや一部の画面は、URLから状態を復元します。

例:アプリがこういう実装だとします:

const lang = new URL(location.href).searchParams.get("lang") || "en";
fetch(`/i18n/${lang}.json`).then(/* ... */);

このとき攻撃者は被害者に次のリンクを踏ませるだけです:

https://example.com/app?lang=../../uploads/evil

するとブラウザは(少なくとも要求としては):

を投げます。サーバやフレームワークの正規化次第で、実質

を取りに行けてしまうことがあります。

ここではフォーム入力は不要で、「リンクを開いただけ」で成立します。


2) URLの #(ハッシュ)由来(これもリンクだけで発火)

SPAは /#/page みたいなハッシュルーティングを使います。

const page = location.hash.slice(2); // "#/xxxx" を読む
fetch(`/templates/${page}.html`).then(r => r.text()).then(html => {
  container.innerHTML = html;
});

攻撃者リンク:

https://example.com/#/../../uploads/evil

→ ページ表示だけで fetch("/templates/../../uploads/evil.html") が走り得ます。


3) localStorage / 設定値由来(“元はSelfっぽい”が昇格する典型)

「コンソールで localStorage.lang=... を入れたら変なファイルを読めた」というのは、たしかに最初は Self XSS っぽいです。

でも、もしアプリに

みたいなのがあると、攻撃者はリンク一発で 被害者のlocalStorageに値を保存させられます。

例:

const lang = new URL(location.href).searchParams.get("lang");
if (lang) localStorage.setItem("lang", lang);

const l = localStorage.getItem("lang") || "en";
fetch(`/i18n/${l}.json`);

攻撃者が ?lang=../../uploads/evil のリンクを踏ませる
→ 被害者は一切入力してないのに、次回以降も勝手にそれを読むようになる

こういう「URL→保存→読み込み」の導線があると、“自分で入れた場合だけ起きる”が崩れてSelf性が消えます


4) 「入力フォームが必要」なケースもある(ただしそれだけでは終わらない)

もちろん実装によっては、あなたの言う通り

みたいな作りもあります。その場合、それ単体だと「被害者が入力しないと起きない」のでXSSとしては弱くなりがちです。

ただ現実には、次のどれかがあると「入力不要」に寄ります:


重要:/uploads/evil.json を「読ませる」だけでは、まだXSSにならない

あなたの疑問の延長で誤解しやすい点ですが、

XSSになるのは、そのJSON等が後段で

など、「実行される経路」に乗っているときです。


確認したいこと(これが分かると“入力不要で発火するか”が判定できます)

次のどれに近いですか?

  1. lang/page/template みたいな値は URLパラメータ or ハッシュから読んでますか?
  2. その値で fetch() / import() / innerHTML などをしていますか?
  3. そのロードは ページ表示直後に自動で走る作りですか?(ボタン押下が必要?)

該当するコード断片(URLから読むところと、fetch/import/innerHTMLしてるところ)を貼ってくれれば、「入力フォームが必要か」「リンクだけで行けるか」「XSSまで行く条件は何か」をあなたのケースで具体的に整理します。