平凡な視界に隠されている: BLACKMATTER ランサムウェアの暗号の識別

Figure 1 news

ランサムウェアのサンプルを評価する主な目的の 1 つは、サンプルが使用している暗号化の種類を特定することです。これは単純な場合もあります。私たちが分析した BLACKMATTER サンプルでは、そうではありませんでした。 BLACKMATTER コードから RSA 暗号の数学的操作を特定するために使用したプロセスは興味深いものであり、他のマルウェア サンプルに再利用でき、一般的に優れたリバース エンジニアになることができました。

序章

BLACKMATTER ランサムウェア ファミリーは、最近のいくつかの攻撃で確認されています。他の著者は、BLACKMATTER の要約を公開しており、これには以前のファミリーである REVIL および DARKSIDE との関係、および暗号化の方法が含まれています。さらに、米国保健社会福祉省は、過去のタイムライン、関連する脅威グループ、および被害者のタイプをカバーするBLACKMATTER の概要を公開しました。

Mandiant の FLARE チームは、32 ビットの BLACKMATTER 亜種 (sha256: 5da8d2e1b36be0d661d276ea6523760dbe3fa4f3fdb7e32b144812ce50c483fa) の内部分析を実行しました。多くのランサムウェア ファミリーと同様に、BLACKMATTER は対称暗号と非対称暗号の組み合わせを使用して、身代金のために被害者のデータを保持します。 BLACKMATTER サンプルは、その構成内に非対称公開鍵を持ち、対応する秘密鍵を保持しているのは攻撃者だけです。被害者の各ファイルを攻撃するとき、BLACKMATTER はまず対称暗号を使用してファイルを暗号化します。次に、BLACKMATTER は非対称公開鍵を使用して対称鍵を暗号化し、その暗号化された鍵をファイルの末尾に追加します。その後、対称キーは破棄されます。この設計は、攻撃者の秘密鍵のみがファイルごとの鍵を復号化して元のデータを復元できるため、BLACKMATTER バイナリのリバース エンジニアリングだけでは被害者がファイルを復号化できないことを意味します。

BLACKMATTER をリバースすると、その対称暗号化が Salsa20 の修正版であることがすぐにわかりました。非対称暗号化はそれほど明白ではありませんでした。ランサムウェアのサンプルは、多くの場合、Windowsの wincrypt 、OpenSSL、Crypto++ などの暗号化ライブラリを使用しています。多くの場合、ライブラリは静的にリンクされているため、識別がやや難しくなります。 BLACKMATTER はユニークで、識別可能な暗号化ライブラリを使用していませんでした。一部の暗号化ルーチンには明確な動作があります。RC4 は 256 バイトの配列に 0 ~ 255 の値を入力し、Salsa20 は 7、9、13、および 18 ビットの左ローテーションを実行します。 BLACKMATTER の非対称コードが使い慣れたアルゴリズムであるとすぐには認識できませんでした。マジック ナンバーは、ChaCha20 の「拡張 32 バイト k」、AES の Te および Td テーブル、および30 82 02 (または base64 エンコード後の MIIC ) などの ASN.1 関連のバイト シーケンスなどの暗号アルゴリズムを識別できます。また、マジック ナンバーも認識しませんでした。

最終的に、私たちは、BLACKMATTER の非対称暗号方式が 1024 ビット RSA であることを特定しました。これは、排除のプロセス (および BLACKMATTER とその前身のファミリーの以前の分析) を通じて部分的に特定した一般的で目立たない選択肢ですが、BLACKMATTER で採用されている数学的操作と、それらをRSAの背後にある生の数学に接続します。これは、他のマルウェア サンプルやリバース エンジニアにとって興味深く、再利用可能であると考えた RSA アルゴリズムを見つけて特定するために採用したプロセスであり、このブログ投稿の焦点です。

RSAのレビュー

まず、RSA の仕組みを簡単に説明します。 RSA (Rivest-Shamir-Adleman) は、非対称暗号の典型的な暗号システムです。個人は、アルゴリズムに従って鍵のペア (公開鍵と秘密鍵) を生成します。秘密鍵は秘密にされ、公開鍵は広く配布されます。他の用途の中でも、公開鍵を使用してメッセージを暗号化し、保護されていない通信チャネルを介して配信することにより、任意の当事者が個人に秘密のメッセージを送信できます。正当な使用法では、RSA を電子商取引で使用して、クレジット カード トランザクションで情報を暗号化するための共有秘密を確立できます。ランサムウェアのような違法な使用において、RSA は、支払いが受領されてから秘密鍵と交換されるまで、ファイルの復号化に必要な情報を隠すのに最適です。

