ETWプロバイダーに対する直接カーネル・オブジェクト操作(DKOM)攻撃。

夜遅くまで作業しながらコンピューターの画面を見ている男性の横顔のポートレート

執筆者

Ruben Boonen

CNE Capability Lead, Adversary Services

IBM X-Force

この投稿では、IBM Security X-Force Redの攻撃的ハッカーが、昇格された特権を持つ攻撃者がそのアクセスを利用して、Windows Kernelのエクスプロイテーション後の機能をステージングする方法を分析しています。ここ数年、公開されているアカウントでは、それほど洗練されていない攻撃者が目的を達成するためにこの手法を使用していることがますます明らかになっています。したがって、この機能に焦点を当て、その潜在的な影響について学ぶことは重要です。具体的には、この投稿では、カーネル・エクスプロイテーションを使用してETWセンサーを盲点化する方法と、それを昨年実際に特定されたマルウェア・サンプルに結び付ける方法について評価します。

イントロ

時間の経過とともに、Windows上のセキュリティー軽減と検知テレメトリーは大幅に向上してきました。適切に構成された EDR(エンドポイントの検知と対応) ソリューションとこれらの機能を組み合わせると、エクスプロイテーション後に対する重要な障壁となります。攻撃者は、検知のヒューリスティックを回避するために、戦術、技術、手順(TTP)を開発し、それを反復するための絶え間ないコストに直面しています。IBM Security X-Forceの敵対者シミュレーション・チームでも、この同じ問題に直面しています。私たちのチームは、大規模かつ最も厳しい環境のいくつかにおいて、高度な脅威機能をシミュレートする任務を負っています。複雑で微調整されたセキュリティー・ソリューションと、十分な訓練を受けたセキュリティー・オペレーション・センター(SOC)チームの組み合わせが、トレードクラフトには非常に重い負担をかけます。場合によっては、特定の(通常は特定のテクノロジー・スタックに関連付けられている)TTPの使用が3~4カ月程度で完全に時代遅れになることもあります。

攻撃者は、Windowsカーネルでのコード実行を活用して、これらの保護策の一部を改ざんしたり、多数のユーザー・ランド・センサーを完全に回避したりすることを選択する場合があります。こうした機能のデモンストレーションは、1999 年にPhrack Magazineで初めて公開されました。この間に、脅威アクター(TA)がエクスプロイテーション後の目的にカーネル・ルートキットを使用したケースが数多く報告されています。古い例では、Derusbi Familyや Lambertsツールキットなどがあります。

従来、この種の機能は主に高度なTAに限定されていました。しかし、近年では、コモディティ攻撃者がエンドポイント上でのアクションを容易にするために、 Bring Your Own Vulnerable Driver (BYOVD)エクスプロイテーション・プリミティブを使用するケースが増えています。実際にこれらの技術は、単純なタスクに限定された極めて原始的なものでしたが、より高性能なデモンストレーションも存在しています。

2022年9月末、ESETの研究者たちはLazarusのTAがベルギーやオランダのエンティティに対しデータ流出を目的とした複数の攻撃で使用した、カーネル機能に関するホワイト・ペーパーを発表しました。このペーパーでは、ペイロードがOS/AV/EDRテレメトリーを盲点化するために使用する多数のDirect Kernel Object Manipulation(DKOM)プリミティブについて説明しています。これらの技術に関する公的研究はほとんどありません。カーネル・エクスプロイト後のトレードクラフトをより徹底的に理解することは、防衛のために重要です。よく聞く、古典的で単純な議論は、昇格された権限を持つ攻撃者は何でもできるので、そのシナリオで機能をモデル化する必要性がどこにあるのか、というものです。これは弱いスタンスです。防御側は、攻撃者がどのような機能を持っているのか、どのようなデータ・ソースが信頼できるのか(また、どのデータ・ソースが信頼できないのか)、どのような封じ込めオプションが存在するか、および高度な技術が(たとえその検知を実行する機能が存在しない場合でも)どのように検知されるかを理解する必要があります。この投稿では、特にKernelEvent Tracing forWindows(ETW)構造体にパッチを当てて、プロバイダを無効または操作不能にすることに焦点を当てていきます。この手法について背景を説明し、攻撃者がカーネルETW構造を操作する方法を分析し、これらの構造を見つける仕組みについていくつかお話しします。最後に、この手法がLazarusによってペイロードにどのように実装されたかをレビューします。

The DX Leaders

AI活用のグローバル・トレンドや日本の市場動向を踏まえたDX、生成AIの最新情報を毎月お届けします。登録の際はIBMプライバシー・ステートメントをご覧ください。

