あまりにもログ;未読 — CLFS ログ ファイルをステルスに使用する未知のアクター

Sample string deobfuscation for "PrintNotify" news

Mandiant Advanced Practices チームは最近、私たちが PRIVATELOG と名付けた新しいマルウェア ファミリとそのインストーラである STASHLOG を発見しました。この投稿では、サンプルがデータを隠すために使用する斬新で特に興味深い手法と、FLARE アナリストのサポートで実行された両方のファイルの詳細な分析を共有します。また、サンプルの検出ルールとハンティングの推奨事項を共有して、環境内で同様のアクティビティを見つけます。

Mandiant はまだ、どの顧客環境でも PRIVATELOG または STASHLOG を監視しておらず、PRIVATELOG によって起動された第 2 段階のペイロードを回復していません。これは、開発中のマルウェア、研究者の作業、または標的型の活動を示している可能性があります。

CLFS とトランザクション ファイル

PRIVATELOG と STASHLOG は、Common Log File System (CLFS) に依存して、レジストリ トランザクション ファイルの第 2 段階のペイロードを隠します。

CLFS は、高性能のために Microsoft が Windows Vista および Windows Server 2003 R2 に導入したログ フレームワークです。ログ データを作成、保存、および読み取るための API 関数 (clfsw32.dll で使用可能) をアプリケーションに提供します。

ファイル形式は広く使用されておらず、文書化されていないため、CLFS ログ ファイルを解析できるツールはありません。これにより、攻撃者は、API 関数を介してアクセスできるため、便利な方法でデータをログ レコードとして隠すことができます。これは性質上、Windows レジストリや NTFS 拡張属性に依存してデータを隠したり、Windows API を使用してバイナリ データを保存および取得する場所を提供したりするマルウェアに似ています。

Microsoft Windows では、CLFS は特に Kernel Transaction Manager (KTM) によって Transactional NTFS (TxF) と Transactional Registry (TxR) 操作の両方に使用されます。これらにより、アプリケーションはファイルシステムまたはレジストリに対して多数の変更を実行できます。これらの変更はすべて、コミットまたはロールバックできる単一のトランザクションにグループ化されます。たとえば、トランザクションでレジストリ キーを開くには、関数RegCreateKeyTransacted()RegOpenKeyTransacted() 、およびRegDeleteKeyTransacted()を使用できます。

レジストリ トランザクションは、<hive><GUID>.TMContainer<number>.regtrans-ms または <hive><GUID>.TxR.<number>.regtrans-ms という命名スキームの専用ファイルに保存されます。これらは、メタデータのみを含むマスター .blf ファイルで参照される CLFS コンテナーであり、ユーザー プロファイル ディレクトリを含むさまざまな場所にあります。

レジストリ トランザクション フォレンジックについては、 以前のブログ記事で簡単に説明しました。 CLFS マスターおよびコンテナー ファイル形式は、ほとんど文書化されていません。ただし、 以前の研究は GitHub で入手できます

マルウェアの難読化

多くのマルウェア ファミリと同様に、PRIVATELOG と STASHLOG で使用される文字列のほとんどは難読化されています。しかし、ここで観察された手法は一般的ではなく、各バイトをインラインでハードコードされたバイトと XOR することに依存しており、ループはありません。したがって、事実上、各文字列は一意のバイト ストリームで暗号化されます。

Sample string deobfuscation for "PrintNotify"
図 1: 「PrintNotify」の文字列の難読化解除の例

興味深いことに、インストーラーからの難読化解除された文字列の一部は、エラー メッセージのログ記録に使用され、次のようなスペル ミスやタイプミスがあります。

  • ログ インデックス=%d、データ境界が境界を超えています。n
  • 内部データ ハッシュの不一致。n
  • ログ バッファ サイズ=%u 小さすぎます。少なくとも %u バイトが必要です。n

STASHLOGの紹介

