Slide 1

Slide 1 text

CTFのWebにおける⾼難易度問題について 2025-03-20 魔⼥のお茶会 #7 おふらいん!(2025 冬)

Slide 2

Slide 2 text

hamayanhamayan @hamayanhamayan ⽇曜 Webセキュリティ愛好家 コーポレートセキュリティ本業 最近は Crypto 頑張ってます Boot2Rootもそこそこやっていました #   whoami ブログ

Slide 3

Slide 3 text

最近の趣味、問題集め #

Slide 4

Slide 4 text

↑独⾃にDifficultyをつけてみています 最近の趣味、問題集め #

Slide 5

Slide 5 text

難易度ピラミッド かじってる⼈ほぼ全員(80-100%) CTFに初めて出る場合でもギリギリ解けそうな問題、フラグを得る楽しさを気軽に実感 baby ※ 個⼈の⾒解で、後付けです easy webは頑張るぞという⼈が結構解ける(40-80%) 1つの脆弱性を⽐較的単純な⽅法で使⽤する、⽐較的解法がはっきり⾒えるもの medium webに⾃信がある⼈は解ける場合がある(2-40%) いくつかの脆弱性を組み合わせて攻撃を成功させる、広い調査が必要なもの hard トップランカーのみ解ける(0.1-2%) ミドルウェアや攻撃⼿法に対する深い理解を要求、実際の実装を確認して攻撃を組み⽴て る、思考の⾶躍が必要、仕様/ドキュメントの通読が必要 insane web神しか解けない(0-0.1%) インターネットでは簡単に⾒つからない新しいアプローチ。解ける⼈がいるかどうか #   最近の趣味、問題集め ##   難易度ピラミッド

Slide 6

Slide 6 text

難易度ピラミッド かじってる⼈ほぼ全員(80-100%) CTFに初めて出る場合でもギリギリ解けそうな問題、フラグを得る楽しさを気軽に実感 baby ※ 個⼈の⾒解で、後付けです easy webは頑張るぞという⼈が結構解ける(40-80%) 1つの脆弱性を⽐較的単純な⽅法で使⽤する、⽐較的解法がはっきり⾒えるもの medium webに⾃信がある⼈は解ける場合がある(2-40%) いくつかの脆弱性を組み合わせて攻撃を成功させる、広い調査が必要なもの hard トップランカーのみ解ける(0.1-2%) ミドルウェアや攻撃⼿法に対する深い理解を要求、実際の実装を確認して攻撃を組み⽴て る、思考の⾶躍が必要、仕様/ドキュメントの通読が必要 insane web神しか解けない(0-0.1%) インターネットでは簡単に⾒つからない新しいアプローチ。解ける⼈がいるかどうか #   最近の趣味、問題集め ##   難易度ピラミッド ボリュームゾーン (CTF難しいので…)

Slide 7

Slide 7 text

難易度ピラミッド ※ 個⼈の⾒解で、後付けです hard トップランカーのみ解ける(0.1-2%) ミドルウェアや攻撃⼿法に対する深い理解を要求、実際の実装を確認して攻撃を組み⽴て る、思考の⾶躍が必要、仕様/ドキュメントの通読が必要 insane web神しか解けない(0-0.1%) インターネットでは簡単に⾒つからない新しいアプローチ。解ける⼈がいるかどうか #   最近の趣味、問題集め ##   難易度ピラミッド ⾼難易度問題の雰囲気や ⾯⽩い部分を紹介します! medium webに⾃信がある⼈は解ける場合がある(2-40%) いくつかの脆弱性を組み合わせて攻撃を成功させる、広い調査が必要なもの

Slide 8

Slide 8 text

仕様?悪⽤? # 細かく、深く # 天才発想 # それぞれの観点について、いくつか問題を抜粋しながら深堀りしていきます。 問題全体の解説はせず、⾯⽩い部分のみ抜粋して話していきます。 #   最近の趣味、問題集め ##   今⽇のもくじ

Slide 9

Slide 9 text

#   最近の趣味、問題集め ##   ちなみに…

Slide 10

Slide 10 text

※ 本⽇、お持ち帰りいただけます 😊 #   最近の趣味、問題集め ##   ちなみに…

Slide 11

Slide 11 text

仕様?悪⽤? #

Slide 12

Slide 12 text