ご登録いただきありがとうございます。

ニュースレターは日本語で配信されます。すべてのニュースレターに登録解除リンクがあります。サブスクリプションの管理や解除はこちらから。詳しくはIBMプライバシー・ステートメントをご覧ください。

ETW DKOM

ETWは、Windowsオペレーティングシステムに組み込まれた高速トレースの設備です。アプリケーション、ドライバー、オペレーティングシステムによるイベントとシステム・アクティビティのログ記録を可能にし、デバッグ、性能分析、セキュリティー診断のためにシステム動作を詳細に可視化します。

このセクションでは、カーネルETWとそれに関連する攻撃対象領域の概要を説明します。これは、ETWプロバイダーの操作に関わる仕組みや、その操作に関連する影響をより深く理解する上で役立ちます。

カーネルETW攻撃対象領域

Binarlyの研究者たちはBHEU 2021で講演を行い、Windows上のETWの一般的な攻撃対象領域について議論しました。脅威モデルの概要は以下のとおりです。

脅威モデリングETWを示すフロー・チャート
図1 – Veni、No Vidi、No Vici:ETW盲目化EDR(エンドポイントの検知と対応)センサーに対する攻撃(Binarly)

この記事では、カーネル空間の攻撃対象領域に焦点を当てています。

カーネル・モードETWプロバイダーに対する攻撃を示し、説明するグラフ
図2 – Veni、No Vidi、No Vici:ETW盲目化EDR(エンドポイントの検知と対応)センサーに対する攻撃(Binarly)

この投稿では、「図2」に示す最初の攻撃カテゴリー内の攻撃のみを考慮し、トレースが無効化されているか、何らかの方法で変更されています。

注意として、Windowsの不透明な構造を検討する場合、これらは変更される可能性があり、実際にはWindowsのバージョン間で頻繁に変更されることを覚えておくことが常に重要です。これは、カーネル・データを破棄する場合には特に重要です。間違いがあると、死のブルー・スクリーン(BSoD)が発生する可能性が高いからです。

初期化

カーネルプロバイダーは、ntoskrnl によってエクスポートされた関数nt!EtwRegisterを使用して登録されます。デコンパイルされたバージョンの関数は、以下のとおりです。

nt!EtwRegistrationデコンパイルのコードのスクリーンショット
図3 – nt!EtwRegistrationのデコンパイル

完全な初期化は内部のEtwpRegistrationKMPProvider関数内で行われますが、ここでの主なポイントは2つあります。

  • ProviderId16バイトのGUIDへのポインターです。この GUID はオペレーティングシステム間で固定されているため、初期化中のプロバイダーの識別に使用できます。
  • RegHandleは、呼び出しが成功したときに_ETW_REG_ENTRY構造へのポインターを受け取るメモリー・アドレスです。このデータ構造とそのネストされたプロパティの一部は、Binarlyの研究によると、ETWプロバイダーを操作するAvenueを提供します。

Binarlyが図2のスライドで強調した構造を簡単にリストしてみましょう。

ETW_REG_ENTRY

_ETW_REG_ENTRY構造の64ビットの完全なリストを以下に示します。追加の詳細については、Geoff Chappell のブログをご覧ください。この構造は、 Vergiliusプロジェクトでさらに詳しくご紹介しています。

// 0x70 bytes (sizeof)
// Win11 22H2 10.0.22621.382
struct _ETW_REG_ENTRY
{
    struct _LIST_ENTRY RegList;                           //0x0
    struct _LIST_ENTRY GroupRegList;                      //0x10
    struct _ETW_GUID_ENTRY* GuidEntry;                    //0x20
    struct _ETW_GUID_ENTRY* GroupEntry;                   //0x28
    union
    {
        struct _ETW_REPLY_QUEUE* ReplyQueue;              //0x30
        struct _ETW_QUEUE_ENTRY* ReplySlot[4];            //0x30
        struct
        {
            VOID* Caller;                                 //0x30
            ULONG SessionId;                              //0x38
        };
    };
    union
    {
        struct _EPROCESS* Process;                        //0x50
        VOID* CallbackContext;                            //0x50
    };
   VOID* Callback;                                       //0x58
    USHORT Index;                                         //0x60
    union
    {
        USHORT Flags;                                     //0x62
        struct
        {
            USHORT DbgKernelRegistration:1;               //0x62
            USHORT DbgUserRegistration:1;                 //0x62
            USHORT DbgReplyRegistration:1;                //0x62
            USHORT DbgClassicRegistration:1;              //0x62
            USHORT DbgSessionSpaceRegistration:1;         //0x62
            USHORT DbgModernRegistration:1;               //0x62
            USHORT DbgClosed:1;                           //0x62
            USHORT DbgInserted:1;                         //0x62
            USHORT DbgWow64:1;                            //0x62
            USHORT DbgUseDescriptorType:1;                //0x62
            USHORT DbgDropProviderTraits:1;               //0x62
        };
    };
    UCHAR EnableMask;                                     //0x64
    UCHAR GroupEnableMask;                                //0x65
    UCHAR HostEnableMask;                                 //0x66
    UCHAR HostGroupEnableMask;                            //0x67
    struct _ETW_PROVIDER_TRAITS* Traits;                  //0x68
};

