インポートハッシュによるマルウェアの追跡

脅威グループを長期にわたって追跡することは、防御側がネットワーク上の悪を探し出し、効果的なインシデント対応を実施するのに役立つ重要なツールです。特定のグループがどのように活動しているかを知ることは、効率的な調査に役立ち、攻撃者の活動を簡単に特定するのに役立ちます。

Mandiant では、脅威グループの活動を特定して関連付けるために、いくつかの方法を利用しています。私たちの仕事の重要な部分には、攻撃者のインフラストラクチャや電子メール アドレスなど、さまざまな運用項目の追跡が含まれます。さらに、各脅威グループが利用する特定のバックドアを追跡します。これは、グループの活動を長期にわたって追跡する主要な方法の 1 つです。たとえば、SOGU バックドアを好むグループもあれば、HOMEUNIX を使用するグループもあります。

Mandiant が特定の脅威グループのバックドアを追跡する独自の方法の 1 つは、移植可能な実行可能ファイル (PE) のインポートを追跡することです。インポートは、ソフトウェアの一部 (この場合はバックドア) が他のファイル (通常、Windows オペレーティング システムに機能を提供するさまざまな DLL) から呼び出す関数です。これらのインポートを追跡するために、Mandiant はライブラリ/API 名と実行可能ファイル内の特定の順序に基づいてハッシュを作成します。この規則を「インパッシュ」(「インポート ハッシュ」の意)と呼びます。 PE のインポート テーブルの生成方法 (したがって、そのインハッシュの計算方法) により、インハッシュ値を使用して関連するマルウェア サンプルを特定できます。また、同じ脅威グループが作成して使用した可能性のある新しい類似のサンプルを検索するためにも使用できます。

Mandiant は 1 年以上にわたってこの手法を社内で活用してきましたが、 これについて公に議論したのは当社が初めてではありません。インハッシュは、値自体が比較的ユニークであるため、関連するマルウェアを特定する強力な方法です。これは、コンパイラのリンカが、ソース ファイル内の関数の特定の順序に基づいてインポート アドレス テーブル (IAT) を生成および構築するためです。次のソース コードの例を見てください。

#含む

#含む

#含む

#含む

#pragma comment(lib, “ws2_32.lib”)

#pragma comment(lib, “wininet.lib”)

int makeMutexA()

{

CreateMutexA(NULL, FALSE, “TestMutex”); 0 を返します。 } int makeMutexW() { CreateMutexW(NULL, FALSE, L”TestMutex”); 0 を返します。

}

int makeUserAgent()

{

HANDLE hInet=0, hConn=0;

char buf[sizeof(struct hostent)] = {0};

hInet = InternetOpenA(“User-Agent: (Windows; 5.1)”, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);

hConn = InternetConnectA(hInet, “www.google.com”, 443, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); WSAAsyncGetHostByName(NULL, 3, “www.yahoo.com”, buf, sizeof(struct hostent));

0 を返します。

}

int main(int argc, char *argv[])

{

makeMutexA();

makeMutexW();

makeUserAgent();

0 を返します。

}

そのソース ファイルをコンパイルすると、結果のインポート テーブルは次のようになります。

ws2_32.dll

ws2_32.dll.WSAAsyncGetHostByName

wininet.dll

wininet.dll.InternetOpenA wininet.dll.InternetConnectA

kernel32.dll kernel32.dll.InterlockedIncrement kernel32.dll.IsProcessorFeaturePresent kernel32.dll.GetStringTypeW kernel32.dll.MultiByteToWideChar kernel32.dll.LCMapStringW

kernel32.dll.CreateMutexA kernel32.dll.CreateMutexW

kernel32.dll.GetCommandLineA kernel32.dll.HeapSetInformation kernel32.dll.TerminateProcess

インパッシュ: 0c6803c4e922103c4dca5963aad36ddf

スペースを節約するために表を省略していますが、赤/太字の API はソース コードで参照されている API です。テーブルに表示される順序に注意し、ソース ファイルに表示される順序と比較してください。

作成者がソース コード内の関数の順序や API 呼び出しの順序を変更すると、コンパイルされたインポート テーブルに影響が及びます。前の例を次のように変更します。

#含む

#含む

#含む

#含む

#pragma comment(lib, “ws2_32.lib”)

#pragma comment(lib, “wininet.lib”)

int makeMutexW()