medium TSG CTF 2024 - I Have Been Pwned https://blog.hamayanhamayan.com/entry/2024/12/15/201408#web-I-Have-Been-Pwned hard Nowruz 1404 CTF - 🌱 FMCTFの公式Discordより insane AlpacaHack Round 7 (Web) - disconnection-revenge https://dimas0305.notion.site/Bypassing-null-Origin-in-4xx-Status-Code-Using-Iframe-di sconnection-revenge-Writeup-AlpacaHack-R-14e48583e65d80e6b8d5c53f07905d97 #   仕様?悪⽤? 時間が無く、省略

Slide 13

Slide 13 text

TSG CTF 2024 - I Have Been Pwned ## medium #   仕様?悪⽤?

Slide 14

Slide 14 text

medium // index.php $hash = password_hash($pepper1 . $_POST["auth"] . $_POST["password"] . $pepper2, PASSWORD_BCRYPT); この1⾏に脆弱点が隠されています… #   仕様?悪⽤? ##   TSG CTF 2024 - I Have Been Pwned

Slide 15

Slide 15 text

medium // index.php $hash = password_hash($pepper1 . $_POST["auth"] . $_POST["password"] . $pepper2, PASSWORD_BCRYPT); この1⾏に脆弱点が隠されています… ここ! 公式ドキュメントを⾒てみましょう。 https://www.php.net/manual/ja/function.password-hash.php #   仕様?悪⽤? ##   TSG CTF 2024 - I Have Been Pwned

Slide 16

Slide 16 text

medium 全CTFプレイヤーが好きな⽂字 公式ドキュメントを読もう! (RFCやHTML仕様も) // index.php $hash = password_hash($pepper1 . $_POST["auth"] . $_POST["password"] . $pepper2, PASSWORD_BCRYPT); この1⾏に脆弱点が隠されています… ここ! 公式ドキュメントを⾒てみましょう。 https://www.php.net/manual/ja/function.password-hash.php この「切り詰め」により$pepper2が特定できます #   仕様?悪⽤? ##   TSG CTF 2024 - I Have Been Pwned

Slide 17

Slide 17 text

Nowruz 1404 CTF - 🌱 ## #   仕様?悪⽤? hard

Slide 18

Slide 18 text

hard
let p = ((new URLSearchParams(location.search)).get('p') ?? '')+`🌱` DOMPurify.sanitize(p) if(!DOMPurify.removed.length) xss.innerHTML = p ※ コンパクトにするため改変済み 普通のDOMPurifyの使い⽅ではないですね。まずは、DOMPurifyの公式レポジトリ を⾒てみましょう! #   仕様?悪⽤? ##   Nowruz 1404 CTF - 🌱

Slide 19

Slide 19 text

hard let p = ((new URLSearchParams(location.search)).get('p') ?? '')+`🌱` DOMPurify.sanitize(p) if(!DOMPurify.removed.length) xss.innerHTML = p (更にコンパクトにしました。)公式レポジトリにある使い⽅はこう。 const clean = DOMPurify.sanitize(dirty); sanitizeの戻り値を使うのが公式の使い⽅。README.mdを隅々まで⾒ると… #   仕様?悪⽤? ##   Nowruz 1404 CTF - 🌱

Slide 20

Slide 20 text

hard let p = ((new URLSearchParams(location.search)).get('p') ?? '')+`🌱` DOMPurify.sanitize(p) if(!DOMPurify.removed.length) xss.innerHTML = p (更にコンパクトにしました。)公式レポジトリにある使い⽅はこう。 const clean = DOMPurify.sanitize(dirty); sanitizeの戻り値を使うのが公式の使い⽅。README.mdを隅々まで⾒ると… #   仕様?悪⽤? ##   Nowruz 1404 CTF - 🌱 removedをチェックに「使ってはならない」とかいてある

Slide 21

Slide 21 text

hard let p = ((new URLSearchParams(location.search)).get('p') ?? '')+`🌱` DOMPurify.sanitize(p) if(!DOMPurify.removed.length) xss.innerHTML = p (更にコンパクトにしました。)公式レポジトリにある使い⽅はこう。 const clean = DOMPurify.sanitize(dirty); sanitizeの戻り値を使うのが公式の使い⽅。README.mdを隅々まで⾒ると… #   仕様?悪⽤? ##   Nowruz 1404 CTF - 🌱 全CTFプレイヤーが好きな⽂字 ②

Slide 22

Slide 22 text