ETW_GUID_ENTRY

_ETW_REG_ENTRY内のネストされたエントリの1つは、_ETW_GUID_ENTRY構造であるGuidEntryです。この文書化されていない構造の詳細については、Geoff Chappell のブログ(こちら)およびVergilius Projectを参照してください。

// 0x1a8 bytes (sizeof)
// Win11 22H2 10.0.22
struct _ETW_GUID_ENTRY
{
    struct _LIST_ENTRY GuidList;                          //0x0
    struct _LIST_ENTRY SiloGuidList;                      //0x10
    volatile LONGLONG RefCount;                           //0x20
    struct _GUID Guid;                                    //0x28
    struct _LIST_ENTRY RegListHead;                       //0x38
    VOID* SecurityDescriptor;                             //0x48
    union
    {
        struct _ETW_LAST_ENABLE_INFO LastEnable;          //0x50
        ULONGLONG MatchId;                                //0x50
    };
    struct _TRACE_ENABLE_INFO ProviderEnableInfo;         //0x60
    struct _TRACE_ENABLE_INFO EnableInfo[8];              //0x80
    struct _ETW_FILTER_HEADER* FilterData;                //0x180
    struct _ETW_SILODRIVERSTATE* SiloState;               //0x188
    struct _ETW_GUID_ENTRY* HostEntry;                    //0x190
    struct _EX_PUSH_LOCK Lock;                            //0x198
    struct _ETHREAD* LockOwner;                           //0x1a0
};

TRACE_ENABLE_INFO

最後に、_ETW_GUID_ENTRY内のネストされたエントリの1つは、_TRACE_ENABLE_INFO構造であるProviderEnableInfoです。このデータ構造の要素についての詳細は、Microsoftの公式 ドキュメンテーションおよびVergilius プロジェクトを参照してください。この構造の設定は、プロバイダーのオペレーションと機能に直接影響します。

// 0x20 bytes (sizeof)
// Win11 22H2 10.0.22621.382
struct _TRACE_ENABLE_INFO
{
    ULONG IsEnabled;                                       //0x0
    UCHAR Level;                                           //0x4
    UCHAR Reserved1;                                       //0x5
    USHORT LoggerId;                                       //0x6
    ULONG EnableProperty;                                  //0x8
    ULONG Reserved2;                                       //0xc
    ULONGLONG MatchAnyKeyword;                             //0x10
    ULONGLONG MatchAllKeyword;                             //0x18
};

登録ハンドルの使用状況を理解する

ある程度の理論的背景は良いですが、トピックをより深く理解するには、具体的な使用例を確認するのが常に最善です。例を簡単に考えてみましょう。ほとんどのクリティカルなカーネルETWプロバイダーは、エクスポートされない「nt!EtwpInitialize」内で初期化されます。この機能の中を見ると、約15社のプロバイダーが明らかになります。

nt!EtwpInitializeの部分的なデコンパイルのコードのスクリーンショット
図4 – nt!EtwpInitialize部分的なデコンパイル

Microsoft Windows-脅威インテリジェンス(EtwTi)エントリーを例に挙げると、グローバルなThreatIntProviderGuidパラメーターをチェックして、このプロバイダーのGUIDを回復できます。

EtwTiプロバイダーGUIDのコードのスクリーンショット
図5 – EtwTiプロバイダーのGUID

このGUIDをオンラインで検索すると、正しい値(f4e1897c-bb5d-5668-f1d8-040f4d8dd344)が復元されたことがすぐにわかります。

登録処理パラメーターであるEtwThreatIntProvRegHandlingが使用されているインスタンスを見て、その使用方法を分析してみましょう。ハンドルが参照される場所の1つは、nt!EtwTiLogDriverObjectUnLoadです。この関数の名前から、ドライバー・オブジェクトがカーネルによってアンロードされたときにイベントを生成することを意味していることが直感できます。