難読化された文字列が含まれていることに加えて、インストーラーのコードは、静的分析を煩雑にするさまざまな制御フローの難読化手法を使用して保護されています。図 2 は、インストーラーのmain()関数のグラフの概要であり、制御フローの難読化の効果を示しています。

main() のグラフ表示
図 2: main() のグラフ表示

STASHLOG には、次の 2 つの異なる操作モードがあります。

  • 引数なしで、その間に環境を準備します
  • CLFS ファイルで非表示にする必要があるファイルである単一の引数を使用する
環境の準備

引数なしで実行すると、インストーラーはコンソールに 2 つの値を出力します。

  • HKLMSOFTWAREMicrosoftCryptographyMachineGUID のレジストリ値から返された GUID
  • CoCreateGUID()でランダムに生成された GUID から派生した 56 バイトの値
コンソール出力の例
図 3: コンソール出力の例

56 バイトの値は、ランダム GUID、その SHA1 ハッシュ、および前の値の SHA1 ハッシュを連結したものです。つまり: GUID+sha1(GUID)+sha1(GUID+sha1(GUID))。

ランダムに生成された GUID は、GlobalAtom テーブルに win:: というプレフィックスが付いた文字列として格納されます。このテーブルはメモリ内にあり、すべてのアプリケーションで使用できる識別子を持つ文字列が含まれています。

インストーラーの実行時に win:: で始まる文字列が既に存在する場合、GlobalAtom テーブル内の既存の GUID が再利用されます。

事実上、引数なしで実行すると、インストーラーは、攻撃者がペイロードをディスクに書き込む前に事前暗号化するために使用する暗号化キーを生成して出力します。

ペイロードのスタッシング

引数を指定して起動すると、インストーラーが開き、引数として渡されたファイルの内容が復号化されます。ファイルに SHA1 ハッシュのサフィックスが付いていることを確認し、メモリに格納されている GlobalAtom GUID 文字列を使用して同じ 56 バイトの値を生成します。

56 バイトの値は再び SHA1 ハッシュされ、最初の 16 バイトは初期化ベクトル (IV) を形成します。キーは、ホストのレジストリからの 16 バイトの MachineGUID 値です。暗号化アルゴリズムは HC-128 で、マルウェアで使用されることはめったにありません。

予想される復号化されたファイルの内容には、40 バイトのヘッダーがあります。

構造体ペイロードヘッダー {
DWORD マジック;
DWORD minWinVer;
DWORD maxWinVer;
DWORD 合計サイズ;
WORD numBlocks;
WORD 不明;
BYTE sha1sum[20];
}

分析されたインストーラーでは、「魔法の」値はチェックサムと呼ばれます。ただし、STASHLOG は、この値がハードコードされた値 0x00686365 と一致することを確認します。オフセット 16 で指定されるブロック数は、2 ~ 5 の間でなければなりません。マルウェアは、オペレーティング システムのバージョンが下限と上限の境界内にあること、および復号化されたデータの SHA1 ハッシュがオフセット 20 のペイロード ヘッダー値と一致することも確認します。 .

マルウェアは、ペイロード ヘッダーに続いて、8 バイトのヘッダーを持つ暗号化されたデータのブロックを期待します。各ブロック ヘッダーには、次の構造があります。

構造体 blockHeader {
DWORD マジック;
DWORD ブロックサイズ;
}

マルウェアは、ペイロードの構造をチェックして検証すると、デフォルトのユーザーのプロファイル ディレクトリで .blf ファイルを検索し、作成日のタイムスタンプが最も古い .blf ファイルを使用します。

実際には、マルウェアは通常、レジストリ トランザクション ログに使用されるファイル C:UsersDefaultNTUSER.DAT<GUID>.TM.blf を見つける必要があります。

一致する .blf ファイルが実際に見つかった場合は、 clfsw32.dll の CreateLogFile() API で開かれます。この関数は CLFS ログを開き、拡張子 .blf を除いた次の形式のファイル名を期待します: log:< LogName >[::< LogStreamName >]

