セキュリティツールの検知を回避するために作られたあるフィッシングページが、コーディングミスによって被害者のブラウザを無限のJavaScriptループに陥らせ、皮肉にも自らの認証情報窃取活動を機能不全にしていたことが分かりました。
この不審なメッセージは、当初は一見普通のものに見えました。よくあるフィッシングの誘い文句を使い、受信者を、標的のメールアドレスをパラメータとして含んだウェブアドレスへ誘導する内容でした。
しかし、そのリンクを仮想マシン上で開いてみると、ブラウザにはログインページが表示されませんでした。代わりに約30秒間フリーズし、その間CPUコアの使用率は100%に達しました。
サイトのHTML自体はほぼ即座に取得できていたため、サーバー応答の遅延が原因である可能性は排除されました。問題は、フィッシングキットをデコードして表示するために使われていた、厳重に難読化されたJavaScriptの内部に隠されていました。
この悪意あるスクリプトは、デコード処理の一環として2つの関数を使用していました。どちらの関数もkという名前のループカウンターに依存していましたが、いずれもこの変数をローカルで宣言していませんでした。
一方の関数はデコードテーブルを構築するためのもので、そのカウンターは0から63まで進む仕組みでした。処理が1回進むたびに、ASCII値から文字を生成する別のヘルパー関数を呼び出していました。この内部のヘルパー関数もまたkを使用していました。
kがローカル宣言されていなかったため、JavaScriptはこれを両関数が共有するグローバル変数として扱いました。内部のルーチンが最後に行う処理は/という文字(ASCII値47)を生成するもので、これにより共有カウンターの値は48で残されました。
処理が外側の関数に戻ると、その関数自身のインクリメント処理によって値は49に変わります。しかし次のサイクルでは、内部の関数が再びその値を48にリセットしてしまいます。そのため、外側のループは同じ値を繰り返すだけの状態に陥っていました。
結果として、デコード処理は決して完了せず、認証情報を窃取するためのページは一切描画されず、ブラウザは処理が停止するかページが閉じられるまでCPUリソースを消費し続けることになりました。
内部の関数をローカルで宣言したカウンターを使うように変更したところ、スクリプトは正常に完了しました。デコードされたペイロードは、ユーザーの認証情報を盗み取ることを目的とした、ごくありふれたフィッシングフォームでした。
最初の失敗は、単に破損した1つのコピーによるものではなさそうでした。同じフィッシングURLを再度開いたところ、今度は正常に動作しました。しかし、リクエストのたびに返されるソースコードはそれぞれ異なっていました。
ページを読み込むたびに、関数名や変数名が変化していました。関数の出現順序も異なり、数値は算術式として書き換えられ、エンコードされたペイロードやページタイトルも毎回異なっていました。
表示されるフィッシングフォーム自体はほぼ同じ見た目を保っていましたが、内部のHTML、CSSクラス、フィールド名、画像パラメータ、識別子はアクセスのたびに変化していました。
このページを50回ダウンロードして検証したテストでは、すべてのサンプルでSHA-256ハッシュ値が異なっていることが判明しました。分析担当者は21種類の異なるページタイトルを確認し、49件のサンプルは正常に難読化解除できました。そのうち1件のサンプルでは、最初のアクセス時に確認されたのと同じ無限ループが発生したとSANSは述べています。
これは、このサイトがリクエストごとに新たな悪意あるページの亜種を生成する、ポリモーフィック型のフィッシング機構を用いていたことを示しています。
こうした手法は、ハッシュ値や固定の識別子、単純なテキストシグネチャに基づく静的な防御を弱体化させる可能性があります。表示文字列に含まれるゼロ幅文字も、解析を妨げるさらなる層として機能していました。
ANY.RUNのブラウザ内データ検査で検知・調査・対応を高速化-> ANY.RUNでSOCを強化
翻訳元: https://cyberpress.org/phishing-evasion-code-backfires/