マルウェア
サイバー攻撃集団「Void Dokkaebi」のCythonコンパイル版マルウェア「InvisibleFerret」の解析
北朝鮮に関する攻撃集団であるVoid Dokkaebiは、情報窃取型マルウェア「InvisibleFerret」を更新し、スクリプトベースの検出を回避するために配布形式を変更しています。
- サイバー攻撃集団「Void Dokkaebi(別名Famous Chollima)」は、マルウェア「InvisibleFerret」を可読性のあるPythonスクリプトからCythonコンパイル済みバイナリへと移行し、Windowsでは.pydファイル、macOSでは.soファイルとして配布しています。
- 今回の更新により、同攻撃集団は追加の回避層を獲得しつつも、バックドアアクセス、ブラウザ認証情報の窃取、クリップボード監視、キーロギング、暗号資産ウォレット狙いといったInvisibleFerretの中核機能は維持されています。さらにBeaverTailも、当初のダウンローダおよびスティーラーという役割を超えて、InvisibleFerretと機能の重なる、より広範なマルウェアへと拡張されており、認証情報窃取やウォレットのトロイの木馬化といった機能も備えるようになっています。
- 本キャンペーンは、ソフトウェア開発者、暗号資産利用者、およびウォレットの認証情報、署名鍵、CI/CDパイプライン、本番システムへのアクセス権を持つ開発者を抱える組織にとって、特に注意すべきものとなっています。
- 防御側は、拡張モジュール、埋め込まれたアーティファクト、実行時のPythonスクリプト、ブラウザ拡張機能の改ざんに対応するため、スクリプトのみを対象とした検出から、バイナリも視野に入れた検出へと移行する必要があります。
- Void Dokkaebiに関連する脅威の特定と緩和に役立てていただくため、ハンティングルールおよび侵入の痕跡(IoC)を以下に掲載しています。
はじめに
「Void Dokkaebi(Famous Chollimaとしても追跡)」は北朝鮮に関するサイバー攻撃集団であり、暗号資産ウォレットの認証情報、署名鍵、継続的インテグレーション/継続的デリバリ(CI/CD)パイプラインおよび本番インフラへのアクセス権を持つソフトウェア開発者を、組織的に標的としています。TrendAI™ Researchの過去のレポートで報告したとおり、同攻撃集団は暗号資産企業やAI企業のリクルーターを装い、偽の採用面接の一環として開発者にコードリポジトリのクローンと実行を促します。
TrendAI™ Researchは、感染チェーンを通じて配信される複数のモジュールから構成されるPythonベースのマルウェアファミリ「InvisibleFerret」が、Cythonによって難読化されていることを確認しました。
Cythonとは、PythonコードをC/C++ソースコードに変換したうえでネイティブバイナリにコンパイルし、実行速度を向上させるツールです。その結果、InvisibleFerretは平文のPythonスクリプトではなく、Windowsでは.pydファイル(DLL形式のPython拡張モジュール)、macOSでは.soファイル(共有ライブラリ)として配布されるようになっています。
Cythonで生成されたバイナリは単体で実行可能なファイルではなくPython拡張モジュールであるため、それ単独では実行できず、読み込むためのPythonスクリプトまたはインタプリタが必要です。そのため、感染チェーンではCythonで難読化されたInvisibleFerretを実行するためのPythonスクリプトを生成します。
検出回避の観点から見ると、こうした変更により、Pythonスクリプトを対象とした既存の検出ルールではマルウェアを特定できなくなる可能性があります。CythonバイナリからはバイナリアナリシスによってIPアドレスとポート番号を抽出できますが、実行時のPythonスクリプトがコマンドライン引数として別のC&C宛先を渡すことで、これらの値が上書きされる可能性があります。そのため、一部のモジュールについては、付随する実行用スクリプトがなければ、バイナリ単体から実際のC&C宛先を特定することはできません。
感染チェーン:マルチプラットフォーム標的化
本キャンペーンにおける初期攻撃ベクトルは確認されていませんが、Void Dokkaebiはこれまで偽の採用面接を通じてソフトウェア開発者を標的としてきました。感染チェーンを分析した結果、BeaverTailとInvisibleFerretの双方に顕著な変化が見られます。
BeaverTailは現在、InvisibleFerretと類似した機能を備えるマルチステージのコンポーネントとして動作するようになっています。これにより攻撃者は、開発言語であるJavaScriptのみで完結する感染チェーンを構築できるだけでなく、プラットフォーム別のInvisibleFerret(例:mod.pyd、mod.so)をダウンロードしてInvisibleFerretの感染チェーンも併せて成立させることが可能となります。
BeaverTailの解析
BeaverTailは当初、データ窃取(ブラウザ認証情報、暗号資産ウォレットなど)とInvisibleFerretペイロードの配信の両方を担っていました。新しい亜種のBeaverTailは、InvisibleFerretと類似した機能を備えるようになっています。さらに、BeaverTailの難読化手法は以前のバージョンと比較して複雑化しており、複数層からなる文字列保護とデコードロジックを採用しています。
- 配列のシャッフルとインデックス参照: 起動時に、約300個のBase64フラグメントを格納する大きな配列を、即時実行関数式(IIFE)でシャッフルします。次に、ルックアップ関数を用いて各フラグメントを16進インデックスから取り出します。
- 文字除去を伴うBase64エンコード: 各エンコード文字列の先頭1文字は、Base64の単純な検出を回避するためにランダムに挿入されたゴミバイトです。
- XOR暗号化: ファイルパスや実行コマンドなど、最も機密性の高い文字列は4バイト鍵によるXOR暗号化で保護されています。
- 分割・交換によるIPアドレスのエンコード: C&CのIPアドレスは2つに分割され、Base64デコードの前に前後が入れ替えられます。
BeaverTailの亜種
BeaverTailは、同一の難読化手法を用いる複数の亜種から構成されるマルウェアへと進化しました。元々の情報窃取・ダウンローダモジュールに加え、現在ではバックドア、ブラウザ情報窃取、トロイの木馬化された暗号資産ウォレットのインストールといったモジュールも備えています。
これらの亜種は2025年10月末までに確認されました。ダウンロード時に使用されるファイル名で識別されます。表1は、各BeaverTail亜種の主な機能を示したものです。
InvisibleFerretのダウンロードプロセス
Cythonで難読化されたInvisibleFerretは、BeaverTail(gjs)によってダウンロードされます。コード上では、図8に示すように、Windows環境および非Windows環境のいずれにおいても、InvisibleFerretの感染チェーンを開始するために _rum() 関数が使用されます。
InvisibleFerretのダウンロードと実行を担うのは、dnp_m() 関数および dnp() 関数です。Windows版の dnp() 関数を図9に示します。
過去に観測されたBeaverTail(gjs)のサンプルでは、InvisibleFerret(main)は /client/{sType} という形式のURLパスからダウンロードされており、sType は攻撃者が用いる識別子でした。ファイルは main_{sType}.py という名前で保存されます。本キャンペーンでは、InvisibleFerretは /clw/{sType} からダウンロードされます。
その後、.mod ファイルが作成され、Pythonを介して実行されます。非Windows環境ではペイロードが /clw1/{sType} からダウンロードされ、同様の .mod ファイルが作成・実行されます。ただし、このバイナリはMach-O形式であり、macOS環境でのみ動作します。
Cythonで生成されたバイナリは単体で実行可能なファイルではなく、拡張モジュールとしてCPythonランタイムに依存します。そのため、Pythonスクリプトまたはインタプリタを介して起動する必要があります。BeaverTailは、この目的のために .mod ファイルを作成・実行します。
実行フローを図10に示します。BeaverTail(gjs)はJavaScriptベースのモジュールであるため、Cython拡張モジュールを直接読み込むことができません。代わりに、以下の処理を行います。
- C&CサーバからCythonバイナリ(mod.pyd または mod.so)をダウンロード
- Pythonスクリプト(.mod)をディスクに書き出し
- Pythonインタプリタを呼び出して実行
続いて .mod スクリプトは、mod.pyd または mod.so をPython拡張モジュールとしてインポートし、sType、エンコードされたIPアドレス、ポート番号をコマンドライン引数として渡します。Cythonモジュールは、図10に示すように、埋め込まれたPythonペイロードを難読化解除し、exec によって実行します。
Cythonで難読化されたInvisibleFerretの解析
Cythonコンパイルは、元のPythonソースをネイティブバイナリに変換することで難読化します。この手法を踏まえれば、防御側はPythonスクリプトに対する単純なテキストベースの検索に依存すべきではありません。
ただし、生成されたバイナリには多数のフォレンジック上のアーティファクトが残されます。Cythonで難読化されたInvisibleFerretのバイナリには、プログラミング関連のアーティファクトが保持されています。Windows環境では、エクスポートテーブル中の PyInit_ といった初期化関数名から、ユーザ定義のモジュール名を特定できる場合があります。
加えて、バイナリ内を検索すると、.pyx や .c などのソースファイルへの参照が文字列として埋め込まれていることがあり、元の開発環境やビルド時の設定に関する手がかりが得られます。例えば mod.pyd では、エクスポートテーブルに依然として PyInit_mod が含まれています。「mod」をキーワードにバイナリ文字列を検索すると、図11に示すように mod.pyc、mod.c、mod.lambda などへの参照が確認できます。
macOS環境では、以下のファイルパスが文字列としてバイナリ内に保持されていました。
/Users/administrator/Pictures/Work/py\_module\_work/build/temp.macosx-10.13-universal2-cpython-312/mod.o
/Users/administrator/Pictures/Work/py\_module\_work/build/temp.macosx-10.13-universal2-cpython-312/pad.o
/Users/administrator/Pictures/Work/py\_module\_work/build/temp.macosx-10.13-universal2-cpython-312/brw.o
/Users/administrator/Pictures/Work/py\_module\_work/build/temp.macosx-10.13-universal2-cpython-312/mc.o
拡張子 .o のファイルはオブジェクトファイルと呼ばれ、CPU向けにコンパイルされた機械語コードです。ファイルパスからは、コンパイルに用いられた環境を読み取れます。macOSのビルド環境では、ユーザ名として administrator が使用され、Pictures が作業ディレクトリとなっていました。プロジェクト名は py_module_work と推測されます。
Cythonで難読化されたInvisibleFerretでは、図12(右)に示すように、モジュール内のPython文字列定数がZlibで圧縮され、バイナリのセクションに格納されていることが確認されました。これは、Cythonコンパイル時に 以下が有効化されていたことを示唆します。
CYTHON_COMPRESS_STRINGS
図12に示すように、PyMemoryView_FromMemory 関数の引数を確認することで、Zlibで圧縮された文字列定数のオフセットとサイズを取得できます。Zlibで解凍すると、図13に示すような文字列テーブルが得られます。
フォーマットは以下のとおりです。
[XOR Encoded IP address] ? [Module Name].pyx [identifiers] [Base64 blob]
実際に、後述するXORエンコードを 91d840f599206f13 に適用すると、IPアドレス 4559160199 が得られます。ポート番号は、図14に示すようにバイナリファイル内に定義されています。
続いて、図15に示すように、埋め込まれたPythonペイロードが PyRun_StringFlags() を介して呼び出されます。
文字列テーブルからは、以下の識別子が確認できます。
- zlib
- base64
- decompress
- b64decode
- __import__
PySlice_New 関数からは 、[::1] が使用されていることが分かります。これらの情報から、図16に示すように、難読化手法は従来バージョンのInvisibleFerretで使用されていたものと同一であることが判明します。ラムダ関数によってBase64エンコード文字列を逆順にし、結果をBase64デコードしたうえでZlibにより解凍する処理を、反復的に繰り返します。この難読化解除と展開を行うコードは、CythonによってC/C++ソースコードに変換され、コンパイルされていました。
この知見は、防御側にとって特に重要です。Cythonによる難読化が施されているものの、難読化解除の中核ロジックは従来バージョンから変わっていません。過去のInvisibleFerretの手法に精通したアナリストであれば、同じ手法でコンパイル済みバイナリから埋め込みPythonペイロードを復元することが可能です。
したがって、難読化解除後のコードは依然としてPythonスクリプトです。IPアドレスとポート番号は mod.pyd や mod.so 内に定義されていますが、Cythonで生成されたバイナリの実行にはPythonスクリプトが必要です。この目的のために、図11に示した .mod のような実行用Pythonスクリプトが使用されます。これらのスクリプトは、sType、2つのエンコード済みIPアドレス、ポート番号といった特定のコマンドライン引数を mod.pyd または mod.so に渡すよう設計されています。
難読化解除後のInvisibleFerretには、新たに start 関数および dcp 関数が導入されています。まず、図17に示す start 関数が実行されます。
start 関数は、図18に示す dcp 関数に引数の値を渡して接続先を取得します。さらに、次段ペイロードを実行するためのPythonスクリプト pad0 および brw0 を生成します。
dcp 関数は、引数として受け取った16文字のXORエンコード文字列から、XORベースのルーチンを用いてIPアドレスを再構築します(図19)。このような仕組みにより、接続先は実行時に実行用Pythonスクリプトから渡された引数から取得されるか、あるいはバイナリファイル内にハードコードされた値が使用されます。さらに、一部の亜種では実行後に実行用Pythonスクリプトを削除します。表2は、どのモジュールが実行時に引数を受け取るか、またどのモジュールがスクリプト削除機能を備えているかをまとめたものです。
防御側は、mc.so を除き、図19に示すコードを利用することで、InvisibleFerretの実行用Pythonスクリプトから接続先を特定できます。現時点で実行用Pythonスクリプトを削除するのは mc.so のみです。他のモジュールでも実行用Pythonスクリプトを削除すれば接続先も同様に隠蔽できるため、残りのモジュールについても今後、実行用Pythonスクリプトを削除するよう更新されると見込まれます。
本調査で確認された、Cythonで難読化されたInvisibleFerret亜種の主な機能を表3に示します。
元のInvisibleFerret(mc)はMetaMaskのみを標的としていましたが、図20に示すように、現在の mc.so はCoinbaseおよびPhantomも標的としています。今後、標的となる暗号資産ウォレットの数はさらに増加すると見られます。
トロイの木馬化された暗号資産ウォレットコンポーネントをChrome拡張機能として動作させるため、攻撃者はmacOS上のChromeのバージョンをダウングレードさせます。これは、GoogleがChrome拡張機能に対して強制しているManifest V3への移行を回避するために行われています。新しい拡張機能仕様では、暗号資産ウォレットの中継や改ざんに必要な機能が提供されないためです。そのため、攻撃者はManifest V2を依然としてサポートしているバージョンへとChromeをダウングレードします。Brave Browserも、現時点ではManifest V2の限定的なサポートしか提供していないため、標的とされています。
この感染チェーンでは、AnyDeskの実行環境のダウンロードおよび設定に使用されているInvisibleFerretのany亜種は、Cythonへの移行途上にあるようです。ダウンロードされるコードは依然としてPythonスクリプトであり、図21に示すように、難読化コードの前に sType と116文字のXORエンコード文字列を含んでいます。
しかし、難読化解除後のInvisibleFerret(any)のコードには、20文字のBase64エンコード文字列を10文字ずつに分割してIPアドレスを復元する、従来バージョンの旧式コードが依然として含まれています。新しい16文字のXORエンコード文字列をデコードする関数は含まれていません(図22)。さらに、20文字のBase64エンコード文字列がコメントアウトされているため、実行は失敗します。
技術的なアーティファクトとTTP、ならびにBeaverTailおよびInvisibleFerretとのコードおよびインフラの重複に基づき、TrendAI™ Researchは本キャンペーンをVoid Dokkaebiによるものと高い確度で評価しています。
まとめ
Void DokkaebiによるCythonコンパイル済みマルウェアの採用は、同攻撃集団の能力の進化を示しています。Cythonによる難読化は、可読性のあるPythonスクリプトをネイティブバイナリへと変換することで、従来のPythonスクリプトベースの検出を回避します。InvisibleFerretは現在、Windowsでは .pyd ファイル、macOSでは .so ファイルとして配布されています。
元のソースコードを直接読むことはできなくなりましたが、本調査により、根底にある難読化手法は従来バージョンから変化していないことが明らかになりました。プログラミングのアーティファクト、ビルド環境のパス、文字列テーブルは依然としてバイナリから復元可能であり、防御側はバイナリアナリシスによって亜種の特定やC&Cインフラの抽出を行うことができます。mcモジュールのウォレットトロイの木馬化機能(特にmacOSにおけるChromeダウングレード攻撃)も、攻撃者が最新のブラウザセキュリティ機構を回避しようとしていることを示しています。
こうした高度化が見られる一方で、本キャンペーンには開発が継続中であることを示す明確な兆候も見られます。any.py コンポーネントにおける変数定義の不備や機能の欠落は、攻撃者がCythonへの移行を完全に完了させるうえで課題を抱えていることを示唆しています。
感染チェーンの中には、InvisibleFerretと同等の機能を備えたBeaverTailの亜種も存在します。それでもなお、BeaverTailは引き続きInvisibleFerretをダウンロードし実行し続けています。この2つのマルウェアファミリは異なるプログラミング言語で開発されているにもかかわらず、機能は大幅に重複しています。このことから、攻撃者がなぜ感染チェーンの中でBeaverTailとInvisibleFerretの両方を維持し続けているのかという疑問が生じます。推測の域を出ませんが、共有のC&Cサーバが使用されていることから、特定のプログラミング言語を軸に編成されたマルウェア開発者クラスタが存在することがうかがえます。
観測されたCython移行の未完了状態と活発な開発パターンから判断すると、Void DokkaebiはBeaverTailとInvisibleFerretの双方を引き続き改良していくと見込まれます。防御側はまた、さらなるプラットフォームを標的とする、トロイの木馬化された暗号資産ウォレット拡張機能の拡大も予期しておくべきです。
TrendAI™ Researchは、引き続きVoid Dokkaebiおよび関連キャンペーンを監視し、進化する脅威に組織が先んじて対応できるよう、実用的なインテリジェンスを提供してまいります。TrendAI™の包括的な脅威インテリジェンスと高度な検出能力の組み合わせにより、組織は暗号資産や機微な企業データを狙う高度な攻撃から保護され続けることが可能となります。
TrendAI Vision One™ Threat Intelligence Hub
TrendAI Vision One™ Threat Intelligence Hubでは、新たな脅威や攻撃者に関する最新の知見、TrendAI™ Researchによる限定公開の戦略レポート、TrendAI Vision One™プラットフォーム上のTrendAI Vision One™ Threat Intelligence Feedをご利用いただけます。
新たな脅威: Void Dokkaebi Adopts Cython-Compiled InvisibleFerret(Void DokkaebiのCythonコンパイル版InvisibleFerretマルウェアの解析)
攻撃者: https://portal.xdr.trendmicro.com/index.htmlVoid Dokkaebi
TrendAI Vision One™ Intelligence Reports(IoC Sweeping)
Void Dokkaebi Adopts Cython-Compiled InvisibleFerret(Void DokkaebiのCythonコンパイル版InvisibleFerretマルウェアの解析)
ハンティングクエリ
TrendAI Vision One™ XDR Data Explorer App
TrendAI Vision One™をご利用のお客様は、XDR Data Explorer Appを使用することで、本ブログ記事で取り上げた不正な痕跡を、ご自身の環境内のデータと照合・ハンティングできます。
eventSubId:(101 or 109) AND objectFilePath:( "\.vscode\mod.pyd" OR "/.vscode/mod.so" OR "\.vscode\pad.pyd" OR "\.vscode\brw.pyd" OR "/.vscode/pad.so" OR "/.vscode/brw.so" OR "/.vscode/mc.so" OR "\.vscode\.mod" OR "\.vscode\pad0" OR "\.vscode\brw0" OR "/.vscode/.mod" OR "/.vscode/pad0" OR "/.vscode/brw0" OR "/.vscode/mc0")
eventSubId:2 AND processCmd:( "\.vscode\mod.pyd" OR "/.vscode/mod.so" OR "\.vscode\pad.pyd" OR "\.vscode\brw.pyd" OR "/.vscode/pad.so" OR "/.vscode/brw.so" OR "/.vscode/mc.so" OR "\.vscode\.mod" OR "\.vscode\pad0" OR "\.vscode\brw0" OR "/.vscode/.mod" OR "/.vscode/pad0" OR "/.vscode/brw0" OR "/.vscode/mc0")
TrendAI Vision One™では、Threat Intelligence Hubの利用権限を有効化することで、追加のハンティングクエリをご利用いただけます。
侵入の痕跡(IoC: Indicators Of Compromise)
本記事に関する侵入の痕跡は、こちらをご参照ください。
参考記事
Analyzing Void Dokkaebi’s Cython-Compiled InvisibleFerret Malware
By: Kazuki Fujisawa
翻訳:与那城 務(Platform Marketing, Trend Micro™ Research)