11.6K Views
July 24, 23
スライド概要
Shibuya.xss techtalk #12
CSPを無意味にする 残念なServiceWorker はせがわようすけ @hasegawayosuke
C S P 突 破 テ ク ニ ッ ク が つ い に 公 開 ‼ Shibuya.xss #12 こ こ で 提供 Shibuya.XSS UTF-8.jp し か 見 ら れ な い S W に よ 2023-07-25 る #shibuyaxss
0. 予備知識 ServiceWorkerって? Shibuya.xss #12 2023-07-25 #shibuyaxss
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
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
ServiceWorkerって? HTTPでの通信箇所が増えた! レンダラーとSWの間、SWとサーバーの間 サーバーがCSPを返しても、レンダラーに伝わらないときがある んじゃない? …というのが今日のテーマ レンダラー ServiceWorker CSP? Shibuya.xss #12 2023-07-25 ネットワーク ブラウザー CSP! #shibuyaxss
1. SWがCSPを削除して レンダラーに返すパターン Shibuya.xss #12 2023-07-25 #shibuyaxss
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
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
SWがCSPを削除してレンダラーに返すパターン SWが生成した レスポンス ネットワークからの レスポンス 生成したレスポンスではCSP ヘッダーが削除されている ネットワークからのレスポンスでは CSPがついている Shibuya.xss #12 2023-07-25 #shibuyaxss
SWがCSPを削除してレンダラーに返すパターン Shibuya.xss #12 2023-07-25 #shibuyaxss
SWがCSPを削除してレンダラーに返すパターン さすがにこんな不自然なコードはなさそう swは通信に介入できるが、直接的にレスポンスヘッダーを改ざんするこ とはできない レスポンスヘッダーをコピーした上でCSPを削除する(CSP以外のレスポ ンスヘッダーをコピーする)のような意図的なコードが必要 ➡ もうちょっと現実にありそうなコードを考える! Shibuya.xss #12 2023-07-25 #shibuyaxss
2. SW内でリダイレクトさせる パターン Shibuya.xss #12 2023-07-25 #shibuyaxss
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
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
3. SW内でHTMLを 生成するパターン Shibuya.xss #12 2023-07-25 #shibuyaxss
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
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
SW内でHTMLを生成するパターン SWからの応答のみでネットワークへの通信がない (どうでもいい小ネタ) DOM操作ではないので `<script>` が効く Shibuya.xss #12 2023-07-25 #shibuyaxss
3.まとめ Shibuya.xss #12 2023-07-25 #shibuyaxss
まとめ 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
まとめ サーバーの返すネットワーク上のレスポンスに加え、SWが生成す る(仮想的な)レスポンスが増えた! これまでの挙動の齟齬が出る箇所があるかも? ブラウザーの脆弱性を探す人にはもしかしたら狙い目かも レンダラー ServiceWorker res? Shibuya.xss #12 2023-07-25 ネットワーク ブラウザー res! #shibuyaxss
Question? @hasegawayosuke @hasegawa.bsky.social Shibuya.xss #12 2023-07-25 #shibuyaxss