RSA は剰余累乗の原則に基づいて動作します。これは実行は簡単ですが、元に戻すのは困難です。以下は、RSA キーの生成方法を非常に簡単に説明したものです。

  1. 2 つの異なる大きな素数pqを選択します。 n = pqおよびϕ(n) = (p-1)(q-1) とします。 nは秘密ではなく、公開鍵の一部として自由に共有できますが、 ϕ(n)を知っていれば誰でも暗号を破ることができます。
  2. 暗号化の指数を選択します。実際には、 e = 65537がほぼ常に使用されます。
  3. 復号化に対応する指数d ≡ e -1 (mod ϕ(n)) を決定します。これは、拡張ユークリッド アルゴリズムを使用すると簡単ですが、 ϕ(n)を知らなければ扱いにくいものです。
  4. ペア ( n, e ) は公開鍵です。 dは秘密鍵です。 dを導き出すのに十分な情報 ( pqまたはϕ(n)など) も秘密にしておく必要があります。

次に、メッセージは、E(m) = m e mod nを計算することによって暗号化され、 D ( E ( m )) = ( m e )d mod nm 1 (mod n ) を計算することによって復号化されます。 n、m、e 、およびdのこのプロパティは、フェルマーの小定理に由来し、RSA のより詳細な説明を確認することで調べることができます。

バイナリ累乗

e = 65537の選択は意図的なものであり、安全性を維持しながら効率的に暗号化を実行できます。 e = 65537 = 65536 + 1 = 2 16 +1なので、 m 65537 mod nは、次のように 2 進累乗を使用して計算できます。

x=m

i = 1から16の場合:

x = x 2 mod n

x = xm mod n

RSA 暗号化がこの形式に要約されると、唯一複雑になるのは、「大きな数」の剰余乗算、つまりf ( x, y, n) = xy mod nを実行する関数が必要になることです。必然的に、BLACKMATTER を分析しているときに、モジュラ乗算を実行する関数に注意が向けられましたが、その目的は明らかではありませんでした。さらに、上記のように実装すると、定数 65537 がコードに表示されることはありません。次に、BLACKMATTER サンプルの各非対称暗号機能が RSA 暗号化プロセスの一部としてどのように適合するかを検証した方法を示す説明を提示します。

乗算機能

2 つの符号なし 2 進整数の乗算は、一連の加算と 2 による乗算として表すことができます。たとえば、次のようになります。

145・113

145 ∙ ( 2 6 + 2 5 + 2 4 + 2 0)

(145 ∙ 2 2 + 145 ∙ 2 + 145) 2 4 + 145 ∙ 2 0

((145 ∙ 2 + 145) ∙ 2 + 145) ∙ 2 ∙ 2 ∙ 2 ∙ 2 + 145

2 による乗算は 1 ビットの左ビット シフトにすぎないため、左シフトと加算のみを使用してx ∙ yを乗算し、 xの各ビットを調べて、各反復で実行中の積にyを加算する必要があるかどうかを判断できます。実行中の製品を左にシフトする前に。 BLACKMATTER の大きな数の乗算関数sub_401B24 (x, y, n)は、この正確な原理に基づいてx = (x * y) % nを計算します。

もちろん、x86 マシンには 1024 ビット レジスタや即値はありません。そのため、1024 ビット整数に対する操作は、1024 ビット整数の各 32 ビット チャンクに対する 32 の操作に分割する必要があります。 x86 命令rcladc 、および sbb、キャリー フラグを使用して 0 ビットまたは 1 ビットを次の 32 ビット チャンクに適切に伝達することで、このような大きな数の演算を可能にします。

まず、大きな数の乗算関数は、実行中の積を保持する一時的な 1024 ビット整数zにスタック スペースを割り当て、それをゼロに初期化します (図 1)。

Figure 1
図 1: 大数乗算関数は、1024 ビット整数 z をゼロに初期化することから開始します

次に、大きな数の乗算関数が、その入力と出力の各ビットをループします。反復ごとに、関数は最初にzを 1 ビット左にシフトします (図 2)。

図 2
図 2: 大きな数の乗算関数は、z を 1 ビット左にシフトします。 z は 1024 ビット長であるため、rcl 命令を使用すると、各 32 ビット チャンクでシフトを実行できます。

この計算はzを 2 倍するため、 zn を超える可能性があります。計算が mod nで実行されるようにするために、関数は次にzからnを減算します (図 3)。

図 3
図 3: z を 2 倍にした後、大きな数の乗算で z から n を減算して、計算が mod n で行われるようにする必要があります。

マルウェアは減算を実行する前にz ≥ nかどうかをチェックしなかったため、結果が負の場合、関数はznを加算して前の減算を逆にし、 z[0, n)の範囲に戻します (図 4)。マルウェアがこのように動作するのは、前の減算を実行する前にznを比較すると、減算を実行するのと同じくらい計算コストがかかるためです。

図 4
図 4: z -= n が負の結果を生成した場合、n が z に追加されて、z が n を法とする正しい範囲に復元されます

ここで、関数はxを 1 ビット左にシフトします。 xの以前の左端のビットは、キャリー フラグに残されます。ビットが 1 の場合、 yzに追加されます。 0 の場合、 yzに追加されません (図 5)。

図 5
図 5: 大数乗算関数は、x を 1 ビット左にシフトし、前の左端のビットを使用して、この反復で実行中の積 z に y を加算する必要があるかどうかを決定します