hard 裏を返すと、 DOMPurify.removed.length を使った判定は脆弱らしい。 更にレポジトリのissueを辿っていくと、核⼼的な情報が⾒つかる。 DOMPurify.removed isn't reporting inline scripts with arbitrary text afterwards https://github.com/cure53/DOMPurify/issues/988 これは… 修正無しでclose! こんな素晴らしい⾮⾃明挙動が! #   仕様?悪⽤? ##   Nowruz 1404 CTF - 🌱 scriptタグが消えているのに removedに出てこない!

Slide 23

Slide 23 text

hard let p = ((new URLSearchParams(location.search)).get('p') ?? '')+`🌱` DOMPurify.sanitize(p) if(!DOMPurify.removed.length) xss.innerHTML = p となると、以下のようなpayloadを試したくなります。 alert(origin); …が、これは動きません。何故かというと、「仕様」に書いてあるためです! HTML5 では innerHTML で挿入された タグは実行するべきではないと定義 しているからです。 https://developer.mozilla.org/ja/docs/Web/API/Element/innerHTML#%E3%82% BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%81% AE%E8%80%83%E6%85%AE%E4%BA%8B%E9%A0%85 #   仕様?悪⽤? ##   Nowruz 1404 CTF - 🌱

Slide 24

Slide 24 text

hard /*</noscript><img src onerror=alert(origin)>*/ これが答えです。サニタイズするときは <noscript><style>/*</noscript><img src onerror=alert(origin)>*/ のように⾒られますが、実際は <noscript><style>/*</noscript><img src onerror=alert(origin)>*/ のように使われるので、XSSになります。 #   仕様?悪⽤? ##   Nowruz 1404 CTF - 🌱

Slide 25

Slide 25 text

hard #   仕様?悪⽤? ##   Nowruz 1404 CTF - 🌱 DOMPurifyでは処理前にこれが通される。 https://github.com/cure53/DOMPurify/blob/e9035b1ca597d02 97bd823db2a33886ad0d82e46/src/purify.ts#L915

Slide 26

Slide 26 text

‧仕様から warning, caution, security, deprecated, preview のような魅⼒的な⾔葉を探す ‧仕様と実装の差、仕様(または、直感やイメージ)に反して⾮⾃明な挙動が  問題を⼀段と難しくさせます… ‧仕様がどんどん分かってくると⾃明の幅が広がっていきます。楽しい! #   仕様?悪⽤? ##   まとめ 仕様?悪⽤? #

Slide 27

Slide 27 text

細かく、深く #

Slide 28

Slide 28 text

hard SEKAI CTF 2024 - Chunky https://fireshellsecurity.team/sekaictf-frog-waf-and-chunky/#challenge-chunky-16-solves insane KalmarCTF 2025 - spukhafte Fernwirkung https://github.com/kalmarunionenctf/kalmarctf/tree/main/2025/web/spukhafte/solution #   細かく、深く

Slide 29

Slide 29 text

baby “Wi-Fi” Browser URL ページ https://www.google.com/ #   細かく、深く

Slide 30

Slide 30 text

baby Webサーバ Browser GET/POST, cookie HTTP Request HTTP Response html, css, js #   細かく、深く

Slide 31

Slide 31 text

easy Browser GET/POST, cookie HTTP Request HTTP Response html, css, js #   細かく、深く

Slide 32

Slide 32 text

medium Browser GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … AdminBotは Headless Chromium #   細かく、深く

Slide 33

Slide 33 text

SEKAI CTF 2024 - Chunky ## #   細かく、深く hard

Slide 34

Slide 34 text

hard 図は https://fireshellsecurity.team/sekaictf-frog-waf-and-chunky/#challenge-chunky-16-solves より Request Smugglingを利⽤しながら、 複雑なCache Poisoningによる攻撃チェーンを組み⽴てる必要がある。 #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 35

Slide 35 text

Browser GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … AdminBotは Headless Chromium Request Smuggling 複数ヵ所で「同じリクエスト」が「異なって解釈される」こと ↑ 多くの要素が存在するので不整合が起きたりする。また、json解釈違いなど、無限にinconsistent系はある hard #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 36

Slide 36 text

Browser GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … AdminBotは Headless Chromium Request Smuggling 複数ヵ所で「同じリクエスト」が「異なって解釈される」こと ↑ 多くの要素が存在するので不整合が起きたりする。また、json解釈違いなど、無限にinconsistent系はある hard VS #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 37

Slide 37 text

Browser GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … AdminBotは Headless Chromium Request Smuggling 複数ヵ所で「同じリクエスト」が「異なって解釈される」こと ↑ 多くの要素が存在するので不整合が起きたりする。また、json解釈違いなど、無限にinconsistent系はある hard VS #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 38