{

CreateMutexW(NULL, FALSE, L”TestMutex”);

0 を返します。

}

int makeMutexA()

{

CreateMutexA(NULL, FALSE, “TestMutex”);

0 を返します。

}

int makeUserAgent()

{

HANDLE hInet=0, hConn=0;

char buf[sizeof(struct hostent)] = {0};

hConn = InternetConnectA(hInet, “www.google.com”, 443, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); hInet = InternetOpenA(“User-Agent: (Windows; 5.1)”, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); WSAAsyncGetHostByName(NULL, 3, “www.yahoo.com”, buf, sizeof(struct hostent));

0 を返します。

}

int main(int argc, char *argv[])

{

makeMutexA();

makeMutexW();

makeUserAgent();

0 を返します。

}

この例では、makeMutexW と makeMutexA、および InternetConnectA と InternetOpenA の順序を逆にしています。 (これは API 呼び出しの無効なシーケンスになることに注意してください。ただし、ここでは要点を説明するために使用します。) 以下は、この変更されたソース コードから生成されたインポート テーブルです (ここでも省略されています)。上記の元の IAT と比較した場合の変更点と、異なるインハッシュ値に注意してください。

ws2_32.dll

ws2_32.dll.WSAAsyncGetHostByName

wininet.dll

wininet.dll.InternetConnectA wininet.dll.InternetOpenA

kernel32.dll kernel32.dll.InterlockedIncrement kernel32.dll.IsProcessorFeaturePresent kernel32.dll.GetStringTypeW kernel32.dll.MultiByteToWideChar kernel32.dll.LCMapStringW

kernel32.dll.CreateMutexW kernel32.dll.CreateMutexA

kernel32.dll.GetCommandLineA kernel32.dll.HeapSetInformation kernel32.dll.TerminateProcess

インパッシュ: b8bb385806b89680e13fc0cf24f4431e

最後の例は、コンパイル時のインクルード ファイルの順序が、結果の IAT (したがって結果の imphash 値) にどのように影響するかを示しています。ファイルimphash1.cimphash2.cを追加して元の例を拡張し、元のソース ファイルimphash.cに含めます。

— imphash1.c —

int makeNamedPipeA()

{

HANDLE ph = CreateNamedPipeA(“.pipe est_pipe”, PIPE_ACCESS_DUPLEX,

PIPE_TYPE_MESSAGE、1、128、

64、200、NULL);

0 を返します。

}

— imphash2.c —

int makeNamedPipeW()

{

HANDLE ph2 = CreateNamedPipeW(L”.pipe est_pipeW”, PIPE_ACCESS_DUPLEX,

PIPE_TYPE_MESSAGE、1、128、

64、200、NULL);

0 を返します。

}

— imphash.c —

#含む

#含む

#含む

#含む

#include “imphash1.h”

#include “imphash2.h”

#pragma comment(lib, “ws2_32.lib”)

#pragma comment(lib, “wininet.lib”)

int makeMutexW()

{

CreateMutexW(NULL, FALSE, L”TestMutex”);

0 を返します。

}

int makeMutexA()

{

CreateMutexA(NULL, FALSE, “TestMutex”);

0 を返します。

}

int makeUserAgent()

{

HANDLE hInet = 0, hConn = 0;

char buf[sizeof(struct hostent)] = {0};

hConn = InternetConnectA(hInet, “www.google.com”, 443, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);

hInet = InternetOpenA(“User-Agent: (Windows; 5.1)”, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); WSAAsyncGetHostByName(NULL, 3, “www.yahoo.com”, buf, sizeof(struct hostent));

0 を返します。

}

int main(int argc, char *argv[])

{

makeMutexA();

makeMutexW();

makeUserAgent();

makeNamedPipeA();

makeNamedPipeW();

0 を返します。

}

次のコマンドを使用して EXE をビルドします。

cl imphash.c imphash1.c imphash2.c /W3 /WX /link

結果の IAT は次のとおりです。

ws2_32.dll

ws2_32.dll.WSAAsyncGetHostByName

wininet.dll

wininet.dll.InternetConnectA

wininet.dll.InternetOpenA

kernel32.dll

kernel32.dll.TlsFree

kernel32.dll.IsProcessorFeaturePresent

kernel32.dll.GetStringTypeW

kernel32.dll.MultiByteToWideChar

kernel32.dll.LCMapStringW

kernel32.dll.CreateMutexW

