Flatt Security Blog

株式会社Flatt Securityの公式ブログです。プロダクト開発やプロダクトセキュリティに関する技術的な知見・トレンドを伝える記事を発信しています。

株式会社Flatt Securityの公式ブログです。
プロダクト開発やプロダクトセキュリティに関する技術的な知見・トレンドを伝える記事を発信しています。

Flatt Security Developers' Quiz #4 解説

はじめに

下記のTweetで出題させていただいた、Flatt Security Developers' Quiz #4にご参加いただきありがとうございました!

景品の獲得条件を満たした方には追ってメールでご連絡を差し上げますので、ご確認いただけますと幸いです。なお、景品獲得条件を満たさなかった方にはご連絡いたしません。ご了承ください。

今回のクイズでFlatt Securityに興味を持ってくださった方は是非下記のバナーよりサービス詳細をご覧ください。

Flatt Securityのセキュリティ診断

今回のクイズの確認

ソースコード

4-1

<html>

<head>
   <meta charset="utf-8">
   <title>リダイレクトページ</title>
</head>

<body>
    <form>
        <label for="url">リダイレクト先のURLを入力してください:</label>
        <input type="text" name="url" placeholder="https://example.com" id="url">
        <input type="submit">
    </form>
    <p id="flag">FLATT{CAN_YOU_GET_ME?}</p>
    <script>
       const params = new URLSearchParams(location.search);
       const url = params.get("url");
       if (url) {
           location.href = url;
       }
   </script>
</body>

</html>

4-2

<html>

<head>
   <meta charset="utf-8">
   <title>リダイレクトページ 2</title>
</head>

<body>
    <form>
        <label for="url">リダイレクト先を入力してください (static.twitter-quiz.flatt.training内のURLを指定してください!): </label>
        <input type="text" name="url" placeholder="https://static.twitter-quiz.flatt.training/" id="url">
        <input type="submit">
    </form>
    <p id="flag">FLATT{YOU_CANT_GET_ME!}</p>
    <script>
       const params = new URLSearchParams(location.search);
       const url = params.get("url");
       if (url) {
           try {
               const parsed = new URL(url);
               if (parsed.origin === window.origin) {
                   location.href = url;
               }
           } catch (_) { }
       }
   </script>
</body>

</html>

デモ環境

出題形式

HTMLの

タグの内容をalertで表示する方法を解答する。

難易度

4-1は開発者にとって「かんたん」、4-2は「難しい」という想定で設定しました。

解答例

4-1

javascript:alert(window.flag.innerText)という値をurlパラメーターに対して渡す、もしくはフォームから送信することで alert(window.flag.innerText)を実行し、ページ上からフラグを取得することが出来ます。

https://static.twitter-quiz.flatt.training/2208leopald-7b9260bb-1327-45b0-baa5-c67570436525/quiz1.html?url=javascript:alert(window.flag.innerText)

4-2

以下のようなHTMLを作成し、開いた際に表示されるClick me!というリンクをクリックすることで、alert(window.flag.innerText)static.twitter-quiz.flatt.training上で実行することが出来ます。

<html>

<head>
   <meta charset="utf-8">
   <title>4-2 回答</title>
</head>

<body>
    <script>
       const iframe = document.createElement("iframe");
       iframe.sandbox = "allow-modals allow-scripts allow-popups";
       iframe.srcdoc = '<a href="https://static.twitter-quiz.flatt.training/2208leopald-7b9260bb-1327-45b0-baa5-c67570436525/quiz2.html?url=javascript:alert(window.flag.innerText)" target="_blank">Click me!</a>';
       document.body.appendChild(iframe);
   </script>
</body>

</html>

解説

4-1

この問題は、任意の値をlocation.hrefに代入できるという状態で、XSSを行うという問題です。
解法としては、javascript:...といったようなjavascriptスキームを使用し、alert(window.flag.innerText)を実行するといった形のものになります。