Slide 38 text

Browser GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … AdminBotは Headless Chromium Cache Poisoning 悪意ある(本来意図せぬ)キャッシュを⼊れ込むこと hard #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 39

Slide 39 text

Browser GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … AdminBotは Headless Chromium Cache Poisoning 悪意ある(本来意図せぬ)キャッシュを⼊れ込むこと hard #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 40

Slide 40 text

Browser GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … AdminBotは Headless Chromium Cache Poisoning 悪意ある(本来意図せぬ)キャッシュを⼊れ込むこと hard #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 41

Slide 41 text

Browser GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … AdminBotは Headless Chromium Cache Poisoning 悪意ある(本来意図せぬ)キャッシュを⼊れ込むこと hard #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 42

Slide 42 text

Browser GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … AdminBotは Headless Chromium Cache Poisoning 悪意ある(本来意図せぬ)キャッシュを⼊れ込むこと hard #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 43

Slide 43 text

ちなみに、ChunkyのExploit Chainは… hard 図は https://fireshellsecurity.team/sekaictf-frog-waf-and-chunky/#challenge-chunky-16-solves より #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 44

Slide 44 text

hard 図は https://fireshellsecurity.team/sekaictf-frog-waf-and-chunky/#challenge-chunky-16-solves より #   細かく、深く ##   SEKAI CTF 2024 - Chunky

Slide 45

Slide 45 text

KalmarCTF 2025 - spukhafte Fernwirkung ## #   細かく、深く insane ⾃分も解けておらず、当時も0 solvesで、また、作問者も分かっていない部分があるっぽく、 なんか間違っていたらすみません!(そんな状態で書いててすみません) あと、⼀部会場onlyです

Slide 46

Slide 46 text

3つのサーバが与えられ、ドメインが割り当てられています。 xss-spukhafte.kalmarctf.local 任意のHTML(JavaScriptも)が動かせるサイト。Admin Botが踏む。 notes-spukhafte.kalmarctf.local フラグを投稿するサイト。Admin Botがxss-spunkhafteを踏む前に投稿する。 bot-spukhafte.kalmarctf.local Admin Bot #   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung insane

Slide 47

Slide 47 text

notes-spukhafte.kalmarctf.local function saveNote() { const note = document.getElementById('noteInput').value; const uuid = uuidv4(); fetch('/note', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uuid, note }) }); } つまりは、UUIDが分かればフラグが得られる。 #   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung insane UUIDを作って 投稿!

Slide 48

Slide 48 text

notes-spukhafte.kalmarctf.local function uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' .replace(/[xy]/g, function (c) { const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } UUIDのジェネレータは⾃作で、MDNで Math.random を⾒てみると… https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math/random #   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung insane

Slide 49

Slide 49 text

この問題は Crypto / Web 問で、Cryptoパートも難しいので割愛するのですが 乱数のサンプルを集めるとシードが復元できて、結果、UUIDが復元できる というのが⼤枠の問題です。 notes-spukhafte.kalmarctf.local → xss-spukhafte.kalmarctf.local の順でアクセスをするが、どちらも同じ環境で動いているならば、xss側で Math.random()を何回か呼んでサンプルを集めれば解けそう。 「どちらも同じ環境で動いているならば」本当に? #   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung insane

Slide 50

Slide 50 text

insane Browser GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … AdminBotは Headless Chromium notes-spukhafte.kalmarctf.local と xss-spukhafte.kalmarctf.local が同じ環境で動くかを理解するには… #   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung

Slide 51

Slide 51 text

insane Chromium GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … 図は https://developer.chrome.com/blog/inside-browser-part1 より #   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung notes-spukhafte.kalmarctf.local と xss-spukhafte.kalmarctf.local が同じ環境で動くかを理解するには… Chromiumはマルチプロセスアーキテクチャで処理を⾏うプロセスがそれぞれ作られる。 → ということは無理…?

Slide 52

Slide 52 text

insane Chromium GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … 図は https://developer.chrome.com/blog/inside-browser-part1 より #   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung Privacy Sandbox / 3rd Party Cookie / Fenced Frame / Cache Partitioning / Fetch Metadata / Site Isolation / Trusted Types / SOP / bfcache / WebAssembly / Sanitizer API / V8 / Blink

Slide 53

Slide 53 text