kernel32.dll.CreateMutexA

kernel32.dll.CreateNamedPipeA kernel32.dll.CreateNamedPipeW

kernel32.dll.GetCommandLineA

kernel32.dll.HeapSetInformation

kernel32.dll.TerminateProcess

インパッシュ: 9129bdbc18cfd1aba498c94e809567d5

ソース ファイルimphash.c内のimphash1.hおよびimphash2.hのインクルードの順序を変更しても、IAT の順序には影響しません。ただし、コマンド ラインでファイルの順序を変更して再コンパイルすると、IAT に影響します。 CreateNamedPipeWCreateNamedPipeAの並べ替えに注意してください。

cl imphash.c imphash2.c imphash1.c /W3 /WX /link

ws2_32.dll

ws2_32.dll.WSAAsyncGetHostByName

wininet.dll

wininet.dll.InternetConnectA

wininet.dll.InternetOpenA

kernel32.dll

kernel32.dll.TlsFree

kernel32.dll.IsProcessorFeaturePresent

kernel32.dll.GetStringTypeW

kernel32.dll.MultiByteToWideChar

kernel32.dll.LCMapStringW

kernel32.dll.CreateMutexW

kernel32.dll.CreateMutexA

kernel32.dll.CreateNamedPipeW kernel32.dll.CreateNamedPipeA

kernel32.dll.GetCommandLineA

kernel32.dll.HeapSetInformation

kernel32.dll.TerminateProcess

インパッシュ: c259e28326b63577c31ee2c01b25d3fa

これらの例は、元のソース コード内の関数の順序と、コンパイル時のソース ファイルの順序の両方が結果の IAT に影響し、結果としての imphash 値に影響することを示しています。ソース コードは同じように編成されていないため、まったく同じインポートを持つ 2 つの異なるバイナリは、異なるインポート ハッシュを持つ可能性が高くなります。逆に、2 つのファイルが同じ imphash 値を持つ場合、それらのファイルは同じ IAT を持ちます。これは、ファイルが同じソース コードから同じ方法でコンパイルされたことを意味します。

パックされたサンプル、単純なツールまたはユーティリティ (インポートがほとんどなく、その単純さに基づいて同じ方法でコンパイルされる可能性が高い) の場合、インハッシュ値は、帰属に役立つほど一意ではない可能性があります。つまり、2 人の異なる脅威アクターが、これらの要因に基づいて同じインハッシュを持つツールを個別に生成する可能性があります。

ただし、十分な数のインポートが存在する、より複雑なツールやカスタム ツール (バックドアなど) の場合、インハッシュは比較的一意である必要があるため、構造的に類似しているコード ファミリを識別するために使用できます。同じインハッシュを持つファイルが同じ脅威グループから発信されたものであるとは限りませんが (たとえば、ファイルがグループ間で共有されている共通のビルダーによって生成された可能性があります)、ファイルには少なくとも共通の脅威グループがあると合理的に想定できます。最終的には、追加の裏付け情報を含む単一の脅威グループに起因する可能性があります。

この方法を採用することで、一定期間にわたって攻撃者のバックドアを検証し、バックドアと関連する脅威グループとの関係を実証することに大きな成功を収めました。