nt!EtwTiLogDriverUnload デコンパイルのスクリーンショット
図 6 – nt!EtwTiLogDriverUnloadデコンパイル

nt!EtwEventEnabled 関数と nt!EtwProviderEnabled 関数は両方とも、ここでは引数の 1 つとして登録処理を渡します。何が起こっているかをより深く理解するために、これらのサブ機能の1つを見てみましょう。

EtwProviderEnableデコンパイルのコードのスクリーンショット
図7 – nt!EtwProviderEnableデコンパイル

確かに、これについて従うのは少し困難です。ただし、ポインターの算術は特に重要ではありません。代わりに、この機能が登録処理をどのように処理するかに焦点を当ててみましょう。この機能は、_ETW_REG_ENTRY構造とそのサブ構造(GuidEntryプロパティなど)の多くのプロパティを検証するようになっています。

struct _ETW_REG_ENTRY
{
    …
    struct _ETW_GUID_ENTRY* GuidEntry;                    //0x20
    …
}

そしてGuidEntry->ProviderEnableInfoプロパティ。

struct _ETW_GUID_ENTRY
{
    …
    struct _TRACE_ENABLE_INFO ProviderEnableInfo;         //0x60
    …
}

その後、関数は同様のレベルベースのチェックを行います。最後に、この関数はtrueまたはfalseを返し、プロバイダーが指定されたレベルとキーワードでのロギングが有効になっているかどうかを示します。詳細については、Microsoft の公式ドキュメンテーションを参照してください。

登録処理を通じてプロバイダーがアクセスされる場合、それらの構造の整合性がプロバイダーの運用にとって非常に重要になることがわかります。逆に、攻撃者がこれらの構造を操作できた場合、呼び出し元の制御フローに影響を与えて、記録されるイベントをドロップしたり排除したりすることができます。

登録ハンドルへの攻撃

Binarly社の攻撃対象領域を振り返り、軽量分析に基づいて、イベント収集を阻止するためのいくつかのストラテジーを立案することができます。

  • 攻撃者は_ETW_REG_ENTRYポインターをNULLにすることができます。登録処理を参照する関数はすべて、プロバイダーが初期化されていないことを前提とします。
  • 攻撃者は、_ETW_REG_ENTRY -> GuidEntry -> ProviderEnableInfo ポインターをNULL 化できます。ProviderEnableInfoは、プロバイダーの動作方法の概要を説明する_TRACE_ENABLE_INFO構造へのポインターであるため、プロバイダーの収集機能を効果的に無効にする必要があります。
  • 攻撃者は_ETW_REG_ENTRY->GuidEntry->ProviderEnableInfo データ 構造のプロパティを上書きし、プロバイダーの設定を改ざんすることができます。
    • IsEnabled:プロバイダーからのイベントの受信を有効にするか、プロバイダーからイベントを受信する際に使用される設定を調整するために、1に設定します。プロバイダーからのイベントの受信を無効にするには、0に設定します。
    • Level:プロバイダーに書き込むイベントの最大レベルを示す値。プロバイダーは通常、イベントのレベルがこの値未満または同等の場合、MatchAnyQuery、およびMatchAllQueryの基準を満たすことに加えて、イベントを書き込みます。
    • MatchAnyKeyword:プロバイダーに書き込ませるイベントのカテゴリーを決定するキーワードの64ビットビットマスク。プロバイダーは通常、イベントのキーワード・ビットがこの値に設定されているビットのいずれかと一致する場合、またはイベントにキーワード・ビットが設定されていない場合に、LevelとMatchAllKeywordの基準を満たすだけでなく、イベントを書き込みます。
    • MatchAllキーワード: プロバイダーに書き込むイベントを制限するキーワードの64ビットのビットマスク。プロバイダーは通常、イベントのキーワード・ビットがこの値に設定されているすべてのビットと一致する場合、またはイベントにキーワード・ビット・セットが設定されていない場合に、LevelとMatchAnykey 基準を満たすだけでなく、イベントを書き込みます。

カーネルサーチの技術

ETW に対する DKOM 攻撃がどのようなものかについては、今ではよく分かっています。ここで、Lazarusマルウェアが脆弱な要因をロードすることで実現しているように、アタッカーがKernel Read/Writeプリミティブを与える脆弱性を持っていると仮定します。欠けているのは、これらの登録処理を見つける方法です。

これらのハンドルを見つけるための2つの主要な手法の概要を説明し、LazarusがKernelペイロードで使用する1つのバリエーションを示します。

中整合性レベル(MedIL)KASLRバイパス

