Daniel Stepanic

GULOADERでねばねばする:ダウンローダーの難読化を解除する

Elastic Security Labsが、更新されたGULOADER分析対策について説明します。

13分で読めますマルウェア分析
GULOADERでねばねばする:ダウンローダーの難読化を解除する

Overview

Elastic Security Labsは、GULOADER( CloudEyE とも呼ばれる)などのアクティブな脅威を継続的に監視しています。これは、長年にわたって高い頻度で開発が続けられてきた回避型シェルコードダウンローダーです。 これらの最近の変更の1つは、新しいキャンペーンでVectored Exception Handler(VEH)に例外が追加され、すでに長いアンチアナリシストリックのリストにさらに複雑さが加わったことです。

GULOADERのコア機能は過去数年間で大幅に変更されていませんが、難読化技術のこれらの絶え間ない更新により、GULOADERの分析は時間とリソースを大量に消費するプロセスになっています。 この投稿では、GULOADERをトリアージする際の次のトピックに触れます。

  • 初期シェルコードのレビューとアンパックプロセス
  • 復号化されたシェルコードのエントリポイントを見つける
  • 制御フローを難読化するGULOADERのVEHの更新について話し合います
  • VEHにパッチを適用する方法を提供する

初期シェルコード

この サンプルでは、GULOADERはNSIS(Nullsoft Scriptable Install System)インストーラー内に事前にパッケージ化されています。 インストーラーが抽出されると、主なコンポーネントは次のとおりです。

  • NSISスクリプト - このスクリプトファイルは、さまざまな設定とインストールの側面をすべて概説しています。

  • System.dll - $PLUGINSDirの下にあります。 このファイルは、GULOADERシェルコードを割り当て/実行するために一時フォルダにドロップされます。

  • シェルコード - 暗号化されたシェルコードは、ネストされたフォルダに埋め込まれます。

シェルコードをホストしているファイルを特定する簡単な方法の1つは、GULOADERの実行後にSysInternalのProcess Monitorから ReadFile イベントを監視することです。 この場合、シェルコードはファイル(Fibroms.Hag)から読み取られていることがわかります。

GULOADERは、さまざまなWindows API関数を使用してコールバックを通じてシェルコードを実行します。 この背後にある主な理由は、プロセスインジェクションに使用される従来の Windows API ( CreateRemoteThreadWriteProcessMemoryなど) を中心とした検出を避けることです。 GULOADERが使用している EnumResourceTypesACallWindowProcW を観察しています。

EnumResourceTypesAの MSDN ドキュメントを確認すると、2 番目のパラメーターがコールバック関数へのポインターを想定していることがわかります。上のスクリーンショットから、新しく割り当てられたシェルコードがこの引数に配置されていることがわかります。

メインシェルコードのエントリポイントを見つける

最近のサンプルでは、GULOADERは多くの異なるジャンク命令とジャンプを含めることにより、初期シェルコードの開始時の複雑さを増しています。 ダウンローダーのリバースエンジニアリングでは、一部のツールで逆アセンブルと制御フローを壊すように設計されたコードの難読化を解除する長いプロセスを処理する必要があり、コアGULOADERシェルコードの実際の開始を見つけるのにイライラします。

最初の呼び出しを見つける方法の 1 つは、x64dbg 内のグラフ ビューを活用し、下から上へのアプローチを使用して call eax 命令を探すことです。

初期制御フローをトレースする別の手法には、リバースエンジニアリングフレームワークMiasmを活用することが含まれます 以下は、シェルコードを渡して手順を逆アセンブルしてフローに従うことができる簡単な例です。

from miasm.core.locationdb import LocationDB
from miasm.analysis.binary import Container
from miasm.analysis.machine import Machine

with open("proctoring_06BF0000.bin", "rb") as f:
    code = f.read()

loc_db = LocationDB()
c = Container.from_string(code, loc_db)

machine = Machine('x86_32')
mdis = machine.dis_engine(c.bin_stream, loc_db=loc_db)
mdis.follow_call = True 
mdis.dontdis_retcall = True
asm_cfg = mdis.dis_multiblock(offset=0x1400)

Miasm は 142 jmp指示をカットスルーし、EAX への呼び出し命令で停止するように設定したジャンク命令をナビゲートします (アドレス: 0x3bde)。