HTML Living Standardで定められているように、現在主に使用されているブラウザは、ナビゲーションが発生した際にナビゲート先のスキームがjavascriptであれば、JavaScriptを実行するという仕様となっています。
これにより、location.hrefaタグのhref属性等に任意の値が指定できる場合、XSSを行うことが可能となります。

4-2

こちらの問題は4-1と非常に似ているのですが、window.originurlパラメーターの値をパースした際のoriginが同一である場合にのみ、location.hrefに対する代入が発生するという条件が追加されています。
この問題の解法は少し複雑で、sandbox属性を付与したiframeの中から、問題のURLをurlパラメーターにjavascript:alert(window.flag.innerText)という値を指定した状態で開かせることにより、alert(window.flag.innerText)を実行することが出来ます。

内部的には以下のような理由でXSSが可能となっています。

sandbox属性が付与されたiframe内のドキュメントはopaque originと呼ばれるオリジンを持つようになり、これはシリアライズされた際に"null"となります。
この仕様により、sandbox属性を指定したiframe内に問題のページを読み込ませることで、window.origin"null" とすることができます。

そして、new URLjavascript:alert(window.flag.innerText)をパースした際のoriginも同様に"null"となるため、parsed.origin === window.origintrueになり、location.hrefに対してjavascript:alert(window.flag.innerText)を渡すことができるようになります。
しかしながら、サンドボックス状態のiframe内で発生したjavascript:...に対する遷移においてはJavaScriptを正常に実行することができません。1

ただし、allow-popups-to-escape-sandboxsandbox属性で指定しなければ、サンドボックス状態のiframe内から開かれたページもsandboxの制限を受けるという仕様となっており、かつGoogle Chromeにおいてはサンドボックス状態のウィンドウであったとしても、サンドボックス状態のiframeから新しいウィンドウを開くことにより、javascript:...に対するページ遷移によるJavaScriptの実行が可能であるという挙動があるため、サンドボックス化されたiframe内から新しいウィンドウを開き、そこで問題のページを読み込ませることによりalert(window.flag.innerText)を実行することができるようになります。

また、MDN web docsに記載のある通り、この状態のオリジンは同一オリジンポリシーの検証に常に失敗するようになるため、ここで実行されているJavaScriptから直接Cookieやローカルストレージへのアクセス等を行うことはできません。 (ただし、ページへ遷移した際にCookieが送信されているため、潜在的に漏洩してはいけない情報等を取得できる可能性があります。)

なお、この仕様はWHATWGにおいても過去に議論の対象となっています: https://github.com/whatwg/html/issues/3585

今回のような脆弱性への対策方法

今回の問題で取り扱った脆弱性は、モダンなフレームワークを使用していたとしても作り込んでしまう可能性があるものとなっています。
ユーザーの入力を受け取ってリダイレクトを行う必要がある場合は、リダイレクト先のURLのスキームが安全なものかを検証することで、XSSの発生を防ぐことが出来ます。

終わりに

Quizにご参加いただいた皆様、ありがとうございました。不定期ではありますがよりみなさまに楽しみながらセキュリティを学んでいただけるよう今後も発信を続けてまいります。

我々株式会社Flatt Securityはセキュリティ診断サービスを提供しています。

Webアプリケーションやスマートフォンアプリケーションを対象に、セキュリティエンジニアによる手動診断によって高い精度で脆弱性を洗い出すことが可能です。 ツールによる診断しか過去実施しておらず認証や決済といった重要な機能のセキュリティに不安があったり、既存のベンダーとは違う会社に依頼したいと考えていたりする方はお気軽にご相談ください。

数百万円からスタートの大掛かりなものばかりを想像されるかもしれませんが、上記のデータが示すように、診断は幅広いご予算帯に応じて実施が可能です。ご興味のある方向けに下記バナーより料金に関する資料もダウンロード可能です。

また、Flatt Securityはセキュリティに関する様々な発信を行っています。 最新情報を見逃さないよう、公式Twitterのフォローをぜひお願いします!

twitter.com

ここまでお読みいただきありがとうございました。


  1. どの仕様に影響されてこの挙動となっているか作問者は確認できなかったため、ご存知の方がいましたらご一報いただけると幸いです!