まず、Kernel ASLRはありますが、ローカル攻撃者がMedIL以上でコードを実行できる場合、この境界はセキュリティー境界ではない、と説明することが賢明です。サンドボックスまたはLowILのシナリオでのみ制限されているカーネル・ポインターを漏洩する方法は数多くあります。背景としては、Alex Ionescuの『I Got 99 Problems But a Kernel Pointer Ain't One』を見てみてください。これらの多くの手法は今でも有効です。

ここで選択したツールは、SystemModuleInformation クラスの ntdll!NtQuerySystemInformation です。

internal static UInt32 SystemModuleInformation = 0xB;

[DllImport(“ntdll.dll”)]
internal static extern UInt32 NtQuerySystemInformation(
    UInt32 SystemInformationClass,
    IntPtr SystemInformation,
    UInt32 SystemInformationLength,
    ref UInt32 ReturnLength);

この関数は、カーネル空間にロードされているすべてのモジュールのライブベース所在地を返します。その時点で、これらのモジュールをディスク上で解析し、生ファイルのオフセットを相対仮想所在地に変換したり、その逆の変換を行ったりすることができます。

public static UInt64 RvaToFileOffset(UInt64 rva, List<SearchTypeData.IMAGE_SECTION_HEADER> sections)
{
    foreach (SearchTypeData.IMAGE_SECTION_HEADER section in sections)
    {
        if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize)
        {
            return (rva – section.VirtualAddress + section.PtrToRawData);
        }
    }
    return 0;
}

public static UInt64 FileOffsetToRVA(UInt64 fileOffset, List<SearchTypeData.IMAGE_SECTION_HEADER> sections)
{
    foreach (SearchTypeData.IMAGE_SECTION_HEADER section in sections)
    {
        if (fileOffset >= section.PtrToRawData && fileOffset < (section.PtrToRawData + section.SizeOfRawData))
        {
            return (fileOffset – section.PtrToRawData) + section.VirtualAddress;
        }
   }
    return0;
}

攻撃者は、標準のロード・ライブラリーAPI呼び出し(例:ntdll!LdrLoadDll)を使用して、これらのモジュールをユーザーランド・プロセスにロードすることもできます。そうすることで、ファイル・オフセットをRVAに変換する複雑な作業を回避できます。ただし、運用セキュリティー(OpSec)の観点から見ると、これはより多くの検知テレメトリーを生成する可能性があるため、理想的ではありません。

方法 1: ガジェットチェーン

可能な限り、これは私が好む手法です。なぜなら、パッチ変更の影響が少なくなるため、モジュールのバージョン間でリークの移植性が向上するからです。欠点は、漏えいしたいオブジェクトの既存のガジェットチェーンに依存していることです。

ETW登録処理については、Microsoft-Windows-脅威インテリジェンスを例に挙げます。以下に、nt!EtwRegistrationの呼び出し全文を示します。

nt!Etwregisterの完全なCALLディスアセンブリーのコードのスクリーンショット
図8 – nt!EtwRegistration の完全なCALLディスアセンブリー

ここでは、登録ハンドルであるEtwThreatIntProvRegHandlingへのポインタをリークしたいと考えます。図8の最初の行でparam_4 にロードされたものを示します。このポインターは、カーネル・モジュールの.dataセクション内のグローバルに解決されます。この呼び出しはエクスポートされていない関数で発生するため、その所在地を直接漏洩することはできません。代わりに、このグローバルがどこで参照されているかを調べ、所在地が漏洩できる機能で使用されているかどうかを確認する必要があります。

nt!EtwThreatIntProvReg Handles参照のコードのスクリーンショット
図9 – nt!EtwThreatIntProvRegHandling

こちらのエントリーの一部を調べると、nt!KeInsertQueueApcの候補者がすぐに見つかります。

nt!KeInsertQueueApcの部分的なデコンパイルのスクリーンショット
図10 – nt!KeInsertQueueApcの部分的なデコンパイル

この候補者が有力な候補である理由はいくつかあります。

  • nt!KeInsertQueueApcはエクスポートされた機能です。これは、KASLRバイパスを使用してライブアドレスを漏洩できることを意味します。そして、カーネルの脆弱性を利用して、その住所にあるデータを読み取ることができます。
  • グローバルは関数の開始時に使用されます。これは、それを見つけるために複雑な命令解析ロジックを構築する必要がほぼないことを意味するため、非常に便利です。

アセンブリを見ると、次のレイアウトが表示されます。

nt!KeInsertQueueAcの部分的なディスアセンブリーのコードのスクリーンショット
図11 – nt!KeInsertQueueApcの部分的な分解

