Zscalerのブログ

Get the latest Zscaler blog updates in your inbox

購読する
セキュリティリサーチ

SmokeLoaderの歴史 - 第1回

image
THREATLABZ
June 11, 2024 - 15 分で読了

はじめに

このブログはSmokeLoaderの歴史に関するシリーズの第1回です。第2回はこちらをご覧ください。

Zscaler ThreatLabzは2024年5月、SmokeLoaderの技術分析により、エンドゲーム作戦と呼ばれる国際的な法執行機関の活動を支援しました。数万もの感染をリモートでクリーンアップするこの作戦で法執行機関を支援する過程において、ThreatLabzはSmokeLoaderのほぼすべての既知のバージョンを文書化しました。

このシリーズでは、SmokeLoaderの進化を2部構成で解説します。SmokeLoaderは当初、他のマルウェア ファミリーを展開するための第1段階のダウンローダーとして使用されていましたが、その後独自のフレームワークを組み込み、情報窃取機能などを実装してさらなる進化を遂げています。長年にわたり重要な開発と改良が加えられたSmokeLoaderですが、既知の最新バージョンは2022年に登場しています。第1回はSmokeLoaderの起源とモジュール設計への最初の変換について解説します。

要点

  • SmokeLoaderはモジュール型マルウェア ファミリーであり、2011年に犯罪フォーラムで初めて宣伝されました。
  • SmokeLoaderは主にダウンローダーとして機能し、第2段階のマルウェアを実行します。
  • SmokeLoaderはマルウェアを強化するさまざまな追加モジュールをダウンロードする機能を備えており、データの窃取、分散型サービス拒否攻撃の開始、暗号通貨のマイニングなどを実行できるようにします。
  • SmokeLoaderは分析環境の検出、偽のネットワーク トラフィックの生成、コードの難読化と検出回避、分析の妨害を行います。
  • SmokeLoaderは何年もかけて大きく進化し、新機能に加えて、暗号化や圧縮、ハッシュ アルゴリズムも改善させてきました。

年表

以下の図は、2011年から現在までのSmokeLoaderの進化を包括的に表したものです。

図1: 2011年から2022年までのSmokeLoaderの進化を表した年表

図1: 2011年から2022年までのSmokeLoaderの進化を表した年表

2011~2013年:最初期

私たちがSmokeLoaderのサンプルを最初に分析した時期は、2011年にさかのぼります。これらのサンプルには顕著な違いがあり、その後のバージョンよりも初歩的です。この最初のSmokeLoaderにはバージョン番号が含まれていないため、これを最初期と呼びます。

これらの初期バージョンには、コマンド&コントロール(C2)サーバーとの初期通信を確立するための基盤となる初期モジュールが含まれています。このマルウェアは埋め込まれた2つのシェルコードを利用し、それらを新たに作成された2つのsvchost.exeインスタンスに注入することでこれを可能にします。最初期にはコードが何度か変更され、一部のサンプルでは共有セクションを作成することで(ZwCreateSectionZwMapViewOfSectionを使用)プロセス インジェクションを実行し、メイン プロセスのスレッドを再開して(ZwResumeThreadを使用)、注入されたコードを実行しました。

2012年の別のサンプルでは、非同期プロシージャー コール(APC)キュー手法を使用し、空洞化されたsvchost.exeプロセスにSmokeLoaderを注入しています。SmokeLoaderがシェルコードを作成し、APCキューのインジェクション手法によってシェルコードをsvchost.exeに注入するプロセスは、以下の図のとおりです。

図2:シェルコードを作成し、svchost.exeに注入するSmokeLoaderの初期バージョン

図2:シェルコードを作成し、svchost.exeに注入するSmokeLoaderの初期バージョン

シェルコードの1つがgetloadコマンドを(login引数とともに) C2サーバーに送信し、他のシェルコードはgetgrabコマンドでC2サーバーにクエリーを実行します。

分析中のSmokeLoaderサンプルに見られたsvchost.exeシェルコードは以下の図のとおりです。