JMP        loc_3afd
->	c_to:loc_3afd 
loc_3afd
MOV        EBX, EAX
FADDP      ST(3), ST
PANDN      XMM7, XMM2
JMP        loc_3b3e
->	c_to:loc_3b3e 
loc_3b3e
SHL        CL, 0x0
PSRAW      MM1, MM0
PSRLD      XMM1, 0xF1
JMP        loc_3b97
->	c_to:loc_3b97 
loc_3b97
CMP        DL, 0x3A
PADDW      XMM3, XMM5
PXOR       MM3, MM3
JMP        loc_3bde
->	c_to:loc_3bde 
loc_3bde
CALL       EAX

瘴気のテールエンド

GULOADER’s VEH Update

GULOADERの特徴の1つは、 ベクトル例外処理 (VEH)機能を中心に据えています。 この機能により、Windows アプリケーションは、例外が標準の例外プロセスを通じてルーティングされる前に、例外をインターセプトして処理できます。 マルウェア ファミリとソフトウェア保護アプリケーションは、この手法を使用して、アナリストやツールが悪意のあるコードを追跡するのを困難にします。

GULOADER は、 RtlAddVectoredExceptionHandlerを使用して VEH を追加することでこのプロセスを開始します。 GULOADERシェルコードの実行中には、これらの異なる例外をトリガーするために意図的に配置されたコードがあります。 これらの例外がトリガーされると、VEH はハードウェア ブレークポイントを確認します。 見つからない場合、GULOADERは、例外が発生した場所から1バイトのオフセットを持つ1バイトのXORキー(サンプルごとに変更)を使用して、 CONTEXT構造体 を介してEIPを直接変更します。 この手法の具体例については、次のセクションで説明します。 以下は、サンプルのVEHの逆コンパイルです。

この手法は新しいものではありませんが、GULOADER は時間の経過とともに新しい例外を追加し続けます。最近、過去数か月で次の 2 つの例外が追加されていることを確認しました。

  • EXCEPTION_PRIV_INSTRUCTION
  • EXCEPTION_ILLEGAL_INSTRUCTION

GULOADERに新たな例外が追加されると、研究者の分析プロセスを迅速化するために使用されていたツールが壊れてしまう可能性があります。

EXCEPTION_PRIV_INSTRUCTION

VEHワークフローに従うために最近追加された2つの例外を見ていきましょう。 最初の例外 (EXCEPTION_PRIV_INSTRUCTION) は、プロセッサの命令セットで特権命令が許可されていない特権レベルで実行しようと試みられた場合に発生します。 次の WRSMR の例のような特定の命令では、カーネル レベルからの特権が想定されているため、プログラムをユーザー モードから実行すると、権限が正しくないために例外がトリガーされます。

EXCEPTION_ILLEGAL_INSTRUCTION

この例外は、プログラムが無効または未定義の CPU 命令を実行しようとしたときに呼び出されます。 このサンプルでは、次のようなIntel仮想化命令に遭遇すると vmclear or vmxon、これにより例外がトリガーされます。

例外が発生すると、GULOADER VEHコードは最初に、例外の原因となった例外コードを特定します。 このサンプルでは、例外が以下の 5 つのいずれかに一致する場合、コードは関係なく同じパスをたどります。

  • EXCEPTION_ACCESS_VIOLATION
  • EXCEPTION_ILLEGAL_INSTRUCTION
  • EXCEPTION_PRIV_INSTRUCTION
  • EXCEPTION_SINGLE_STEP
  • EXCEPTION_BREAKPOINT

その後、GULOADER は EXCEPTION_POINTERS 構造体内で見つかった CONTEXT レコードを走査して、ハードウェアブレークポイントをチェックします。 ハードウェアブレークポイントが異なるデバッグレジスタで見つかった場合、GULOADERはCONTEXTレコードに 0 を返し、シェルコードがクラッシュする原因となります。

ハードウェアブレークポイントがない場合、GULOADERは例外の原因となったアドレスから 7 バイト離れた1バイトを取得します。 最後の例を vmclearと共に使用すると、バイト (0x8A) が取得されます。