そうすれば、この登録処理を漏洩させることは簡単になります。脆弱性を利用してバイトの配列を読み出し、最初のmov R10命令を検索して、グローバル変数の相対仮想オフセットを計算します。計算は次のようになります。

Int32 pOffset = Marshal.ReadInt32((IntPtr)(pBuff.ToInt64() + i + 3));
hEtwTi = (IntPtr)(pOffset + i + 7 + oKeInsertQueueApc.pAddress.ToInt64());

登録ハンドルを使用すると、_ETW_REG_ENTRYデータ構造にアクセスできるようになります。

一般に、このようなガジェット・チェーンは、さまざまなカーネル・データ構造を漏洩するために使用できます。ただし、そのようなガジェット・チェーンを見つけることが常に可能とは限らず、場合によってはガジェット・チェーンに複数の複雑な段階があることがあることを指摘しておく価値があります。たとえば、ページ・ディレクトリー・エントリ (PDE) 定数を漏洩する可能性のあるガジェット・チェーンは、次のようになります。

MmUnloadSystemImage -> MiUnloadSystemImage -> MiGetPdeAddress

実際、ETW登録処理を大まかに分析した結果、ほとんどの登録手続きには上記のように使用できる適切なガジェット・チェーンがないことが明らかになりました。

方法 2: メモリスキャン

これらのETW登録ハンドルを漏洩させるもう1つの主なオプションは、ライブ・カーネル・メモリーまたはディスク上のモジュールから、メモリー・スキャンを使用することです。ディスク上のモジュールをスキャンする場合、ファイル・オフセットをRVAに変換できることに注意してください。

このアプローチは、固有のバイト・パターンを特定し、それらのパターンをスキャンし、最後にパターンの一致のオフセットでいくつかの操作を実行することで構成されています。これをよりよく理解するために、nt!EtwpInitialize をもう一度見てみましょう。

nt!EtwpInitializeの部分的なデコンパイルのコードのスクリーンショット
図12 – nt!EtwpInitializeの部分的なデコンパイル

nt!EtwRegistrationへの呼び出しの大部分(15個)はすべて、この関数にまとめられています。ここでの主なストラテジーは、nt!EtwRegistration への最初の呼び出しの前に表示される固有のパターンと、nt!EtwRegistration への最後の呼び出し後に表示される 2 番目のパターンを見つけることです。これはそれほど複雑ではありません。移植性を向上させるために使用できる1つのトリックは、ワイルドカード・バイト文字列を処理できるパターン・スキャナーを作成することです。これは読者に任せるタスクです。

開始インデックスと停止インデックスが特定されると、その間にあるすべての命令を確認できます。

  • 潜在的なCALL命令は、CALLのオペコードが0xe8であることから識別できる。
  • その後、DWORDサイズの読み取りが、潜在的なCALL命令の相対オフセットを計算するために使用されます。
  • このオフセットは、CALLの相対所在地に追加され、5(アセンブリ命令のサイズ)だけ増分されます。
  • 最後に、この新しい値をnt!EtwRegisterと比較して、有効なCALLロケーションをすべて検索できます。

すべてのCALL命令が見つかると、関数の引数(1番目はETWプロバイダーを識別するGUID)、2番目は登録処理の所在地を抽出することができます。入手した情報で、登録ハンドルに対して情報に基づいたDKOM攻撃を実行し、特定されたプロバイダーのオペレーションに影響を与えることができます。

Lazarus ETWのパッチ適用

ESETホワイトペーパーで言及されているFudModleのDLLサンプルを入手し、分析しました。このDLLは、署名された脆弱なDell要因(インラインXORエンコードされた参考情報から)を読み込み、その後、ホスト上のテレメトリーを制限するために、要因に多くのカーネル構造にパッチを適用するように試験運用します。

Lazarus FudModuleハッシュのコードのスクリーンショット
図13 – Lazarus FudModuleハッシュ

この投稿の最後に、LazarusがカーネルETW登録処理を見つけるために使用する戦略について確認したいと思います。これは、上記で説明したスキャン方法のバリエーションです。

検索機能の開始時に、Lazarusは nt!EtwRegistration を解決し、この所在地を使用してスキャンを開始します。

Lazarus FudModule部分ETW検索デコンパイルのコードのスクリーンショット
図14 – Lazarus FudModuleの部分ETW検索デコンパイル

