Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Brave Browserの脆弱性を見つけた話(iOS編)
Search
MUNEAKI NISHIMURA
July 25, 2023
Technology
3.1k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Brave Browserの脆弱性を見つけた話(iOS編)
Shibuya.XSS techtalk #12の発表資料です。
MUNEAKI NISHIMURA
July 25, 2023
More Decks by MUNEAKI NISHIMURA
See All by MUNEAKI NISHIMURA
脆弱星に導かれて
nishimunea
3
3k
ブラウザの脆弱性とそのインパクト
nishimunea
26
10k
脆弱性発見者が注目する近年のWeb技術
nishimunea
29
13k
脆弱性発見者の目から見た、脆弱性対応の最前線
nishimunea
15
2.8k
Slack Team for Security Testers and Bug Hunters
nishimunea
1
860
Finding Vulnerabilities in Firefox for iOS
nishimunea
3
9.2k
SWIFT Code for Mozilla Bank
nishimunea
1
1k
次世代プラットフォームのセキュリティモデル考察
nishimunea
6
5.8k
Other Decks in Technology
See All in Technology
現場で役立つ技術負債の効果的な返済方法
masuda220
PRO
9
4.4k
TinyGo 開発サイクルを高速化する:Go で作るエミュレータ入門
zozotech
PRO
1
740
AI 時代のスタートアップエコシステ厶から考究する技術的負債との向き合い方
m3m0r7
PRO
3
2.2k
Claude Code本って、 読む必要あるの?
oikon48
2
470
AI時代、データエンジニアが一番おもろい
genshun9
0
660
顧客に向き合う開発組織へ。リアーキテクチャとフィーチャーチーム化で挑む組織改革
safie
0
2k
GoのInterface内部構造から学ぶ!最高パフォーマンスを出すコード設計
yappli_developers
0
130
AI に書かせたその API、 “信頼” できますか?
nagix
0
120
HRC_Frontend_Conference_Fukuoka_2026.pdf
ts020
0
740
山手線を徒歩で一周してわかった、 位置情報アプリは「足」が最強のデバッガー
hinakko
0
160
幾何アルゴリズムで なめらかなピン操作を / iOSDC Japan 2026 / smoothpin
kazumanagano
0
350
DEFCON_CHV_CTF_Write-up.pdf
bata_24
0
150
Featured
See All Featured
Discover your Explorer Soul
emna__ayadi
2
1.3k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
510
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
520
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.4k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
750
Mobile First: as difficult as doing things right
swwweet
225
10k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
940
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
3
4.3k
How STYLIGHT went responsive
nonsquared
100
6.3k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
500
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.8k
Marketing to machines
jonoalderson
1
5.8k
Transcript
Brave Browserの脆弱性を⾒つけた話(iOS編) Shibuya.XSS techtalk #12 Muneaki Nishimura (nishimunea)
⾃⼰紹介 ⻄村 宗晃(にしむねあ) ブラウザの脆弱性を探すのが⼤好き 好きなブラウザはFirefox https://www.slideshare.net/codeblue_jp/firefox-by https://files.speakerdeck.com/presentations/3cf321f5ee4d4aaba9f18305ee2a461c/SWIFT_Code_for_Mozilla_Bank__nishimunea_.pdf 1
Braveとは︖ JavaScriptの⽣みの親 Brendan Eich⽒が 開発したオープンソースのWebブラウザ 広告ブロック、暗号資産連携、Torを⽤いた プライベートブラウズモード等の野⼼的な 機能を多数搭載 2
Braveの脆弱性を探し始めた経緯 1/3 僕もブラウザの脆弱性を 探してみたいのですが… 最初は簡単そうなところ が良いね。HackerOneに Braveってブラウザが あったけどどうかな︖ インターン⽣ わし
きっかけは当時、弊社に来ていたインターン⽣との会話 3
Braveの脆弱性を探し始めた経緯 2/3 思い付きで他⼈に勧めるのも良くないので、試しに⾃分でも探してみたところ… ちょっと⾃分でも 探してみるか… わし 4
Braveの脆弱性を探し始めた経緯 3/3 気付いたら、誰よりもBraveの脆弱性探してるマンになっていた(発表時点) https://hackerone.com/brave/thanks わし 5
本⽇は⾒つけた脆弱性を3つ紹介 6 XSS関連
リーダーモードに存在したXSS
リーダーモードとは︖ Webページから余計なものを削除して、本⽂を読みやすくしてくれる機能 押す 8
Braveアプリ内の HTMLテンプレート リーダーモードの仕組み 1/2 元ページからタイトル、クレジット、本⽂を抽出し、Braveがアプリ内に保持する HTMLテンプレートに埋め込む タイトル クレジット 本⽂ 9
Braveアプリ内の HTMLテンプレートに 埋め込んだ結果 リーダーモードの仕組み 2/2 埋め込んだ結果をアプリ内のローカルHTTPサーバから配信して表⽰ 表⽰時にはCSP(Content Security Policy)を付与することによりXSSを防⽌ XSS防⽌のためCSPを付与
script-src: 'nonce-XXXXX' 10
⾒つけた脆弱性①︓CSPのnonce制限を迂回したXSS 元ページからクレジットを埋め込む箇所にHTMLインジェクションがあった しかしCSP(script-srcのnonce制限)が効いているためXSSは困難… 著者名に<s>タグを挿⼊ 11 https://hackerone.com/reports/1436142 <s>タグが効いてる
CSPのnonceはどのように埋め込まれるのか︖ HTMLテンプレート内に埋め込まれた特殊⽂字列をNative側(Swift)で置換する 12 https://github.com/brave/brave-ios/pull/4209/files#diff-eaeef15a290e9e5e9bcaae784f18d874f8c932dfa3de416a5820eccd6b2d8cfbR54 <h1 id="reader-title"></h1> <div>%READER-CREDITS%</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById("reader-title").textContent
= "%READER-TITLE%"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of: "%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) テンプレート内の特殊⽂字列を置換するSwiftコード
HTMLテンプレート置換処理の実際の動き 1/8 はじめに元のページのタイトルが埋め込まれる 13 .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of: "%READER-CREDITS%",
with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) テンプレート内の特殊⽂字列を置換するSwiftコード <h1 id="reader-title"></h1> <div>%READER-CREDITS%</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById("reader-title").textContent = "%READER-TITLE%"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き 2/8 はじめに元のページのタイトルが埋め込まれる 14 <h1 id="reader-title"></h1> <div>%READER-CREDITS%</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent
= "Title"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of: "%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) テンプレート内の特殊⽂字列を置換するSwiftコード
HTMLテンプレート置換処理の実際の動き 3/8 次に元のページのクレジットが埋め込まれる 15 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div>%READER-CREDITS%</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き 4/8 次に元のページのクレジットが埋め込まれる 16 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div>Credit</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き 5/8 続いて元のページの本⽂が埋め込まれて… 17 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div>Credit</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き 6/8 続いて元のページの本⽂が埋め込まれて… 18 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div>Credit</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> Contents </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き 7/8 最後にランダムなnonceの値が埋め込まれる 19 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div>Credit</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> Contents </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き 8/8 最後にランダムなnonceの値が埋め込まれる 20 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div>Credit</div> <script nonce="80841642004944135718143322189292"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> Contents </div> HTMLテンプレート
Braveアプリ内の HTMLテンプレート お気づきになりましたか… scriptタグのnonceに「%READER-TITLE-NONCE%」という⽂字列を指定すると、 Braveが正しいnonceに置き換えてくれる %READER-TITLE-NONCE% タイトル 本⽂ 21 https://hackerone.com/reports/1436142
HTMLテンプレート置換処理の実際の動き(再掲) 1/8 はじめに元のページのタイトルが埋め込まれる 22 .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of: "%READER-CREDITS%",
with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) テンプレート内の特殊⽂字列を置換するSwiftコード <h1 id="reader-title"></h1> <div>%READER-CREDITS%</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById("reader-title").textContent = "%READER-TITLE%"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き(再掲) 2/8 はじめに元のページのタイトルが埋め込まれる 23 <h1 id="reader-title"></h1> <div>%READER-CREDITS%</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent
= "Title"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of: "%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) テンプレート内の特殊⽂字列を置換するSwiftコード
HTMLテンプレート置換処理の実際の動き(再掲) 3/8 次に元のページのクレジットが埋め込まれる 24 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div>%READER-CREDITS%</div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き(再掲) 4/8 次に元のページのクレジットが埋め込まれる 25 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div> </div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き(再掲) 5/8 続いて元のページの本⽂が埋め込まれて… 26 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div> </div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> %READER-CONTENT% </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き(再掲) 6/8 続いて元のページの本⽂が埋め込まれて… 27 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div> </div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> Contents </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き(再掲) 7/8 最後にランダムなnonceの値が埋め込まれる 28 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div> %READER-TITLE-NONCE% </div> <script nonce="%READER-TITLE-NONCE%"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> Contents </div> HTMLテンプレート
HTMLテンプレート置換処理の実際の動き(再掲) 8/8 最後にランダムなnonceの値が埋め込まれる 29 テンプレート内の特殊⽂字列を置換するSwiftコード .replacingOccurrences(of: "%READER-TITLE%", with: readabilityResult.title) .replacingOccurrences(of:
"%READER-CREDITS%", with: readabilityResult.credits) .replacingOccurrences(of: "%READER-CONTENT%", with: readabilityResult.content) .replacingOccurrences(of: "%READER-TITLE-NONCE%", with: titleNonce) <h1 id="reader-title"></h1> <div> 80841642004944135718143322189292 </div> <script nonce="80841642004944135718143322189292"> document.getElementById(“reader-title”).textContent = "Title"; </script> <div> Contents </div> HTMLテンプレート
30
様々なUniversal XSS
Universal XSS(UXSS)とは︖ 対象のサイトにXSSの脆弱性が無くてもXSSで攻撃できてしまうブラウザの脆弱性 SOPに代表されるWebのセキュリティモデルが損なうため深刻度が⾼い 32 https://www.mozilla.org/en-US/security/client-bug-bounty/ Mozillaでは深刻度最⾼の脆弱性 (報奨⾦ $18,000)
iOS版ブラウザアプリによくあるUXSS evaluateJavascript関数(後述)に起因するUXSSが発⽣しやすい 過去にChromiumでも発⾒し、$10,000の報奨⾦を得たことがある 33 https://bugs.chromium.org/p/chromium/issues/detail?id=1164846 evaluateJavascriptに起因するUXSS
evaluateJavascript関数とは︖ 1/2 iOSのブラウザアプリは全てWebページの表⽰にWKWebViewを使⽤している WKWebViewにはNative(Swift)⇔ Web(JS)間でやり取りする仕組みがある 34 iOSのブラウザアプリ Native(Swift) WKWebView(JS)
evaluateJavascript関数とは︖ 2/2 WebからNative側への通信には webkit.messageHandlers を使⽤する Native側からWebへの通信に使うのが evaluateJavascript 関数 35 iOSのブラウザアプリ
Native(Swift) WKWebView(JS) Native(Swift)からWeb(JS)への通信に利⽤
iOS版ブラウザアプリによくあるUXSSの仕組み iframe内のページから不正なデータをNative側に送ることにより、 evaluateJavascriptを通じて親フレーム上で任意のJSを実⾏させることができる 36 iOSのブラウザアプリ Native(Swift) WKWebView(JS) '+alert(1)+' 親フレーム(異なるサイト)上で実⾏される iframe内のページから不正なデータを送ると
動的にJSが⽣成されて
⾒つけた脆弱性②︓FIDO U2F機能によるUXSS 1/3 iframe内のページからFIDO U2Fの機能を介して不正なデータを送ることにより、 親フレーム上で任意のJSを実⾏させることができるというもの(UXSS) 37 https://hackerone.com/reports/993670
⾒つけた脆弱性②︓FIDO U2F機能によるUXSS 2/3 実⾏するJSのコードを⽂字列結合で組み⽴てており、さらにこの中で versionという変数はWebサイト側から取得した値であった 38 https://github.com/brave/brave-ios/blob/d01b8c07b8a6244af48798efe4afeccd266707e2/Client/WebAuthN/U2FExtensions.swift#L1003 実⾏するJSのコードを⽂字列結合で組み⽴てている (変数versionはWebサイト側から取得した値)
⾒つけた脆弱性②︓FIDO U2F機能によるUXSS 3/3 FIDOの認証デバイスを登録する関数の引数に不正なversionを指定することにより evaluateJavascriptを通じて親フレームのページ上で任意のJSが実⾏可能 39 https://hackerone.com/reports/993670 Braveブラウザ Native(Swift) WKWebView(JS)
'+alert(1)+' '+alert(1)+' 親フレーム上でJSが実⾏される
40
41
⾒つけた脆弱性③︓Playlist機能によるUXSS 1/2 同様にWebサイトの動画をプレイリストに追加する機能にも同様の実装があり 変数 nodeTag を通じてUXSSが可能であった 42 https://hackerone.com/reports/1436558 変数nodeTagはWebサイト側から取得した値
⾒つけた脆弱性③︓Playlist機能によるUXSS 2/2 Googleサイト(sites.google.com)でサイトを構築し、攻撃⽤のページを フレームの中に埋め込むと… 43 https://sites.google.com/view/nishimunea-brave-uxss1/page 攻撃⽤のページをフレームに埋め込む
44
最後に
謝辞 今回の発表に際して、報告した脆弱性のうち13件の公開許可を頂きました ご対応頂いたBrave開発者の皆様に御礼を申し上げます 46 https://hackerone.com/nishimunea?type=user
Thanks!