ログ ファイルはCloseAndResetLogFile()関数を使用してリセットされ、データを挿入するために再度開かれます。

CLFS ログ ファイルにデータを挿入する前に、マルウェアは HC-128 を使用して各ブロックを復号化します。キーは 16 バイトのアトム GUID で、IV はアトム GUID SHA1 ハッシュの最初の 16 バイトです。各ブロックは、次のように新しいキー マテリアルで再暗号化されます。

  • 暗号化キーは、 GetVolumeNameForVolumeMountPointW() からの 16 バイトの GUID です。
  • IV は、連結された GUID の SHA1 ハッシュの最初の 16 バイトです。
    • GetVolumeNameForVolumeMountPointW()
    • レジストリ値 HKLMSOFTWAREMicrosoftCryptographyMachineGUID

内容は clfsw32.dll API 関数ReserveAndAppendLog()を使用して CLFS ログ ファイルに書き込まれます。ペイロード ヘッダーは、最初のエントリとしてログ ファイルに書き込まれ、その後にブロックごとに個別のエントリが続きます。

データは、レジストリ トランザクション ログの最初のコンテナ ファイル C:UsersDefaultNTUSER.DAT<GUID>.TMContainer0000000000000000001.regtrans-ms に効果的に格納されます。

PRIVATELOGへ

Mandiant が回収した PRIVATELOG サンプルは、難読化されていない 64 ビット DLL で、prntvpt.dll という名前です。このファイルには、正規の prntvpt.dll ファイルを模倣したエクスポートが含まれていますが、エクスポートには機能がありません。 PRIVATELOG は、PrintNotify という名前のサービスのメイン DLL である PrintConfig.dll から、DLL 検索順序ハイジャックを介して読み込まれることを想定しています。

悪意のあるコードは、DLL のエントリ ポイントで実行されます。実行中のプロセスのコマンドライン引数を確認することから始め、svchost.exe -k print の下で実行されていると予想されます。これが一致する場合、マルウェアは、このコマンド ラインを使用して、サービスのサービス エントリ ポイントである PrintConfig.dll の ServiceMain エクスポート関数の関数アドレスを解決します。この関数は、Microsoft Detours ( Win32 関数の計測に使用される公開ライブラリ) を使用してパッチが適用されるため、実行フローは正規のサービス DLL で発生するように見えます。

パッチが適用された ServiceMain 関数は、PRIVATELOG がその機能のほとんどを実行する場所です。

STASHLOG と同様に、PRIVATELOG は、デフォルトのユーザーのプロファイル ディレクトリにある *.blf ファイルを列挙することから開始し、作成日のタイムスタンプが最も古い .blf ファイルを使用します。

一致する .blf ファイルが見つかった場合、PRIVATELOG はそれを clfsw32.dll で開きます。 関数CreateLogFile() 。その後、ログ ファイルは、 CreateLogMarshallingArea()ReadLogFile()ReadNextLogFile()など、CLFS に固有の他の関数を使用してマーシャリングおよび解析されます。マルウェアは、インストーラーの分析に一致する特定のエントリを見つけることを期待しています。

PRIVATELOG は、最初のログ エントリが次の形式であると想定します。

  • 40 より大きいサイズ (ペイロード ヘッダー サイズ)
  • オフセット 16 (ブロック数) で 2、3、4、または 5 の WORD 値

最初のエントリが前述の基準に一致する場合、次の 8 バイトのヘッダーが含まれるまで後続のレコードが読み取られます。

  • 最初の DWORD は 2 でなければなりません (想定されるマジック値)
  • 2 番目の DWORD は、エントリのサイズからヘッダーを引いた値よりも小さくする必要があります。この値は、復号化されるペイロードのサイズと同じです。

予想されるログ エントリが見つかると、その内容は HC-128 暗号化アルゴリズムを使用して復号化されます。復号化キーと IV は、STASHLOG で使用されたのと同じ固有のホスト プロパティを使用して生成されます。