この決定は、関数が呼び出される場所に関してその関数が存在する場所に依存しているため、少し特異なものです。新しいコードが導入、削除、または変更される可能性があるため、モジュール内の関数の相対位置はバージョンごとに異なる場合があります。ただし、モジュールのコンパイル方法のため、関数は比較的安定した順序を維持することが期待されます。これは検索速度の最適化だと推測されます。

ntoskrnlnt!EtwRegisterへの参照を探している場合、この手法を使って見逃されるエントリーは多くないと思われます。Lazarus は追加の分析を行い、欠落したエントリは重要ではないか、パッチを適用する必要がないと判断した可能性もあります。欠落したエントリーは以下で強調表示されます。この戦略を採用すると、Lazarusはスキャンを実行しながら0x7b1de0バイトをスキップできます。スキャナーが遅い場合は無視できる量になる可能性があります。

nt!EtwRegisterへの呼び出しインスタンスのコードのスクリーンショット
図15 – nt!EtwRegisterへの呼び出しインスタンス

さらに、スキャンを開始するとき、登録処理の記録を開始する前に、最初の5つのマッチングがスキップされます。以下に、検索機能の一部を示します。

Lazarus FudModuleの部分ETW検索デコンパイルのコードのスクリーンショット
図16 – Lazarus FudModuleの部分ETW検索デコンパイル

コードは少し難解ですが、プロットのハイライトはわかります。コードは、nt!EtwRegistrationへの呼び出しを探し、登録ハンドルを抽出し、KASLRバイパスを使用してこのハンドルを所在地に変換し、マルウェア構成構造(初期化時に割り当て)内のこの目的のために確保された配列にポインターを保管します。

最後に、Lazarus がこれらのプロバイダーを無効化するために何をしているのかを見てみましょう。

Lazarus FudModule NULL ETW登録処理のコードのスクリーンショット
図17 – Lazarus FudModule NULL ETW登録処理

これはほとんど理にかなっています。Lazarusがここで行うのは、先ほど見たグローバル変数をリークさせ、その所在地のポインターをNULLで上書きすることです。これにより、_ETW_REG_ENTRYデータ構造が存在する場合、それへの参照が効果的に消去されます。

私は、いくつかの理由により、示されたトレードクラフトに完全に満足していません。

  • ペイロードはプロバイダーのGUIDをキャプチャしないため、プロバイダー登録処理を上書きする必要があるかどうかについてインテリジェントな決定を下すことができません。
  • スキャンのオフセットはntoskrnlのバージョンによって異なる可能性があるため、ntoskrnl内のオフセットでスキャンを開始するという決定には疑問が生じます。
  • 最初の5試合を任意にスキップすることも同様に疑わしいと思われます。この決定には戦略的な理由があるかもしれませんが、より良いアプローチは、まずすべてのプロバイダーを収集し、その後、いくつかのプログラム・ロジックを使用して成果をフィルタリングすることです。
  • _ETW_REG_ENTRY へのポインターを上書きすると機能するはずですが、この手法は明白です。_ETW_REG_ENTRY_ETW_GUID_ENTRY、または_TRACE_ENABLE_INFOのプロパティを上書きすることをお勧めします。

この手法を科学のために再実装しましたが、トレードクラフトにいくつかの調整を加えました

  • ntoskrnl内のすべての0xe8バイトを検索するために、速度が最適化された検索アルゴリズムが使用されます。
  • その後、どれが有効なCALL命令とその宛先を判断するために、いくつかの後処理が行われます。
  • 関数は登録ハンドルの動的引数を使用して呼び出される場合があるため、 nt!EtwRegisterへのすべての呼び出しが有用であるとは限りません。このため、残りの呼び出しをフィルタリングするには、いくつかの追加のロジックが必要です。
  • 最後に、すべてのGUIDが人間が読み取れる形式に解決され、登録ハンドルが列挙されます。

全体として、調整後、上記の手法は、明らかにこの種の列挙を実行する最良の方法です。最適化されたアルゴリズムでは検索時間がごくわずかであるため、ディスク上のモジュール全体をスキャンし、追加のスキャン後ロジックを使用して成果をフィルタリングすることは理にかなっています。

ETW DKOMインパクト

このような攻撃がどの程度の影響を与えるかを簡単に評価することが賢明です。プロバイダーのデータが削減または完全に排除されると情報の損失が発生しますが、同時にすべてのプロバイダーがセキュリティ上重要なイベントを通知するわけではありません。