insane Chromium GET/POST, cookie HTTP Request HTTP Response html, css, js + ライブラリ Web App Framework Template Engine Database … 図は https://developer.chrome.com/blog/inside-browser-part1 より #   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung Privacy Sandbox / 3rd Party Cookie / Fenced Frame / Cache Partitioning / Fetch Metadata / Site Isolation / Trusted Types / SOP / bfcache / WebAssembly / Sanitizer API / V8 / Blink notes-spukhafte.kalmarctf.local と xss-spukhafte.kalmarctf.local は同じ環境(プロセス)で動く!

Slide 54

Slide 54 text

Site Isolation 異なるサイトは別のプロセスで動かす。 #   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung insane //attacker.example/poc.html //note.a.example/ iframe[0] iframe[1] //xss.a.example/

Slide 55

Slide 55 text

Site Isolation 異なるサイトは別のプロセスで動かす。 #   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung insane //attacker.example/poc.html iframe[0] iframe[1] 同じPID:1234 違うPID:5678 //note.a.example/ //xss.a.example/

Slide 56

Slide 56 text

#   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung insane 会場only

Slide 57

Slide 57 text

#   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung insane 会場only

Slide 58

Slide 58 text

#   細かく、深く ##   KalmarCTF 2025 - spukhafte Fernwirkung insane 会場only

Slide 59

Slide 59 text

‧コンポーネントがあって、チェーンする問題は単純に難しい…(組み合わせが…) ‧解けていないときは何か⾒逃している要素や仕様があるかも! ‧時には祈りながら進む必要も… #   細かく、深く ##   まとめ 細かく、深く #

Slide 60

Slide 60 text

天才発想 #

Slide 61

Slide 61 text

判定無し corCTF 2022 friends https://x.com/hamayanhamayan/status/1557584112004648961 insane SECCON CTF 13 Qual - JavaScrypto https://zenn.dev/ponyopoppo/articles/894c3c2e5a06b6#javascrypto 天才発想 # 時間が無く、省略

Slide 62

Slide 62 text

判定無し corCTF 2022 friends ## #   天才発想

Slide 63

Slide 63 text

#   天才発想 ##   corCTF 2022 friends https://x.com/hamayanhamayan/status/1557584112004648961 判定無し

Slide 64

Slide 64 text

‧天才発想を本番中に⾃⼒発⾒すると、ドーパミンがすごい ‧天才発想問題を作っている⼈の⽣活習慣が知りたい (最近はオヤジギャグの上⼿さと天才発想に相関があるのでは、と本気で思っています) 天才発想 # #   天才発想 ##   まとめ

Slide 65

Slide 65 text

#   おわり! おわり! 難しいWeb問、脳が焼き切れるんですが、 楽しいよ!というのが伝わってたら幸いです! https://docs.google.com/spreadsheets/d/12nRbFdmwNPBOc D2eitybnVixar-LVFmm_WudrOObVf4/edit?usp=sharing

Slide 66

Slide 66 text

Slide 67

Slide 67 text

Slide 68

Slide 68 text

AlpacaHack Round 7 (Web) - disconnection-revenge ## #   仕様?悪⽤? insane

Slide 69

Slide 69 text

import express from "express"; const html = ` eval(new URLSearchParams(location.search).get("xss")); `.trim(); express() .get("/", (req, res) => res.type("html").send(html)) .all("/*", (req, res) => res.socket.destroy()) // disconnected .use((err, req, res, next) => { // revenge! res.socket.destroy(); // disconnected }) 以上がサーバ側の抜粋。AdminBotは以下。 await page.setCookie({ name: "FLAG", value: FLAG, domain: APP_HOST, path: "/cookie", // 🍪 }); // 与えられた任意の URLを開く #   仕様?悪⽤? ##   AlpacaHack Round 7 (Web) - disconnection-revenge insane

Slide 70

Slide 70 text

import express from "express"; const html = ` eval(new URLSearchParams(location.search).get("xss")); `.trim(); express() .get("/", (req, res) => res.type("html").send(html)) .all("/*", (req, res) => res.socket.destroy()) // disconnected .use((err, req, res, next) => { // revenge! res.socket.destroy(); // disconnected }) 以上がサーバ側の抜粋。AdminBotは以下。 await page.setCookie({ name: "FLAG", value: FLAG, domain: APP_HOST, path: "/cookie", // 🍪 }); // 与えられた任意の URLを開く #   仕様?悪⽤? ##   AlpacaHack Round 7 (Web) - disconnection-revenge insane `GET /`は ⾃由にXSS可 それ以外の エンドポイントは全部拒否 /cookieでCookieを 盗む必要がある

Slide 71