図3: 2012年頃のSmokeloaderサンプルのsvchost.exeシェルコード

図3: 2012年頃のSmokeloaderサンプルのsvchost.exeシェルコード

ボットは、getloadコマンドを使用し、HTTP GETリクエストを介してC2サーバー内に自身を登録します。Smokeの初期バージョンのネットワーク トラフィックの例を以下に示します。

図4: SmokeLoaderバージョン2012からのC2リクエストの例

図4: SmokeLoaderバージョン2012からのC2リクエストの例

SmokeLoaderは、以下の表のとおり、getloadコマンドを使用して2つのパラメーターをC2サーバーに送信します。

引数

概要

login

ボットIDを指定するもので、被害者のコンピューター名の単純なMD5ハッシュとして計算されます。

sel

ハードコードされた販売者ID (別名アフィリエイトID)を指定するもので、このIDはサンプルごとに異なります(77777など)。

表1: SmokeLoader 2012におけるgetloadコマンドのパラメーター

SmokeLoaderは最新バージョンでも、ボットIDと販売者IDを送信して自身をC2サーバーに登録します。登録が完了すると、サーバーがgetloadコマンドに応答してペイロードを返します。そして、マルウェアがそのペイロードをディスクに書き込んで実行します。

また、getgrabコマンドは、SmokeLoaderに追加のグラバー モジュールをダウンロードすることもできます。この場合、C2サーバーから返されるデータは、単純なXORアルゴリズムで暗号化されます。復号されると、ポータブル実行可能(PE)ファイルがシェルコードによって現在のプロセス コンテキストに直接マッピングされます。

情報窃取型プラグイン

SmokeLoader 2012パネルのソース コードが流出しました。以下のコード サンプルは、パネルがサポートするさまざまなコマンドを管理する方法を示しています。