ただし、これらのプロバイダーの一部のサブセットは、セキュリティーに敏感です。その最もわかりやすい例が、現在はDefender for エンドポイントと呼ばれているMicrosoft Defender Advanced Threat Protection(MDATP)のコア・データ・ソースであるMicrosoft Windows-脅威インテリジェンス(EtwTi)です。このプロバイダへのアクセスは厳しく制限されており、このプロバイダに登録できるのはEarly Launch Anti マルウェアELAMドライバーのみです。同様に、これらのイベントを受信するユーザーランド・プロセスには、保護ステータス(ProtectedLight / Antimalware)が必要であり、ELAMドライバーと同じ証明書で署名されている必要があります。

EtwExplorerを使えば、このプロバイダーがどのような種類の情報をシグナルできるかをより正確に把握できます。

ETW Explorerファイル・ページのスクリーンショット
図18 – ETW Explorer

XML マニフェストは大きすぎてそのすべてをここに含めることはできませんが、DKOM を使用して削減できるデータの種類のアイデアを示す 1 つのイベントを以下に示します。

EtwTi 部分的な XML マニフェストのコードのスクリーンショット
図19 – EtwTiの部分的なXMLマニフェスト

結論

カーネルは、これまでも、そして次に進む重要であり、Microsoftやサード・パーティー・プロバイダーがオペレーティング・システムの整合性を保護するために取り組む必要がある、重要な領域であり続けます。カーネルにおけるデータ破損は、エクスプロイテーションの主要な機能であるだけでなく、カーネルエクスプロイト開発における主要なコンポーネントでもあります。Microsoftはすでにこの分野で大きな進展を遂げており、仮想化ベースのセキュリティ (VBS)とそのコンポーネントの一つであるカーネルデータ 保護 (KDP)を導入しています。

Windowsオペレーティング・システムの消費者も、これらの進歩を確実に利用して、攻撃者にできるだけ多くのコストを課す必要があります。Windows Defender アプリケーション コントロール (WDAC) を使用して、VBS の安全対策が講じられていること、および潜在的に危険な要因の読み込みを禁止するポリシーが存在していることを確認できます。

コモディティTAがBYOVD攻撃を活用してカーネル空間でDKOMを実行することが増えているため、これらの取り組みはすべてますます重要です。

 

オフィスでミーティングをするビジネスチーム

IBMお客様事例

お客様のビジネス課題(顧客満足度の向上、営業力強化、コスト削減、業務改善、セキュリティー強化、システム運用管理の改善、グローバル展開、社会貢献など)を解決した多岐にわたる事例のご紹介です。

参考文献

  • Veni、No Vidi、No Vici: ETWブラインドEDR(エンドポイントの検知と対応)センサーへの攻撃(BHEU 2021スライド) – こちら
  • Veni、No Vidi、No Vici: ETWブラインドEDR(エンドポイントの検知と対応)センサーへの攻撃(BHEU 2021ビデオ)– こちら
  • Windows セキュリティの進化 (BlueHat Shanghai 2019) –こちら
  • 「単純な」脆弱性を悪用する – 35ステップ以内で簡単!— こちら
  • 「単純な」脆弱性を悪用する – パート1.5 – 情報漏洩 – こちら
  • 脅威インテリジェンスETW入門 -こちらご覧ください
  • TelemetrySourcerer – こちらご覧ください
  • WDAC ポリシーウィザード — こちらご覧ください

X-Force Redについて詳しくはこちらをご覧くださいこちらからX-Force との無料相談をご予約ください。

関連ソリューション
エンタープライズ・セキュリティー・ソリューション

世界有数の企業向けセキュリティー・プロバイダーが提供するソリューションで、セキュリティー・プログラムを変革します。

サイバーセキュリティー・ソリューションの詳細
サイバーセキュリティー・コンサルティング・サービス

サイバーセキュリティー・コンサルティングやクラウド、マネージド・セキュリティー・サービスでビジネスを変革し、リスクを管理しましょう。

    サイバーセキュリティー・サービスはこちら
    サイバーセキュリティーのための人工知能(AI)| IBM

    AIを活用したサイバーセキュリティー・ソリューションで、セキュリティー・チームの俊敏性、精度、生産性を向上させます。

    AIを活用したサイバーセキュリティーの詳細はこちら
    次のステップ

    データ・セキュリティー、エンドポイント管理、IDおよびアクセス管理(IAM)ソリューションのいずれが必要であっても、IBMのエキスパートはお客様と協力して、高度なセキュリティー体制を実現します。サイバーセキュリティー・コンサルティング、クラウド・セキュリティー・サービス、マネージド・セキュリティー・サービスなど、業界の世界的リーダーとして、事業の変革とリスク管理を支援します。

    サイバーセキュリティー・ソリューションの詳細 サイバーセキュリティー・サービスを発見する