Zscalerのブログ
Get the latest Zscaler blog updates in your inbox
購読するSmokeLoaderの歴史 - 第1回
はじめに
このブログは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の進化を表した年表
2011~2013年:最初期
私たちがSmokeLoaderのサンプルを最初に分析した時期は、2011年にさかのぼります。これらのサンプルには顕著な違いがあり、その後のバージョンよりも初歩的です。この最初のSmokeLoaderにはバージョン番号が含まれていないため、これを最初期と呼びます。
これらの初期バージョンには、コマンド&コントロール(C2)サーバーとの初期通信を確立するための基盤となる初期モジュールが含まれています。このマルウェアは埋め込まれた2つのシェルコードを利用し、それらを新たに作成された2つのsvchost.exe
インスタンスに注入することでこれを可能にします。最初期にはコードが何度か変更され、一部のサンプルでは共有セクションを作成することで(ZwCreateSection
とZwMapViewOfSection
を使用)プロセス インジェクションを実行し、メイン プロセスのスレッドを再開して(ZwResumeThread
を使用)、注入されたコードを実行しました。
2012年の別のサンプルでは、非同期プロシージャー コール(APC)キュー手法を使用し、空洞化されたsvchost.exe
プロセスにSmokeLoaderを注入しています。SmokeLoaderがシェルコードを作成し、APCキューのインジェクション手法によってシェルコードをsvchost.exe
に注入するプロセスは、以下の図のとおりです。
図2:シェルコードを作成し、svchost.exe
に注入するSmokeLoaderの初期バージョン
シェルコードの1つがgetload
コマンドを(login
引数とともに) C2サーバーに送信し、他のシェルコードはgetgrab
コマンドでC2サーバーにクエリーを実行します。
分析中のSmokeLoaderサンプルに見られたsvchost.exe
シェルコードは以下の図のとおりです。
図3: 2012年頃のSmokeloaderサンプルのsvchost.exe
シェルコード
ボットは、getload
コマンドを使用し、HTTP GETリクエストを介してC2サーバー内に自身を登録します。Smokeの初期バージョンのネットワーク トラフィックの例を以下に示します。
図4: SmokeLoaderバージョン2012からのC2リクエストの例
SmokeLoaderは、以下の表のとおり、getload
コマンドを使用して2つのパラメーターをC2サーバーに送信します。
引数 | 概要 |
---|---|
| ボットIDを指定するもので、被害者のコンピューター名の単純なMD5ハッシュとして計算されます。 |
| ハードコードされた販売者ID (別名アフィリエイトID)を指定するもので、このIDはサンプルごとに異なります( |
表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を示す文字列
このバージョンにおける最も興味深い機能として、マルウェアがいくつかの読み込み段階に分割されたことが挙げられます。ステージャー コンポーネントの導入は、重要な変化を示し、その後のすべてのバージョンで標準となりました。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
に注入する仕組み
SmokeLoaderのステージャーは、その後のバージョンで大きな進化を遂げ、高度な難読化技術と追加の分析回避策が組み込まれました。ただ、このバージョンでも、基本的な難読化の手法の存在は確認できます。
たとえば、SmokeLoaderは呼び出された関数のXOR暗号化のレイヤーを解き明かすために、ポリモーフィック型でない復号ループを採用しています。その復号は以下の図のとおりです。
図7: SmokeLoaderバージョン2014におけるステージャー関数の復号
永続化
SmokeLoaderの販売者は、永続化の有無にかかわらず、脅威アクターにサンプルを作成するオプションを提供しています。被害者のシステムに対する永続化を実現するためのSmokeLoaderのアプローチは、時代とともに多くの変更が加えられてきました。以前のバージョン(2011~2017)では、一般的な実行レジストリー キーを活用し、(レジストリー値の設定に失敗した場合の)フォールバック オプションとしてスタートアップ ショートカットを作成していました。さらに、変更されたレジストリー キーを保護するために、2つの専用スレッドを確立していました。
ボットID
2011年頃から2012年にかけてのSmokeLoaderの初期バージョンでは、ボットIDが被害者のマシンのコンピューター名の単純なMD5ハッシュを取得して生成されることを確認しました。ボットIDを生成するアルゴリズムは、時代とともに軽微な変更が加えられてきています。特に、バージョン2014では、CRC32とXORベースのアルゴリズムが採用されました。
2014年以降のどのバージョンも、コンピューター名とボリューム情報の両方を使用してIDを計算します。そのコードは以下の図のとおりです。
図8: SmokeLoaderバージョン2014におけるボットIDの生成
分析回避技術とプレーンテキスト文字列
SmokeLoaderバージョン2014のメイン モジュールは、以下の図に示すように、マルウェアの文字列をプレーンテキストで格納しました。これは、文字列が暗号化されていた初期バージョンとは大きく異なります。
図9: SmokeLoaderバージョン2014におけるプレーンテキスト文字列
これは、sbiedll
、dbghelp
、qemu
、virtual
、vmware
、xen
という文字列を検索し、マルウェア分析環境に関連するライブラリーやプロセスを確認する最初のバージョンです。分析環境が検出されると、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に実装されたシンプルなコピー防止メカニズム
このマルウェアは、C2 URL文字列のCRC32値を計算し、コード内のさまざまなポイントで事前定義された期待値と比較します。このメカニズムは、他のハッカーがサンプルを新しいC2でパッチするビルダーを作成できないようにすることで、SmokeLoaderの売上の減少を防ぐ目的で設計されたと考えられます。
通信
SmokeLoaderバージョン2014の通信プロトコルは、以前のバージョンと類似しており、暗号化されてC2サーバーに送信されるシンプルなテキストベースのプロトコルを使用します。そのプロトコルの構文は以下のとおりです。
arg1=value1&arg2=value2…&argN=valueN
バージョン2014パネルは、以下の引数を認識します。
引数 | 概要 |
---|---|
| C2パネルは、一連のコマンドを受け入れます。指定したコマンドによっては、追加の引数を指定する必要があります。 |
| この引数はボットIDであり、その長さは40バイトである必要があります。 |
| 一部のコマンドで提供される追加情報です。 |
| OSのバージョンです。 |
| 被害者のWindows OSのアーキテクチャーです。 |
| 主に |
| あらゆる目的のさまざまなコマンドで使用されます。たとえば、更新をリクエストする、更新が正常に実行されたことを示す場合などです。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| ファイルが提出されると、 |
表2: SmokeLoaderバージョン2014におけるネットワーク プロトコル
このパネルは、以下のコマンド(cmd
引数で指定されたコマンド名)を処理できます。
コマンド(cmd) | 概要 |
---|---|
*追加の引数と一緒に使用 | メイン コマンドの1つであり、指定された引数によって異なります。
|
*引数なし | このコマンドが引数なしで受信された場合、
|
| ボットがSOCKSプロキシ機能を有効にしたことをサーバーに通知するために使用されます。サーバーは、指定されたポート上にあるボットのIPアドレスに接続することにより、ボット プロキシがアクティブであることを検証しようとします。 |
|
|
| ボットは、実行されたシェル コマンドの結果をサーバーに送信します。shell引数には結果が含まれます。 |
| フォーム グラバー プラグインから結果をサーバーに送信します。 |
| FTPグラバー プラグインから結果をサーバーに送信します。 |
| 盗まれた情報をサーバーに送信します。引数データには、盗まれた情報が含まれています。 |
| インストールされているセキュリティ製品に関する情報をサーバーに送信します。情報は |
|
|
|
|
| キーロガー プラグインによってキャプチャーされた情報をサーバーに送信します。grab引数には、キャプチャーされたデータを含むbase64でエンコードされた文字列が含まれている必要があります。 |
| HTTPクエリーがapplication/binで、コマンドが |
表3: SmokeLoader 2014のC2サーバーでサポートされているコマンド
SmokeLoaderバージョン2012では、HTTP GETリクエストを使用してコマンドと引数をプレーンテキストとして送信していましたが、バージョン2014ではネットワーク プロトコルが更新され、コマンドと引数のデータがHTTP POSTリクエスト経由で送信されるようになりました。POSTリクエスト本文は、データのサイズを含む初期のDWORD
と、残りのデータの暗号化を解除するために必要なRC4キーとして機能するDWORD
で構成されます。
シリーズの第2回
このシリーズの第1回では、SmokeLoaderの初期バージョンについて、初期の基本的なフレームワークからモジュール構造と暗号化されたネットワーク プロトコルの導入までを解説しました。第2回では、SmokeLoaderが高度な分析回避技術を備え、より洗練されたモジュール型マルウェア ファミリーへと進化した過程を深く掘り下げます。
Zscalerの防御策
Zscalerの多層クラウド セキュリティ プラットフォームは、サンドボックス検知に加え、以下の脅威名でSmokeLoaderに関連する指標をさまざまなレベルで検知します。