PRIVATELOG は最初に一致したブロックのみを復号化し、STASHLOG によって少なくとも 2 ~ 5 個のブロックが挿入されることが予想されることに注意してください。

PRIVATELOG はついに、めったに見られない手法を使用して DLL ペイロードを実行します。今回は NTFS トランザクションに依存しています。インジェクション プロセスは、 Phantom DLL の空洞化に似ており、次のように説明されています。

  • API CreateFileTransactedA()を使用して、コピーされたファイルへのトランザクション ハンドルを開きます
    • Mandiant が分析したサンプルでは、トランザクションに使用されたファイルは正規のバイナリ C:WindowsSystem32dbghelp.dll のコピーであり、C:Windowssystem32WindowsPowerShellv1.0dbghelp にコピーされています。 dll.
  • トランザクション ファイルを復号化されたペイロード コンテンツで上書きします。
  • API NtCreateSection()を介して、SEC_IMAGE 属性を持つトランザクション ファイルに基づくセクションを作成します。
  • 新しく作成されたセクションのビューをマップします
    • これにより、トランザクション ファイル データがある程度暗黙的に読み込まれます。 PE ヘッダーが検証され、セクションがメモリにマップされます。ただし、セクションのアクセス許可を修正したり、インポートを解決したりしません。
  • セクションの権限を修正する
  • インポート テーブルでインポートを解決する
  • ペイロードのエントリ ポイントを実行する
  • SvcMain という名前のエクスポート関数を見つけて実行します

PRIVATELOG のハンティング

容器サンプル

図 4 は、STASHLOG によって作成され、PRIVATELOG によってロードされるサンプルの予想されるログ ファイルを表す、作成されたコンテナー ファイルを示しています。

STASHLOG によって作成されるログ ファイルの例
図 4: STASHLOG によって作成されたログ ファイルの例
子供のルール

Mandiant は、PRIVATELOG と STASHLOG、およびそれらが使用するさまざまな方法論と一意の文字列に基づいて考えられる亜種を探す YARA ルールを作成しました。 PRIVATELOG 構造に一致する、または暗号化されたデータを含む CLFS コンテナを検出するルールも提供されます。これらのルールは、本番環境で実行する前に十分にテストする必要があります。

「数学」をインポート
「ペ」をインポート

ルール HUNTING_Win_PRIVATELOG_CLFS {

メタ:
著者 = “adrien.bataille@mandiant.com”
description = “このルールは、PRIVATELOG によって使用される可能性のあるデータを含む CLFS コンテナーを探します。このルールはファイル コンテンツでループする可能性があるため、できれば regtrans-ms ファイルでのみ使用するか、注意して使用してください。

調子:
ファイルサイズ < 100MB およびファイルサイズ >= 512KB
そして uint16(0) == 0x0015 // 署名
and uint8(2) != 0 // fixup 値の上位バイト
そして uint8(3) == 0 // 常に 0
and uint16(4) == uint16(6) and uint16(4) != 0 // num セクタ
uint32(8) == 0 // 常に 0
そして uint32(16) == 1 // 常に 1
そして uint32(20) == 0 // 常に 0
and uint32(40) == 0x70 // レコード ヘッダーのサイズ

// 最初のレコードのデータのサイズは少なくとも 0x28
および uint32(0x70+0x18) – 0x28 >= 0x28

// ペイロードヘッダー.numblocks (0x70+uint16(0x70+0x22) のペイロードヘッダー)
および (uint16(0x70+uint16(0x70+0x22)+0x10) == 0x2 または uint16(0x70+uint16(0x70+0x22)+0x10) == 0x3 または uint16(0x70+uint16(0x70+0x22)+0x10) = = 0x4 または uint16(0x70+uint16(0x70+0x22)+0x10) == 0x5)

// これはサイズです。ファイルサイズよりも小さいと仮定します
そして uint32(0x70+uint16(0x70+0x22)+0xC) < ファイルサイズ

// 2 つの異なる方法を使用してマルウェアを確認する
と (
// 最初のログ レコードでハードコードされたマジックを探す
uint32(0x70+uint16(0x70+0x22)) == 0x00686365 または
// 考えられる各セクターをループして、ブロックヘッダー構造体を探します
(0 .. (ファイルサイズ 512) – 1) の任意の i について:
(
// レコード ヘッダー、セクタ数、およびレコードのサイズを探します
uint16(i*512)==0x0015 および uint16(i*512+4) == uint16(i*512+6) および uint16(i*512+4) != 0 および uint32(i*512+40) = = 0x70 および uint32(i*512+0x88) > 0x28 および uint32(i*512+0x88) < ファイルサイズおよび
// ペイロードで magic と blockheader.blocksize を探します
uint32(i*512+0x70+uint16(i*512+0x70+0x22)) == 2 および uint32(i*512+0x70+uint16(i*512+0x70+0x22)+4) == uint32(i* 512+0x88)-0x30
)
)
}