次に、そのバイトを使用して、ハードコードされた別のバイトでXOR演算を実行します。 私たちの場合(0xB8)、これはサンプルごとに一意です。 これで、派生したオフセット0x32 (0xB8 ^ 0x8A) を使用して、GULOADER は、例外の原因となった前のアドレス (0x7697630) に 0x32 を追加することで、CONTEXT レコードから直接 EIP アドレスを変更し、次のコードはアドレス (0x7697662から実行されます。

その間に異なるジャンク命令があり、例外を繰り返しヒットします (サンプルでは 229 個の固有の例外を数えました) ため、これがさまざまなツールを壊し、アナリストの時間を増やす理由を理解するのは難しくありません。

制御フロークリーニング

制御フローの追跡を容易にするために、アナリストは、実行をトレースし、例外をログに記録し、前述のEIP変更アルゴリズムを使用してシェルコードにパッチを適用することにより、VEHをバイパスできます。 この手順では、 @hasherezade によって作成されたツールである TinyTracer を利用しました。これは、動的バイナリインストルメンテーションフレームワークである PIN を活用しています。これにより、例外をトリガーしたさまざまなアドレスをキャッチできるため、上記の例を vmclearで使用すると、アドレスが 0x7697630され、ユーザーモードの例外の処理を担当する関数 KiUserExceptionDispatcherを呼び出す例外が生成されたことを確認できます。

すべての例外が収集され、フィルタリングされたら、これらを IDAPython スクリプトに渡して各アドレスをウォークスルーし、7 バイト目と XOR キー (0xB8) を使用してオフセットを計算し、短いジャンプで例外を生成するすべての命令にパッチを適用できます。

次の図は、アドレス の 0x076976300x0769766Cで例外をトリガーするパッチ適用手順の例です。

次の図は、パッチ適用がグローバルに適用される前の制御フローグラフを表しています。 vmclear命令を含む基本ブロックはオレンジ色で強調表示されています。VEHを実装することで、GULOADERは制御フローグラフをフラット化し、プログラムロジックのトレースを困難にします。

VEHに jmp 命令でパッチを適用した後、基本ブロックを一緒に接続することで変換し、シェルコードのフローの背後にある複雑さを軽減します。

この手法を使用すると、クリーニングプロセスを加速できますが、万全の方法ではないことに注意することが重要です。 この場合、分析が必要なコード/機能がまだ大量にありますが、これは間違いなくVEHを削除することでコードを簡素化するのに大いに役立ちます。 完全な POC スクリプトは ここにあります。

まとめ

GULOADERには、分解を壊したり、制御フローを妨げたり、研究者の分析を困難にしたりする可能性のあるさまざまな機能があります。 これとプロセスが不完全であるにもかかわらず、さまざまな静的または動的プロセスを通じてこれらの特性に対抗し、分析時間を短縮することができます。 たとえば、VEHに新しい例外がある場合でも、それらをトレースしてシェルコードにパッチを適用できることがわかりました。 このプロセスにより、アナリストは正しい道を歩むことができ、GULOADERのコア機能へのアクセスに近づくことができます。

私たちのワークフローの一部を共有することで、実際にGULOADERに出会った場合に複数のポイントを提供できることを願っています。 GULOADERの変更に基づくと、将来の行動には新しい異なる戦略が必要になる可能性が高くなります。 GULOADERの検出については、次のセクションにYARAルールが含まれており、この投稿のIDAPythonスクリプトは ここにあります。 最新の脅威調査に関する最新情報については、Elastic Security Labsチームによる マルウェア分析セクション をご覧ください。

ヤラ

Elasticセキュリティは、このアクティビティを識別するためにさまざまな YARAルール を作成しました。 以下は、GULOADER を識別するための 1 つの YARA ルールの例です。

rule Windows_Trojan_Guloader {
    meta:
        author = "Elastic Security"
        creation_date = "2023-10-30"
        last_modified = "2023-11-02"   
        reference_sample = "6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99"
        severity = 100
        arch = "x86"
        threat_name = "Windows.Trojan.Guloader"
        license = "Elastic License v2"
        os = "windows"
    strings:
        $djb2_str_compare = { 83 C0 08 83 3C 04 00 0F 84 [4] 39 14 04 75 }
        $check_exception = { 8B 45 ?? 8B 00 38 EC 8B 58 ?? 84 FD 81 38 05 00 00 C0 }
        $parse_mem = { 18 00 10 00 00 83 C0 18 50 83 E8 04 81 00 00 10 00 00 50 }
        $hw_bp = { 39 48 0C 0F 85 [4] 39 48 10 0F 85 [4] 39 48 14 0F 85 [7] 39 48 18 }
        $scan_protection = { 39 ?? 14 8B [5] 0F 84 }
    condition:
        2 of them
}

観測

すべてのオブザーバブルは、ECS形式とSTIX形式の両方で ダウンロード することもできます。

この研究では、次の観測量について議論しました。

Observableタイプ名前参考
6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99SHA-256のWindows.Trojan.GuloaderGULOADERダウンローダー
101.99.75[.]183/MfoGYZkxZIl205.binURLNAのGULOADER C2 URL
101.99.75[.]183IPv4-アドレスNAのグローダーC2 IP

参照資料

この記事を共有する