PlugXローダーの進化 Published: 2022-11-30 · Archived: 2026-04-02 11:42:59 UTC はじめに FFRIセキュリティ ソフトウェアエンジニアの松本です。 PlugX というバックドアは非常に古くか ら存在しており、現在でも利用されています。 今回はその PlugX のローダーの変遷について見ていき ます。 PlugX は標的型攻撃に利用されている Remote Access Tool(以下 RAT と記載)であり、主に政府機関など を狙う標的型攻撃に利用されるツールです。 機能としては情報収集・窃取、侵入後の PC のコントロ ールなどがあります。 2012 年には既に TrendMicro の記事[1]があるほど PlugX は古くから存在します。 その一方、ここ数か 月の間にも TA416 (通称 Mustang Panda[2]) というグループが利用しているという報告[3]が上がってお ります。 日本国内では株式会社 JTB が PlugX に感染し情報漏洩したという事例が存在します[5][6]。 また、株式 会社ジャストシステムのワープロソフトである一太郎の脆弱性を利用し感染を狙う事例もありました [7][9]。 PlugX ローダーは世代に関わらず以下のような流れで動作しています。 1. ドロッパーを添付したメール経由などにより、対象組織内部でドロッパーを実行してもらう 2. ドロッパーが①正規 exe、②マルウェア dll、③バイナリファイルの 3 つを生成する 3. ドロッパーが①正規 exe を実行する https://engineers.ffri.jp/entry/2022/11/30/141346 Page 1 of 13 4. ②マルウェア dll は①正規 exe が利用する正規 dll を偽装しており、①正規 exe はそのまま②マル ウェア dll を読み込む 5. ②マルウェア dll が③バイナリファイルを読み込む 6. ③バイナリファイルが子プロセスを生成し、次の段階に移る 読み込まれるバイナリファイルの内部に暗号化されたコードなどが入っているため、実際にはその複 合など更に細かい動作が存在しますが、動作の流れは以下の概要図のようになります。 図1 PlugX動作の概要図 実際にドロップされる具体例としては下の図のようになります。 この図では RasTls.exe は正規の exe であり、RasTls.dll へのリンクが存在しています。 マルウェアバイナリにもアイコンが付いています が、これは本来 msc が Microsoft 管理コンソールに紐付けられている拡張子であるためです。 実際の中 身は全く関係無いバイナリファイルとなっています。 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 2 of 13 図2 PlugX動作例 exe が不正な dll を読み込んでしまう理由は、exe が dll を読み込む際に同じディレクトリの dll を優先 する仕様のためです。 図3 正規 exe のロードにより読み込まれる例 次の図は、LoadLibraryW 関数で同ディレクトリの dll を動的に読み込む箇所です。 このような動的な dll 読み込みの際、通称 exe は自身の現在のパスから読み込みたい dll のパスを生成 しますが、 PlugX は同ディレクトリ内にファイルを生成するため、結局すり替えられた dll を読み込む ことになります。 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 3 of 13 図4 正規 exe から LoadLibraryW 関数で読み込まれる例 この手法は DLL Side-Loading[4]と呼ばれ、デジタル署名された正規の exe を利用することで、アンチウ イルスソフトの検知回避によく利用されます。 一般に dll 内部の悪意のあるコードは entry 関数か、exe から必ず呼ばれる export 関数のどちらかに実装 されていますが、PlugX は entry 関数に悪意のあるコードが実装されています。 ここで dll における entry 関数は DllMain 関数であり、リンクなどで読み込まれた段階で必ず呼ばれる処理です。 そのた め、exe の初期化処理である dll 読み込みの段階で PlugX ローダーが動作します。 次の段階では、子プロセスもしくはサービス登録によるサービスプロセスを生成します。 実行プロセ スに管理者権限が付与されているときには、サービスプロセスを生成し、管理者権限がない時には、 子プロセスを生成します。 さて、ここまでは全世代の PlugX 共通な動作の流れについて説明しましたが、次に世代によって変化 してきた内容を説明します。 PlugXの変化 PlugX は時間と共に進化していきました。 その流れを簡潔に纏めると以下のようになります。 年代 世代 追加機能の例 引用 2012 第一世代 RAT [1] 2013 第二世代 自己解凍書庫の利用開始 [8][9] 2015 第三世代 P2P機能の追加による、感染端末間の通信 [10] 2017 第四世代 PoisonIvyのコード流用 [11] 現状 第五世代 Go言語の利用、ローダーの多様化 C2 サーバーからドロップ検体をダウンロード [3][12] 第一世代 PlugX では前述通り、まずは正規 exe から dll が読み込まれ、dll からバイナリを読み込んだ上で次のプ ロセスを生成するという動作をします。 まず注目すべきは、マルウェアバイナリのコード内部でスタ ック上に文字列を生成する stack strings という手法が利用されている点です。 この手法はバイナリに文 字列を直接埋め込まないため、解析をより困難にします。 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 4 of 13 図5 stack strings が利用されている箇所 stack strings は x86 の mov 命令や lea 命令を利用してスタック上に文字列を手動で生成する方法です。 図6 stack strings の例 上の図であれば、4 byte 長の mov 命令を利用することでスタック上に文字列を構築できます。 構築後 に esp レジスタが指している箇所を文字列として扱えば Windows API などで認識できる文字列になり ます。 上記の例では char 型でしたが、wchar_t 型でも同様に作成可能です。 図7 LoadLibraryA を 4 byte ずつ生成 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 5 of 13 図8 スタック上の GetProcAddress を 1 byte ずつ確認 スタック上に生成された文字列はバイナリ内部で利用する API のアドレスを動的に取得するために利 用されます。 ここで LZNT1 復号に利用する RtlDecompressBuffer という関数も動的解決しています。 ここで得た LZNT1 復号と XOR 復号により、子プロセスの生成に進みます。 図9 バイナリをメモリ内部で復号 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 6 of 13 図10 XOR 復号関数 図11 RtlDecompressBuffer 呼び出しによる LZNT1 復号 この復号により、メモリ内部に PE ヘッダーを持つコード領域を展開することが確認できます。 図12 マジックナンバーである MZ が確認できる 第二世代 第二世代は機能面の変化の他、メモリ内部で復号される PE ファイルのマジックナンバーが PE ではな くなっているといった変更点が報告されています[8][9]。 ドロッパーに自己解凍書庫が利用されている パターンもこの辺りから出てきます。 ここではローダー部分の変化について見ていきます。 マルウェア dll には上から順番にディスアセンブルすると、実際に実行される命令とは異なってしまう という難読化が施された関数も追加されています。 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 7 of 13 図13 不要な分岐 赤枠は以下のような役割を担っています。 1. 二行続けて同じ所にジャンプするように指定されているため、無条件ジャンプと同等 2. その直後の call 命令は、直前のジャンプを無条件ジャンプと解釈できなかった為に発生 3. e8 から命令を開始することで、意図的にコード外にジャンプする不正な call 命令と解釈させる 他にも、不要な XOR なども追加されており、処理を追いかけにくい構造になっています。 図14 不要な XOR これらの難読化処理は Ghidra のデコンパイル結果にも影響しますが、除去整理することで以下のよう にデコンパイル結果が綺麗になります。 デコンパイル結果から dll 内部のバイナリを復号するコードが 出てきたことが確認できます。 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 8 of 13 図15 デコンパイル結果 この処理以降は第一世代と同じ stack strings を利用した API 動的解決の処理に入ります。 RtlDecompressBuffer も LZNT1 復号に利用されており、XOR 復号のコードは変わっているもののこの辺 りのローダーとしての処理は第一世代と同じと見て良いでしょう。 第一世代と同じく PE ヘッダーを持つバイナリが復号されメモリに展開されますが、マジックナンバー が改変されている物となっております。 図16 XV ヘッダー 第三世代 第三世代の機能面における変化は P2P 機能の搭載などが報告されています[10]。 しかし、ローダー部分に関しては XV というマジックナンバーの出現個数の変化などの変更はあるもの の、 XOR 復号はもちろん大まかな流れには変化が無いように見受けられました。 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 9 of 13 第四世代 PoisonIvy のコードを一部流用している[11]ということで第四世代として分類されるこの世代では、 ド ロッパー自体は第三世代から引き続き自己解凍書庫であるものの、ローダー部分にも多くの変更が入 っています。 例えば、簡単なものであればマルウェア dll の entry 関数から早速 NOP、DEC、INC 命令 を使った無駄な処理を大量に配置しています。 図17 entry 関数の NOP ただし、Ghidra のデコンパイル結果には影響しておらず、比較的除去が簡単な処理になっています。 図18 entry 関数のデコンパイル結果 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 10 of 13 また、今までの世代では VirtualAlloc で取得したメモリへの展開は memcpy を利用していましたが、こ の世代からはバイナリファイルを直接 ReadFile で読み込む処理に変わっています。 ReadFile 経由の読 み込みはユーザーランドにおける WinDbg のハードウェアブレークポイントでは止まらないので、 メ モリアドレスへの書き込みイベントで止めることが出来ず、より解析が困難になっています。 WinDbg のクエリ結果から見ても、突然メモリに 0x3f の値が出てきていることが確認できます。 図19 Time Travel Debugging で確認できるメモリ操作 上記のクエリ結果の中にある Write は復号処理であることが確認できます。 図20 ReadFile で読み込んだメモリ領域を復号する箇所 また、子プロセス生成までに必要な API の取得において stack strings は利用しなくなり、代わりに随時 ハッシュ値から関数アドレスを復元する形になっています。 図21 CreateServiceA の呼び出し例 この関数アドレス取得の手法は子プロセス生成後にも沢山利用されております。 この手法は Poison Ivy と酷似していることが報告されています[11]。 API 解決に必要なモジュールのアドレス解決においては、対象のモジュール名がメモリ上にそのまま存 在しています。 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 11 of 13 図22 メモリ上にある kernel32 現在 PlugX と呼ばれる検体は現在も変化し続けていますが、 基本的に正規 exe、マルウェアである dll、そ してバイナリファイルを同じディレクトリに配置するというドロップ方式は変わっていないようで す。 一方で解析をより困難にするため C2 サーバーと接続出来なければ次の段階に進まないような改良が見 られるようになりました。 図23 検体の C2 サーバーへの接続試行 また、Go 言語製の PlugX ローダーが存在していることも報告されています[11]。 この dll のインポー ト関数からも確認できます。 図24 インポート関数の一部 この Go 言語製の dll も C2 サーバーと接続できなければ動作しないようになっていたため、 現在の主 流は解析を困難にするため、 C2 サーバー側にドロップさせたい物を配置する形式になってきたと思わ れます。 終わりに https://engineers.ffri.jp/entry/2022/11/30/141346 Page 12 of 13 PlugX は長い活動期間を通して、機能の高度化以外にもローダー部分における難読化も施していること が確認できました。 こうした変化によりマルウェア解析用のスクリプトが正常に動作しないこともあ り、検知を回避し、解析の妨害に一役買っています。 今回調査した PlugX の検体など多くのマルウェアは、以上のような検知回避や難読化などを行ってお りますが、FFRI yarai は検知エンジンの新規開発や改善などを継続的に行うことで、検知可能となって おりますのでご安心ください。 (※ただし、全ての PlugX が検出できることを保証するものではありま せん)。 エンジニア募集 FFRIセキュリティではマルウェアの解析などセキュリティの研究開発を行っています。採用に関 しては こちら をご覧ください。 参考文献 [1] 標的型攻撃に利用されるPlugXの脅威とは | トレンドマイクロ 脅威データベース [2] Mustang Panda, TA416, RedDelta, BRONZE PRESIDENT, Group G0129 | MITRE ATT&CK® [3] The Good, the Bad, and the Web Bug: TA416 Increases Operational Tempo Against European Governments as Conflict in Ukraine Escalates | Proofpoint US [4] Hijack Execution Flow: DLL Side-Loading, Sub-technique T1574.002 - Enterprise | MITRE ATT&CK® [5] JTB、約793万人分の個人情報流出の恐れ - 有効パスポート番号4,300件含む | マイナビニュース [6] 「PlugX」はどんなマルウェア? JTBを狙った標的型攻撃をファイア・アイが解説 | マイナビニュー ス [7] 一太郎の脆弱性を突くマルウェア、人事情報装うメールで日本に「着弾」 | ITmedia エンタープライ ズ [8] PlugX - The Next Generation | Sophos [9] From the Labs: New PlugX malware variant takes aim at Japan | Naked Security [10] マルウエアPlugXの新機能 (2015-01-22) - JPCERT/CC Eyes | JPCERTコーディネーションセンター公 式ブログ [11] Poison Ivyのコードを取り込んだマルウエアPlugX(2017-01-12) - JPCERT/CC Eyes | JPCERTコーディ ネーションセンター公式ブログ [12] TA416 Goes to Ground and Returns with a Golang PlugX Malware Loader | Proofpoint US Source: https://engineers.ffri.jp/entry/2022/11/30/141346 https://engineers.ffri.jp/entry/2022/11/30/141346 Page 13 of 13