ルール HUNTING_Win_CLFS_Entropy {

メタ:
著者 = “adrien.bataille@mandiant.com”
description = “このルールは、高いエントロピーを含むレコードを持つ CLFS コンテナーを探します。このルールはファイル コンテンツでループする可能性があるため、regtrans-ms ファイルのみで使用するか、注意して使用することをお勧めします。”

調子:
ファイルサイズ < 100MB およびファイルサイズ >= 512KB
そして uint16(0) == 0x0015 // 署名
and uint8(2) != 0 // fixup 値の上位バイト
そして uint8(3) == 0 // 常に 0
and uint16(4) == uint16(6) and uint16(4) != 0 // num セクタ
uint32(8) == 0 // 常に 0
そして uint32(16) == 1 // 常に 1
そして uint32(20) == 0 // 常に 0
and uint32(40) == 0x70 // レコード ヘッダーのサイズ

そして (0 .. (filesize 512) – 1) の任意の i について:
(
// レコード ヘッダー、セクタ数、およびレコードのサイズを探します
uint16(i*512)==0x0015 および uint16(i*512+4) == uint16(i*512+6) および uint16(i*512+4) != 0 および uint32(i*512+40) = = 0x70 および uint32(i*512+0x88) > 0x200 および uint32(i*512+0x88) < ファイルサイズ
// 可能性のあるブロック ヘッダーを説明するために、レコード [8:] で高いエントロピーを探します
および math.entropy(i*512+0x70+uint16(i*512+0x70+0x22)+8, i*512+0x70+uint16(i*512+0x70+0x22)+uint32(i*512+0x88)- 0x28) > 7.95
)
}

ルール HUNTING_Win_PRIVATELOG_1_strict {

メタ:
著者 = “adrien.bataille@mandiant.com”
description = “文字列とインポートに基づいて PRIVATELOG および STASHLOG バリアントを検出します”
md5 = “91b08896fbda9edb8b6f93a6bc811ec6”

文字列:
$hvid = “GlobalHVID_” ascii
$apci = “GlobalAPCI#” ワイド

調子:
uint16(0) == 0x5A4D および uint32(uint32(0x3C)) == 0x00004550 および
(
それらすべてと
(
pe.imports(“clfsw32.dll”,”CreateLogMarshallingArea”) および
pe.imports(“kernel32.dll”, “VirtualProtect”) および
pe.imports(“ktmw32.dll”, “CreateTransaction”) および
pe.imports(“kernel32.dll”, “CreateFileTransactedA”)
)
)
}