Slide 71 text

⼀旦、「拒否」を無視して考えてみると、SOPをうまく使うと解ける。 #   仕様?悪⽤? ##   AlpacaHack Round 7 (Web) - disconnection-revenge insane SOP: Same Origin Policy JavaScriptでアクセスできるのは同じサイトだけ

Slide 72

Slide 72 text

⼀旦、「拒否」を無視して考えてみると、SOPをうまく使うと解ける。 #   仕様?悪⽤? ##   AlpacaHack Round 7 (Web) - disconnection-revenge insane SOP: Same Origin Policy JavaScriptでアクセスできるのは同じサイトだけ //attacker.example/poc.html //chall.example/ //chall.example/cookie iframe[0] iframe[1]

Slide 73

Slide 73 text

⼀旦、「拒否」を無視して考えてみると、SOPをうまく使うと解ける。 #   仕様?悪⽤? ##   AlpacaHack Round 7 (Web) - disconnection-revenge insane SOP: Same Origin Policy JavaScriptでアクセスできるのは同じサイトだけ //attacker.example/poc.html //chall.example/ //chall.example/cookie iframe[0] iframe[1] 󰢃

Slide 74

Slide 74 text

⼀旦、「拒否」を無視して考えてみると、SOPをうまく使うと解ける。 #   仕様?悪⽤? ##   AlpacaHack Round 7 (Web) - disconnection-revenge insane SOP: Same Origin Policy JavaScriptでアクセスできるのは同じサイトだけ //attacker.example/poc.html //chall.example/ //chall.example/cookie iframe[0] iframe[1] 󰢏 window.top.frames[1]. document.cookie window=iframe[0]

Slide 75

Slide 75 text

#   仕様?悪⽤? ##   AlpacaHack Round 7 (Web) - disconnection-revenge insane 図は https://dimas0305.notion.site/Bypassing-null-Origin-in-4xx-Status-Code-Using-Iframe-disconnection-revenge-Writeup-AlpacaHack- R-14e48583e65d80e6b8d5c53f07905d97 より 拒否されると originがnullになる

Slide 76

Slide 76 text

「拒否」を含めてみよう。 #   仕様?悪⽤? ##   AlpacaHack Round 7 (Web) - disconnection-revenge insane //attacker.example/poc.html //chall.example/ //chall.example/cookie iframe[0] iframe[1] 󰢃 nullなので SOP違反 図は https://dimas0305.notion.site/Bypassing-null-Origin-in-4xx-Status-Code-Using-Iframe-disconnection-revenge-Writeup-AlpacaHack- R-14e48583e65d80e6b8d5c53f07905d97 より

Slide 77

Slide 77 text

431 (Request Header Fields Too Large)テクを使う。 #   仕様?悪⽤? ##   AlpacaHack Round 7 (Web) - disconnection-revenge insane //chall.example/cookie iframe origin = null 図は https://dimas0305.notion.site/Bypassing-null-Origin-in-4xx-Status-Code-Using-Iframe-disconnection-revenge-Writeup-AlpacaHack- R-14e48583e65d80e6b8d5c53f07905d97 より //chall.example/cookie?AA...[10^5 times]...AA ↓ 431 Request Header Fields Too Large エラー ↓ これをiframeに埋め込むとなぜか、 originが保存される! これは…仕様? iframe origin = http://chall.example ※ ちなみにFirefoxだと431エラー時はiframeに⼊れなくてもoriginは保存されるみたいです 󰤇

Slide 78

Slide 78 text

#   仕様?悪⽤? ##   AlpacaHack Round 7 (Web) - disconnection-revenge insane //attacker.example/poc.html //chall.example/ //chall.example/cookie ?AA...[10^5 times]...AA iframe[0] iframe[1] 󰢏 図は https://dimas0305.notion.site/Bypassing-null-Origin-in-4xx-Status-Code-Using-Iframe-disconnection-revenge-Writeup-AlpacaHack- R-14e48583e65d80e6b8d5c53f07905d97 より ※ 実際にはもう1ステップ、open("") という超⾮⾃明テクを⾒つける必要があります 解けた!

Slide 79

Slide 79 text

SECCON CTF 13 Qual - JavaScrypto ## #   天才発想 insane

Slide 80

Slide 80 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane http://victim.example/?id= にアクセスすると、ブラウザ側で… 1. fetch(`/note/${id}`) でnoteIdに対応した encrypted を得る 2. Local Storageから、ユーザー固有の鍵 key を取得する 3. encrypted, key をbase64デコードする 4. key を使って encrypted を復号化し、plaintextを得る 5. plaintextを「そのまま」HTMLファイルに埋め込んで表⽰する