Mandiant は、特定の PE のインハッシュ値の計算を可能にするパッチを Ero Carrera の pefile (http://code.google.com/p/pefile/) に送信しました。

コード例:

インポート ファイル

pe = pefile.PE(sys.argv[1])

print “ハッシュのインポート: %s” % pe.get_imphash()

Mandiant は、特定のインポートの序数を特定の関数にマップする必要があるインハッシュ規則を使用します。序数によって一般的に参照される関数を pefile にエクスポートする DLL のルックアップをいくつか追加しました。

Mandiant のインハッシュ規則では、次のことが必要です。

  • 関数名が現れたときに序数を関数名に解決する
  • DLL 名と関数名の両方をすべて小文字に変換する
  • インポートされたモジュール名からファイル拡張子を削除する
  • 小文字の文字列を作成して保存します。順序付きリストで
  • 順序付きリストの MD5 ハッシュの生成

この規則は、pefile.py バージョン 1.2.10-139 の 3618 行目から実装されています。

インハッシュ値がマルウェア ファミリ (および潜在的に特定の脅威グループ) の比較的一意の識別子として機能する場合、この手法について議論することで攻撃者に警告し、攻撃者に方法を変更させることはありませんか?攻撃者は、(マルウェア自体の機能に影響を与えない方法で) ソース コードを変更するか、コンパイル時にファイルの順序を変更する必要があります (ソース コードが複数のファイルに分散していると仮定します)。攻撃者はインハッシュを変更するためのツールを作成できますが、多くの攻撃者がこれを行うのに十分な注意を払っているとは思われません。

マルウェアのサンプルについてより高いレベルで議論し、攻撃者や脅威グループに関する情報を交換する方法として、用語集に imphash を追加することが重要であると考えています。たとえば、インシデント レスポンダーは、議論されている正確なサンプル (特定の MD5) を具体的に開示することなく、インハッシュ値を使用してマルウェアについて議論することができます。

攻撃者が、C2 の場所とキャンペーン ID が異なるバックドアの 30 の亜種をコンパイルし、さまざまな企業に展開するシナリオを考えてみましょう。特定の MD5 がキャンペーンの一部として識別されたことを示すブログ投稿が公開された場合、攻撃者はその MD5 に基づいて、どのインフラストラクチャ (C2 ドメインや関連付けられた IP アドレスなど) が危険にさらされているか、どのキャンペーンが危険にさらされているかをすぐに知ることができます。 .ただし、インハッシュ値だけでマルウェアを識別した場合、攻撃者の 30 の亜種すべてでインハッシュが共有されている可能性があります。マルウェアは依然としてセキュリティ コミュニティによって識別可能であり、セキュリティ コミュニティ内で議論することができますが、攻撃者は特定された特定のサンプルや、インフラストラクチャのどの部分が危険にさらされているかを知りません。

この分析方法の有効性を実証するために、Mandiant APT1 レポートからいくつかのマルウェア ファミリのインハッシュ値を共有することにしました。

 

苗字 ハッシュのインポート 総輸入 の数

一致した

サンプル

グリーンキャット 2c26ec4a570a502ed3e8484295581989 74 23
グリーンキャット b722c33458882a1ab65a13e99efe357e 74 18
グリーンキャット 2d24325daea16e770eb82fa6774d70f1 113 13
グリーンキャット 0d72b49ed68430225595cc1efb43ced9 100 13
スターサイパウンド 959711e93a68941639fd8b7fba3ca28f 62 31
クッキーバッグ 4cec0085b43f40b4743dc218c585f2ec 79 10
ニュースリール 3b10d6b16f135c366fc8e88cba49bc6c 77 41
ニュースリール 4f0aca83dfe82b02bbecce448ce8be00 80 10
TABMSGSQL ee22b62aa3a63b7c17316d219d555891 102 9
WEBC2 a1a42f57ff30983efda08b68fedd3cfc 63 25
WEBC2 7276a74b59de5761801b35c672c9ccb4 52 13

上記のマルウェア ファミリと、2013 年 2 月にリリースされた Mandiant APT1 レポートから、一連のマルウェアの対応するインハッシュ値を計算しました。上記のインハッシュ法を使用して、すべてのサンプルのインハッシュ値を計算し、サンプルの総数を数えました。各インハッシュで一致しました。レポートの合計 356 のサンプルを使用して、それぞれのファミリーを大幅にカバーする 11 のインハッシュ値を特定することができました。これらのインハッシュ値からピボットすることで、さらに分析を行った結果、同じマルウェア ファミリの一部であり、同じ脅威グループに起因することが示された追加のマルウェア サンプルを特定することができました。

他の方法と同様に、Imphash 分析には限界があり、単一の成功ポイントと見なすべきではありません。 2 つのバイナリが同じインハッシュ値を持っているからといって、それらが同じ脅威グループに属しているとは限らず、同じマルウェア ファミリの一部であるとは限りません (ただし、その可能性は高くなります)。 Imphash 分析は、潜在的なマルウェア サンプルをトリアージし、さらなる分析に値する「興味深い」サンプルを特定することで発見を拡大するための、低コストで効率的かつ価値のある方法です。インハッシュ値は、アナリストが脅威グループとそのツールを発見する際のもう 1 つのピボット ポイントとなります。この方法を採用すると、時間の経過とともに攻撃者のバックドアを追跡および検証する結果が得られ、バックドアと脅威グループの間の関係を明らかにするのに役立ちます。ハッピーハンティング!

参照: https://www.mandiant.com/resources/blog/tracking-malware-import-hashing

Comments

Copied title and URL