ルール HUNTING_Win_PRIVATELOG_2_notstrict {

メタ:
著者 = “adrien.bataille@mandiant.com”
description = “文字列またはインポートに基づいて、可能性のある PRIVATELOG および STASHLOG バリアントを検出します。このルールは意図的に緩いため、FP レートが高くなる可能性があります。”
md5 = “91b08896fbda9edb8b6f93a6bc811ec6”

文字列:
$hvid = “GlobalHVID_” ascii
$apci = “GlobalAPCI#” ワイド

調子:
uint16(0) == 0x5A4D および uint32(uint32(0x3C)) == 0x00004550 および
(
それらのいずれかまたは
(
pe.imports(“clfsw32.dll”,”CreateLogMarshallingArea”) および
pe.imports(“kernel32.dll”, “VirtualProtect”) および
pe.imports(“ktmw32.dll”, “CreateTransaction”) および
pe.imports(“kernel32.dll”, “CreateFileTransactedA”)
)
)
}

ルール HUNTING_Win_hijack_prntvpt {

メタ:
著者 = “adrien.bataille@mandiant.com”
description = “欠落しているエクスポートに基づいて、正当な prntvpt.dll のハイジャックの可能性を検出します”
md5 = “91b08896fbda9edb8b6f93a6bc811ec6”

調子:
uint16(0) == 0x5A4D および uint32(uint32(0x3C)) == 0x00004550 および
pe.exports(“PTOpenProviderEx”)
pe.exports(“MergeAndValidatePrintTicketThunk”) ではありません
}

EDR/SIEM

Yara による静的ハンティングを補完するために、Mandiant は、典型的な EDR ログの「プロセス」、「イメージロード」、または「ファイル書き込み」イベントで同様の侵害の兆候をハンティングすることも推奨しています。これらは、PRIVATELOG がLoadLibrary()およびGetProcAddress( ) を使用してインポートを動的に解決する場合と、現在知られているサンプルの静的インポートを解決する場合をカバーします。

図 5 は、検索クエリの作成に使用される可能性がある PRIVATELOG によってロードされる主要なモジュール (ktmw32.dll、dbghelp.dll、および clfsw32.dll) を示しています。

実行中の PRIVATELOG プロセスのメモリ ビュー
図 5: 実行中の PRIVATELOG プロセスのメモリ ビュー

ハンティング クエリの例は次のとおりです。

  • C:WindowsSystem32WindowsPowerShellv1.0dbghelp.dll の書き込みまたは読み込みプロセス
  • clfsw32.dll と ktmw32.dll の両方をロードするプロセス
  • svchost.exe -k print loading clfsw32.dll または ktmw32.dll
  • clfsw32.dll をロードする svchost.exe プロセス

svchost.exe に関しては、他の svchost.exe プロセスが ktmw32.dll をロードするケースが多数観察されていますが、svchost.exe プロセスが clfsw32.dll をロードしていることはほとんど観察されていません。

.regtrans-ms または .blf ファイルへのファイル書き込みはかなり一般的ですが、プロセス名とファイル パスをスタックすることでも良い結果が得られる場合があります。たとえば、デフォルト ユーザーのレジストリ トランザクション ファイルへのファイル書き込みは、一般的ではありません。

ハッシュ

プライベートログ

Prntvpt.dll:

1e53559e6be1f941df1a1508bba5bb9763aedba23f946294ce5d92646877b40c

STASHLOG

Shiver.exe:

720610b9067c8afe857819a098a44cab24e9da5cf6a086351d01b73714afd397

MITRE ATT&ACK テクニック

ID

技術

T1012

レジストリのクエリ

T1564

アーティファクトを非表示

T1574

ハイジャック実行の流れ

T1574.002

DLL サイドローディング

T1055.013

プロセス インジェクション: プロセス ドッペルゲンガー

FireEye 製品の検出

プラットフォーム

検出名

ネットワークセキュリティー

メールセキュリティ

オンデマンドでの検出

マルウェア分析

ファイルプロテクト

FE_APT_Loader_Win_PRIVATELOG

FE_APT_Installer_Win_STASHLOG

HX セキュリティ

Generic.mg.0c605276ff21b515

参照: https://www.mandiant.com/resources/blog/unknown-actor-using-clfs-log-files-for-stealth

Comments

タイトルとURLをコピーしました