Slide 81

Slide 81 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane http://victim.example/?id= にアクセスすると、ブラウザ側で… 1. fetch(`/note/${id}`) でnoteIdに対応した encrypted を得る 2. Local Storageから、ユーザー固有の鍵 key を取得する 3. encrypted, key をbase64デコードする 4. key を使って encrypted を復号化し、plaintextを得る 5. plaintextを「そのまま」HTMLファイルに埋め込んで表⽰する plaintextに任意の値を⼊れてXSSする

Slide 82

Slide 82 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane http://victim.example/?id= にアクセスすると、ブラウザ側で… 1. fetch(`/note/${id}`) でnoteIdに対応した encrypted を得る 2. Local Storageから、ユーザー固有の鍵 key を取得する 3. encrypted, key をbase64デコードする 4. key を使って encrypted を復号化し、plaintextを得る 5. plaintextを「そのまま」HTMLファイルに埋め込んで表⽰する plaintextに任意の値を⼊れてXSSする noteIdもadmin botに渡せる かつ、noteは攻撃者が⽤意可能 → 好きな encrypted を渡せる

Slide 83

Slide 83 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane http://victim.example/?id= にアクセスすると、ブラウザ側で… 1. fetch(`/note/${id}`) でnoteIdに対応した encrypted を得る 2. Local Storageから、ユーザー固有の鍵 key を取得する 3. encrypted, key をbase64デコードする 4. key を使って encrypted を復号化し、plaintextを得る 5. plaintextを「そのまま」HTMLファイルに埋め込んで表⽰する plaintextに任意の値を⼊れてXSSする noteIdもadmin botに渡せる かつ、noteは攻撃者が⽤意可能 → 好きな encrypted を渡せる 「ユーザー固有の」=「 adminbotの」 adminbotの鍵が分からない!!

Slide 84