if ($command == "getgrab") {
    getmodule("./mods/grab");
    exit;
} elseif ($command == "getproxy") {
    getmodule("./mods/socks");
    exit;    
} elseif ($command == "getspoof") {
    getmodule("./mods/hosts");
    exit;    
} elseif ($command == "getcmdshell") {
    getmodule("./mods/shell");
    exit;    
} elseif ($command == "getload" && isset($cmd["doubles"])) {
…
} elseif ($command == "getload" && isset($cmd["final"])) {
…
} elseif ($command == "getload" && isset($cmd["personal"])) {
…

getgrabコマンドは、C2サーバー上にある./mods/grabのファイルの内容を取得します。このファイルの最初の4バイトは、残りのコンテンツの暗号化解除に使用されるXORキーに対応します。grabの実行可能ファイルは、メール、FTP、メールのパスワードを盗むことができる、フル機能を備えた情報窃取型モジュールです。

SmokeLoaderの主要な通信チャネルにはHTTP GETリクエストが使用される一方、グラバーとC2間の通信はHTTP POSTクエリーを介して行われます。

流出したパネルのmodsフォルダー内には./mods/shellのファイルもあり、このファイルはシンプルなリモート シェルを実装します。流出したパネルのソース コードは、getproxyコマンドとgetspoofコマンドで言及されているものなど、他のモジュールを参照しています。

最初の分析回避技術

SmokeLoaderはさまざまな分析回避技術を採用していることで有名ですが、その技術はバージョンが新しくなるごとに改善されています。このセクションでは、SmokeLoaderバージョン2012で確認された最初の分析回避技術について解説します。

このマルウェアは、エクスポートされた関数の名前を保存する代わりに、ハッシュベースのアプローチを利用して必要なAPIアドレスを特定します。このバージョンにおいて、API名をハッシュ化するために使用されるアルゴリズムは、以下のPythonコード サンプルに示すように比較的単純です。

def calc_hash_smoke2012(apiname):
   hash = 0
   for byte in apiname:
       hash = (hash << 8 | hash >> (32 - 8)) & 0xFFFFFFFF
       hash = byte ^ hash
   return hash

マルウェアの第1段階で使用される文字列は暗号化されています。バイナリー内の暗号化された各文字列は、最初の4バイトがXORキーに対応し、その後に暗号化されたデータが続くという構造になっています。

Pythonによる文字列を復号するアルゴリズムの実装は、以下のコード サンプルのとおりです。

def smoke2012_string_decrypt(data, key):
   aligned = data
   unaligned = b''
   unaligned_dec = b''
   if n_unaligned := len(data) % 4:
       aligned = data[0:-n_unaligned]
       unaligned = data[-n_unaligned:]
   aligned_dec = xor(aligned, key)
   unaligned_dec = xor(unaligned, key[0:1])
   return aligned_dec + unaligned_dec

このバージョンでは、C2のURLは暗号化された文字列のリストに格納されます。

2014年:最初のモジュール分割

次のセクションでは、SmokeLoaderバージョン2014の新機能と変更点について解説します。具体的には、多段階の読み込みプロセス、ボットIDを生成するための更新されたアルゴリズム、暗号化された個別のC2リストなどです。

2014年にさかのぼったサンプルの分析中に、ファイル マッピングの名前として使用される文字列であるs2k14が導入されていることを確認しました。s2k14はバージョン2014を参照していると考えられます。以下の図のとおり、コード内にこの文字列が参照されています。

図5: s2k14というファイル マッピング名でSmokeLoaderバージョン2014を示す文字列

図5: s2k14というファイル マッピング名でSmokeLoaderバージョン2014を示す文字列

このバージョンにおける最も興味深い機能として、マルウェアがいくつかの読み込み段階に分割されたことが挙げられます。ステージャー コンポーネントの導入は、重要な変化を示し、その後のすべてのバージョンで標準となりました。2014年以降の各バージョンは、ステージャー、メイン モジュール、追加機能を実装するプラグインで構成されています。

バージョン2014では、パネル内の./mods/フォルダーに./mods/pluginsファイルが含まれていますが、これは複数のプラグインを1つのファイルにまとめたものです。以前のバージョンにあった./mods/grabの情報窃取型モジュールは、FTP/メール窃取型モジュール、ブラウザー窃取型モジュール、キーロガー モジュールなど複数に分割され、その後、このpluginsファイルにパッケージングされました。このプラグインの変更点は、2014から最新バージョンまで保持されています。

aPLibステージャー

SmokeLoaderバージョン2014で導入されたステージャーは非常にシンプルです。このステージャーは、以下のアクションを実行します。

  • 1バイトのXORキーを使用し、データのセクションを復号する。
  • aPLibを使用し、データを解凍する。
  • メイン モジュールを同じプロセス コンテキストに割り当てられたバッファーにマッピングする。
  • BeingDebuggedフィールドやNtGlobalFlagsフィールドなどのプロセス環境ブロック(PEB)をチェックすることで、いくつかの簡単な分析回避策を実行する。

次に、ステージャーは実行をメイン モジュールに転送してsvchost.exeのインスタンスを作成し、APCキューのコード インジェクションによってこの新しく作成されたsvchost.exeプロセスにSmokeLoaderを挿入します。

バージョン2014のaPLibステージャーの仕組みは以下の図のとおりです。

図6: SmokeLoaderバージョン2014でコードを解凍してsvchost.exeに注入する仕組み

図6: SmokeLoaderバージョン2014でコードを解凍してsvchost.exeに注入する仕組み

SmokeLoaderのステージャーは、その後のバージョンで大きな進化を遂げ、高度な難読化技術と追加の分析回避策が組み込まれました。ただ、このバージョンでも、基本的な難読化の手法の存在は確認できます。

たとえば、SmokeLoaderは呼び出された関数のXOR暗号化のレイヤーを解き明かすために、ポリモーフィック型でない復号ループを採用しています。その復号は以下の図のとおりです。

図7: SmokeLoaderバージョン2014におけるステージャー関数の復号

図7: SmokeLoaderバージョン2014におけるステージャー関数の復号

永続化

SmokeLoaderの販売者は、永続化の有無にかかわらず、脅威アクターにサンプルを作成するオプションを提供しています。被害者のシステムに対する永続化を実現するためのSmokeLoaderのアプローチは、時代とともに多くの変更が加えられてきました。以前のバージョン(2011~2017)では、一般的な実行レジストリー キーを活用し、(レジストリー値の設定に失敗した場合の)フォールバック オプションとしてスタートアップ ショートカットを作成していました。さらに、変更されたレジストリー キーを保護するために、2つの専用スレッドを確立していました。

ボットID

2011年頃から2012年にかけてのSmokeLoaderの初期バージョンでは、ボットIDが被害者のマシンのコンピューター名の単純なMD5ハッシュを取得して生成されることを確認しました。ボットIDを生成するアルゴリズムは、時代とともに軽微な変更が加えられてきています。特に、バージョン2014では、CRC32とXORベースのアルゴリズムが採用されました。

2014年以降のどのバージョンも、コンピューター名とボリューム情報の両方を使用してIDを計算します。そのコードは以下の図のとおりです。

図8: SmokeLoaderバージョン2014におけるボットIDの生成

図8: SmokeLoaderバージョン2014におけるボットIDの生成

分析回避技術とプレーンテキスト文字列

SmokeLoaderバージョン2014のメイン モジュールは、以下の図に示すように、マルウェアの文字列をプレーンテキストで格納しました。これは、文字列が暗号化されていた初期バージョンとは大きく異なります。

図9: SmokeLoaderバージョン2014におけるプレーンテキスト文字列

図9: SmokeLoaderバージョン2014におけるプレーンテキスト文字列

これは、sbiedlldbghelpqemuvirtualvmwarexenという文字列を検索し、マルウェア分析環境に関連するライブラリーやプロセスを確認する最初のバージョンです。分析環境が検出されると、SmokeLoaderは自らを終了して検出を回避します。

暗号化されたC2

SmokeLoaderのバージョン間で最も多くの変更が加えられたコンポーネントの1つが、暗号化されたC2リストの復号に使用されるアルゴリズムです。

バージョン2014のメイン モジュールの文字列はプレーンテキストで格納されますが、C2サーバーのリストは暗号化されます。リストを復号するには、カスタマイズされたXORベースの復号アルゴリズムが使用されます。このコードにはポインターのテーブルが含まれており、各ポインターは、XORキーとして使用されるバイトが先頭に付加される文字列を参照し、その後に未使用のバイトが3つ続きます。次のバイトは暗号化されたC2 URLのサイズを示しており、残りのバイトは暗号化されたC2 URLです。以下のPythonコードは、バージョン2014で使用されたC2復号アルゴリズムを示しています。

def smoke2014_c2_xor_decrypt(enc_data):
   key = enc_data[0]
   size = enc_data[4]
   enc = enc_data[5:]
   dec = b''
   for i in range(0, len(enc)-1, 2):
       dec += (0xff&((enc[i] ^ key) - 
              (enc[i+1] ^ key))).to_bytes(1, 'little')
   return dec


C2パッチ回避メカニズム

以下の図に示すように、SmokeLoaderバージョン2014にはシンプルなコピー防止メカニズムが実装されています。

図10: SmokeLoaderバージョン2014に実装されたシンプルなコピー防止メカニズム

図10: SmokeLoaderバージョン2014に実装されたシンプルなコピー防止メカニズム

このマルウェアは、C2 URL文字列のCRC32値を計算し、コード内のさまざまなポイントで事前定義された期待値と比較します。このメカニズムは、他のハッカーがサンプルを新しいC2でパッチするビルダーを作成できないようにすることで、SmokeLoaderの売上の減少を防ぐ目的で設計されたと考えられます。

通信

SmokeLoaderバージョン2014の通信プロトコルは、以前のバージョンと類似しており、暗号化されてC2サーバーに送信されるシンプルなテキストベースのプロトコルを使用します。そのプロトコルの構文は以下のとおりです。

arg1=value1&arg2=value2…&argN=valueN

バージョン2014パネルは、以下の引数を認識します。

引数

概要

cmd

C2パネルは、一連のコマンドを受け入れます。指定したコマンドによっては、追加の引数を指定する必要があります。

login

この引数はボットIDであり、その長さは40バイトである必要があります。

info

一部のコマンドで提供される追加情報です。

ver

OSのバージョンです。

bits

被害者のWindows OSのアーキテクチャーです。

file

主にgetloadコマンドと一緒に使用し、更新やタスクをリクエストします。

run

あらゆる目的のさまざまなコマンドで使用されます。たとえば、更新をリクエストする、更新が正常に実行されたことを示す場合などです。

port

getsocksコマンドと一緒に使用し、プロキシのポートを指定します。

procname

procmonコマンドに含まれるプロセス名です。

doubles

getloadコマンドと一緒に送信できる追加のフラグです。

removed

getloadコマンドと一緒に送信できる追加のフラグです。

personal

getloadコマンドと一緒に送信できる追加のフラグです。

shell

getshellコマンドがサーバーに送信されると、この引数には実行されたコマンドの結果が含まれます。

grab

formgrabftpgrabkeylogコマンドと一緒に使用されます。この引数には盗まれた情報が含まれます。

filedata

ファイルが提出されると、filedata引数にはファイルの内容が含まれます。

表2: SmokeLoaderバージョン2014におけるネットワーク プロトコル

このパネルは、以下のコマンド(cmd引数で指定されたコマンド名)を処理できます。

コマンド(cmd)

概要

getload

 

*追加の引数と一緒に使用

メイン コマンドの1つであり、指定された引数によって異なります。

  • file: getloadコマンドがfile引数に含まれている場合、パネルはfile引数の値に応じてコマンドをさまざまな方法で処理します。
    • file引数の値がuで、run引数が設定されていない場合、ボットは更新をリクエストします。サーバーは、更新プログラムの実行可能ファイル、または更新プログラムをダウンロードするためのURLを返すことができます。
    • file引数の値がuで、run引数が設定されている場合、ボットは更新が正常に実行されたことを確認します。
    • file引数の値がuではなく、run引数が設定されていない場合、ボットは次回のタスクをリクエストします。サーバーは、各ボットに最後に与えられたタスクをデータベースで追跡します。このコマンドを受信すると、サーバーは次回のタスクを(存在する場合に)返します。
    • file引数の値がuではなく、run引数が設定されている場合、SmokeLoaderは最後のタスクが正しく実行されたかどうかを確認します。
  • doubles:ボットがこの引数をgetloadコマンドと一緒に指定した場合、パネルはデータベース内の関連付けられた被害者IDdoubフラグを設定します。このフラグの目的は不明です。
  • removed:ボットがremoved引数をgetloadコマンドと一緒に指定した場合、サーバーからアンインストールのリクエストが受信されたことを確認できます。パネルは、受信時にデータベースからボットIDを削除します。
  • personal: personal引数をgetloadコマンドと一緒に指定した場合、個人のタスクをリクエストできます。引数には、個人のタスクのIDを含める必要があります。構成されたタスクがデータベースでlocalとして設定されている場合、サーバーはレスポンスでファイルを返し、ボットはそれを実行します。それ以外の場合はURLが提供され、ボットは指定されたURLからコンテンツをダウンロードして実行します。

getload

 

*引数なし

このコマンドが引数なしで受信された場合、helloまたはknockのクエリーと解釈され、ボットがサーバーとのやり取りを開始するために使用されます。サーバーが引数なしでgetloadコマンドを受け取った場合、レスポンスはデータベース構成によって異なる場合があります。

  • ボットに保留中の更新がある場合、サーバーは文字列「Smku」で応答します。
  • ボットに個人のタスクがある場合、サーバーは文字列「Smki」で応答します。
  • ボットに保留中の削除リクエストがある場合、サーバーは文字列「Smkr」で応答します。
  • 残りのケースでは、サーバーは構成されたタスクの総数と各プラグインの構成ルールの順に応答します。各構成項目は「|:|」の文字列で始まり、その後にルール セットの名前とプラグインのルールが続きます。
    • |:|socks_rules=
    • |:|hosts_rules=
    • |:|shell_rules=
    • |:|fakedns_rules=
    • |:|filesearch_rules=
    • |:|procmon_rules=
    • |:|ddos_rules=
    • |:|keylog_rules=

getsocks

ボットがSOCKSプロキシ機能を有効にしたことをサーバーに通知するために使用されます。サーバーは、指定されたポート上にあるボットのIPアドレスに接続することにより、ボット プロキシがアクティブであることを検証しようとします。

gethosts

hosts_rulesで指定されたホストが正常にスプーフィングされたことを確認します。

getshell

ボットは、実行されたシェル コマンドの結果をサーバーに送信します。shell引数には結果が含まれます。

formgrab

フォーム グラバー プラグインから結果をサーバーに送信します。grab引数には、base64でエンコードされた文字列が含まれている必要があります。この文字列はデコードされると、取得されたデータのコンマ区切りリストを含む必要があります。

ftpgrab

FTPグラバー プラグインから結果をサーバーに送信します。grab引数には、盗まれたデータを含むbase64でエンコードされた文字列が含まれている必要があります。

grab

盗まれた情報をサーバーに送信します。引数データには、盗まれた情報が含まれています。

avinfo

インストールされているセキュリティ製品に関する情報をサーバーに送信します。情報はinfo引数で送信されます。送信された文字列は、区切り記号777で分割されます。最初の部分文字列には、インストールされているウイルス対策に関する情報が含まれています。2番目の部分文字列には、インストールされているファイアウォールに関する情報が含まれています。

procmon

procmon_rules構成項目のルールで構成されたプロセスのいずれかが被害者のマシンで見つかった場合、ボットはプロセスの存在についてサーバーに通知し、プロセスの名前をprocname引数で送信します。サーバーは、実行するファイルまたはファイルをダウンロードするURLで応答できます。

ddos

ddos_rulesで構成されたDDoS攻撃が正常に実行されたことを確認します。

keylog

キーロガー プラグインによってキャプチャーされた情報をサーバーに送信します。grab引数には、キャプチャーされたデータを含むbase64でエンコードされた文字列が含まれている必要があります。

getfilesearch

HTTPクエリーがapplication/binで、コマンドがgetfilesearchの場合、ボットはfilesearch_rulesの構成項目で指定されたルールに従い、filesearchプラグインによって検出されたファイルの内容をサーバーに送信します。ファイルの内容はfiledata引数で指定されます。

表3: SmokeLoader 2014のC2サーバーでサポートされているコマンド

SmokeLoaderバージョン2012では、HTTP GETリクエストを使用してコマンドと引数をプレーンテキストとして送信していましたが、バージョン2014ではネットワーク プロトコルが更新され、コマンドと引数のデータがHTTP POSTリクエスト経由で送信されるようになりました。POSTリクエスト本文は、データのサイズを含む初期のDWORDと、残りのデータの暗号化を解除するために必要なRC4キーとして機能するDWORDで構成されます。

シリーズの第2回

このシリーズの第1回では、SmokeLoaderの初期バージョンについて、初期の基本的なフレームワークからモジュール構造と暗号化されたネットワーク プロトコルの導入までを解説しました。第2回では、SmokeLoaderが高度な分析回避技術を備え、より洗練されたモジュール型マルウェア ファミリーへと進化した過程を深く掘り下げます。

Zscalerの防御策

Zscaler Sandboxによる保護

Zscalerの多層クラウド セキュリティ プラットフォームは、サンドボックス検知に加え、以下の脅威名でSmokeLoaderに関連する指標をさまざまなレベルで検知します。

Win32.Downloader.Smokeloader

form submtited
お読みいただきありがとうございました

このブログは役に立ちましたか?

Zscalerの最新ブログ情報を受信

このフォームを送信することで、Zscalerのプライバシー ポリシーに同意したものとみなされます。