いずれの場合も、 zは再び範囲[0,n)に復元されるため、すべての計算は mod nのままになります。このプロセスは 1024 回繰り返されます。別の言い方をすれば、 xをビットごとに調べて、シフトする前に各反復で中間積zyを加算する必要があるかどうかを判断します。すべての反復の後、 xは積zで上書きされます (図 6)。

図 6
図 6: 計算が完了すると、値 x は実行中の積 z で上書きされます

sub_401B24(x, x, n) を呼び出すことにより、 sub_401B24を使用してx 2 mod nを計算できることに注意してください。

二項累乗関数

前述の大きな数の乗算関数は、2 進累乗関数sub_4019C0 ( x, buf, n, m ) であると分析したものによって繰り返し呼び出されていることがわかりました。この 2 進累乗関数は、m が NULL の場合は x = x 256 mod n を計算し、 mNULLない場合x = m x 256 mod n計算します。 2 進累乗関数がxを 8 回平方し、オプションでmを乗算する場合、2 進累乗関数は「アンロール」方式で大きな数の乗算関数を 9 回個別に呼び出すため、その目的を特定するのが少し難しくなります。以下は、2 進累乗関数からの IDA 疑似コードの抜粋です。

const __m128i *__stdcall sub_4019C0 (const __m128i *x, __m128i *buf, int n, int m)
{

x0 = x;
y0 = buf;
i0 = 8;
行う
{
*y0++ = _mm_load_si128 (x0++) ;
–i0;
}
ながら ( i0 );
y1 = buf;
x1 = x;
sub_401B24((__m128i *)x, (int)buf, (_DWORD *) n);
i1 = 8;
行う
{
*y1++ = _mm_load_si128(x1++);
–i1;
}
ながら ( i1 );

y8 = buf;
x8 = x;
result = sub_401B24 ((__m128i *)x, (int) buf, (_DWORD *) n);
もし (m)
{
i8 = 8;
行う
{
*y8++ = _mm_load_si128 (x8++);
–i8;
}
ながら ( i8 );
return sub_401B24 ((__m128i *) x, m, (_DWORD *) n);
}
結果を返します。
}

RSA暗号機能

ピースをまとめると、他のルーチンをラップする BLACKMATTER の RSA 暗号化機能を理解できるようになりました。 RSA 暗号化関数sub_40183C ( m, n ) は、 m= m 65537 mod nを計算してmを暗号化します。個々のステップとして、次のように動作します。

  1. bufxを 1024 ビットの整数とし、x = 1 とします。
  2. 2 進累乗関数sub_4019C0 ( x, buf, n, m ) を呼び出してx = mx 256 mod nを計算します。これは、 x = 1 であるため、単にx = mです。
  3. 2 進累乗関数sub_4019C0 ( x, buf, n, 0 ) を呼び出して、 x = x 256 mod nを計算します。
  4. 2 進累乗関数sub_4019C0 ( x, buf, n, m ) を呼び出して、 x = mx 256 mod nを計算します。

これらのステップの最後に、 x = m( m 256 ) 256 mod n = m 65537 mod n .

以下は、RSA 暗号化関数全体からの IDA 疑似コードの抜粋です。

__m128i *__stdcall sub_40183C(__m128i *m, __m128i *n)
{

x [0] .m128i_i64 [1] = 0i64;
memset (&x [1], 0, 112);
x [0] .m128i_i64 [0] = 1i64;
sub_4019C0 (x、buf、n、m);
sub_4019C0 (x、buf、n、0);
sub_4019C0 (x、buf、n、m);
px = x;

午後=メートル;
私は= 8;
行う
{
*pm++ = *px++;
– 私;
}
ながら (私);
px を返します。
}

Python は大きな整数をネイティブにサポートしているため、BLACKMATTER の計算全体を Python で次のように単純化できます。

x = m
範囲内の i の場合 (16):
x = x * x % n
x = x * m % n

結論

BLACKMATTER を分析したところ、作成者は外部ライブラリを使用せずに 3 つの独立した関数だけで RSA 暗号化を達成したことがわかりました。 RSA 公開鍵は、バイナリでは 1024 ビットの整数であるため、目立ちません。コードがどのように構造化されているかにより、65537 などのマジック ナンバーはありません。数値 65537 は、16 回の二乗とそれに続く 1 回の最終的なmによる乗算を実行することで暗黙的に示されます。作成者が 3 つのルーチン内でインライン アセンブリを使用した可能性があります。これらの観察結果が、他のマルウェアの RSA 実装を特定するのに役立つことを願っています。また、難読化とパッキングによってコードの意味をあいまいにする代わりに、攻撃者は、外部依存関係のない暗号化アルゴリズムの独立した最小限の実装を作成することで、コードの意味を明白に隠すことができるという洞察に満ちたリマインダーも発見しました.

参照: https://www.mandiant.com/resources/blog/cryptography-blackmatter-ransomware

Comments

Copied title and URL