Slide 84 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane この問題には、 Prototype Pollution 脆弱性(以下、PP)があります。PPをざっく りと説明すると「存在しない」任意のプロパティを追加できるという脆弱性です。 if (this.isAdmin) { // you got a flag! } 上のような状況で⾃由に isAdmin を設定できてフラグが得られたりします。 PPはプロパティを増やせるだけなので、それが悪⽤できるような上記のような箇所 (Gadget [1] )を⾒つけてくる必要があります。 [1] セキュリティにおける"gadget"とは何なのか? https://blog.hamayanhamayan.com/entry/2022/09/14/212004

Slide 85

Slide 85 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane http://victim.example/?id= にアクセスすると、ブラウザ側で… 1. fetch(`/note/${id}`) でnoteIdに対応した encrypted を得る 2. Local Storageから、ユーザー固有の鍵 key を取得する 3. encrypted, key をbase64デコードする 4. key を使って encrypted を復号化し、plaintextを得る 5. plaintextを「そのまま」HTMLファイルに埋め込んで表⽰する Gadgetどこかな…

Slide 86

Slide 86 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane http://victim.example/?id= にアクセスすると、ブラウザ側で… 1. fetch(`/note/${id}`) でnoteIdに対応した encrypted を得る 2. Local Storageから、ユーザー固有の鍵 key を取得する 3. encrypted, key をbase64デコードする 4. key を使って encrypted を復号化し、plaintextを得る 5. plaintextを「そのまま」HTMLファイルに埋め込んで表⽰する Gadgetどこかな… base64デコード!!

Slide 87

Slide 87 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane ユーザー固有の鍵 key は CryptoJS というライブラリを使ってBase64デコード されます。 const rawKey = CryptoJS.enc.Base64.parse(key); そして、このライブラリには _reverseMapというPPと⼀緒に悪⽤可能な箇所が! var reverseMap = this._reverseMap; if (!reverseMap) { reverseMap = this._reverseMap = []; for (var j = 0; j < map.length; j++) { reverseMap[map.charCodeAt(j)] = j; } } // map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='

Slide 88

Slide 88 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane ユーザー固有の鍵 key は CryptoJS というライブラリを使ってBase64デコード されます。 const rawKey = CryptoJS.enc.Base64.parse(key); そして、このライブラリには _reverseMapというPPと⼀緒に悪⽤可能な箇所が! var reverseMap = this._reverseMap; if (!reverseMap) { reverseMap = this._reverseMap = []; for (var j = 0; j < map.length; j++) { reverseMap[map.charCodeAt(j)] = j; } } // map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' nullだと⼊る、デフォルトの対応テーブル reverseMap = { ʻA’ -> 0, ʻB’ -> 1, ʻC’ -> 2, … } PPで上書き可能! 表は https://ja.wikipedia.org/wiki/Base64 より

Slide 89

Slide 89 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane という訳で、_reverseMapを使ってやれば、base64のデコードを⾃由に操作できる! laTrJHY/GOEk1QWdBtuzNg== ↓ 95 a4 eb 24 76 3f 18 e1 24 d5 05 9d 06 db b3 36 表は https://ja.wikipedia.org/wiki/Base64 より

Slide 90

Slide 90 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane という訳で、_reverseMapを使ってやれば、base64のデコードを⾃由に操作できる! laTrJHY/GOEk1QWdBtuzNg== ↓ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 どんな⼊⼒もゼロにできる! 表は https://ja.wikipedia.org/wiki/Base64 より 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000

Slide 91

Slide 91 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane http://victim.example/?id= にアクセスすると、ブラウザ側で… 1. fetch(`/note/${id}`) でnoteIdに対応した encrypted を得る 2. Local Storageから、ユーザー固有の鍵 key を取得する 3. encrypted, key をbase64デコードする 4. key を使って encrypted を復号化し、plaintextを得る 5. plaintextを「そのまま」HTMLファイルに埋め込んで表⽰する plaintextに任意の値を⼊れてXSSする noteIdもadmin botに渡せる かつ、noteは攻撃者が⽤意可能 → 好きな encrypted を渡せる

Slide 92

Slide 92 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane http://victim.example/?id= にアクセスすると、ブラウザ側で… 1. fetch(`/note/${id}`) でnoteIdに対応した encrypted を得る 2. Local Storageから、ユーザー固有の鍵 key を取得する 3. encrypted, key をbase64デコードする 4. key を使って encrypted を復号化し、plaintextを得る 5. plaintextを「そのまま」HTMLファイルに埋め込んで表⽰する plaintextに任意の値を⼊れてXSSする noteIdもadmin botに渡せる かつ、noteは攻撃者が⽤意可能 → 好きな encrypted を渡せる ʻ0x00’*16固定! admin鍵不要!

Slide 93

Slide 93 text

#   天才発想 ##   SECCON CTF 13 Qual - JavaScrypto insane http://victim.example/?id= にアクセスすると、ブラウザ側で… 1. fetch(`/note/${id}`) でnoteIdに対応した encrypted を得る 2. Local Storageから、ユーザー固有の鍵 key を取得する 3. encrypted, key をbase64デコードする 4. key を使って encrypted を復号化し、plaintextを得る 5. plaintextを「そのまま」HTMLファイルに埋め込んで表⽰する plaintextに任意の値を⼊れてXSSする noteIdもadmin botに渡せる かつ、noteは攻撃者が⽤意可能 → 好きな encrypted を渡せる ʻ0x00’*16固定! admin鍵不要! こっちも全部 ʻ0x00’に なるのでは…?

Slide 94

Slide 94 text

⾼度パズル #

Slide 95

Slide 95 text

medium 防衛省サイバーコンテスト2023 - Bypass https://blog.hamayanhamayan.com/entry/2023/08/06/220606#web-Bypass hard SECCON CTF 13 Qual - Double-Parser https://zenn.dev/tchen/articles/0efc8f9679a818#%E2%9C%85-double-parser-(221pts-17solves) insane ImaginaryCTF 2023 - Sanitized Revenge https://github.com/maple3142/My-CTF-Challenges/blob/master/ImaginaryCTF%202023/San itized%20Revenge/README.md ⾼度パズル # easy Flatt Security XSS Challenge - hamayanhamayan問 https://speakerdeck.com/flatt_security/jie-da-jie-shuo-flatt-security-xss-challenge

Slide 96

Slide 96 text

⾼度パズル      防衛省サイバーコンテスト2023 - Bypass # ## ">alert(1)

Slide 97

Slide 97 text

⾼度パズル      Flatt Security XSS Challenge - hamayanhamayan問 # ##
medium

Slide 98

Slide 98 text

⾼度パズル      SECCON CTF 13 Qual - Double-Parser # ## alert(1)// hard

Slide 99

Slide 99 text

⾼度パズル      ImaginaryCTF 2023 - Sanitized Revenge # ##
https://webhook.site/[yours]?
<![CDATA[
--> insane