コロニアルパイプラインのランサムウェア攻撃:ダークサイドの仕組みが明らかに

コロニアルパイプラインのランサムウェア攻撃:ダークサイドの仕組みが明らかに

ここ2週間ほど、サイバーセキュリティ・コミュニティ全体がコロニアル・パイプラインのランサムウェア攻撃に釘付けになっている。この攻撃は、ここ数年の重要インフラに対する攻撃の中でも最も注目すべきものの1つであり、米国経済の複数の産業に直接的・間接的な影響を及ぼしている。ありがたいことに、約1週間の停止と500万ドルの身代金の支払いが報告された後、オペレーションは再開されている1。

Colonial Pipeline社に対して展開されたRansomware as a Service(RaaS)であるDarkSideは、世界中の組織を攻撃する同様のマルウェアの好例である。周到に準備され、展開されたこのマルウェアは、被害者から恐喝を成功させるために様々なテクニックを組み合わせて使用する。

Nozomi Networks LabsはDarkSide実行ファイルの内部を研究し、本日、そのマシンコードが3つの領域(被害者とファイルの選択、匿名性の確保と検知防止、データ復元の防止)で使用するテクニックを明らかにするための調査結果を共有します。また、DarkSideの検出に役立つIoCと復号スクリプトも提供します。

ダークサイドのコードの合計は、物理的な世界で壊滅的な結果をもたらすことを忘れてはなりません。DarkSideのテクニックを理解し、自社の防御とインシデントレスポンス能力を評価することをお勧めします。

DarkSide ランサムウェア:テクニカル分析

被害者の検証

マルウェアはまず、被害者のコンピューター・システムの基本情報を収集し、技術環境の詳細を知る。

マルウェアは感染したコンピュータの名前を取得する。
マルウェアは感染したコンピュータの名前を取得する。
DarkSideは被害者の基本的なシステム情報を収集します。
DarkSideは被害者の基本的なシステム情報を収集します。

さらに、システムで使用されている言語をチェックすることで、特定の地域からの被害者をスキップする。(特に、DarkSideはロシア語やその他の東欧言語を使用するシステムを攻撃しない2)。

このランサムウェアは、システムの言語がCIS諸国で使用されているものかどうかをチェックする。

暗号化するファイルの選択

次に、DarkSideは暗号化するファイルを決定します。マルウェアがシステム上で利用可能なすべてのファイルを暗号化しようとすると、システムはすぐに使用不能になり、被害者は攻撃者に連絡するための情報を失うことになります。さらに、攻撃を実行するために必要な時間よりも、暗号化にかかる時間の方がはるかに長くなります。DarkSideは、暗号化するファイルを特に厳選しており、主にファイル・ディレクトリ、ファイル名、ファイル拡張子を調べて選択します。

暗号化中にスキップされたディレクトリ、ファイル名、ファイル拡張子のリスト。
暗号化中にスキップされたディレクトリ、ファイル名、ファイル拡張子のリスト。

匿名性

匿名性を保ち、迅速なシャットダウンを防ぐため、ランサムウェアの脅威行為者と接触するためのウェブサイトはTorネットワークでホストされている。

Torベースのウェブサイトへのアクセス方法を説明するDarkSideの説明書の一部。
Torベースのウェブサイトへのアクセス方法を説明するDarkSideの説明書の一部。

探知防止技術

被害者のシステムが影響を受けるまで水面下に潜伏するため、DarkSideは一般的に使用されている様々なテクニックを取り入れている。

自己暗号化

ダークサイドの重要な文字列のほとんどは、検知を避けるために暗号化されている。

XORベースの復号アルゴリズム。
XORベースの復号アルゴリズム。

同じ理由で、マルウェアのメインコンフィギュレーションも暗号化されている。これはaPLibで圧縮されており、個々の設定値はBase64アルゴリズムでエンコードされている。

コンフィギュレーション・ブロックの復号と伸張。
コンフィギュレーション・ブロックの復号と伸張。
マルウェア内のBase64エンコードされた設定値。
マルウェア内のBase64エンコードされた設定値。

動的API解決

WinAPIは、ファイルやネットワーク操作を含む特定の機能にアクセスするために、プログラムがWindowsオペレーティングシステムと相互作用する標準的な方法である。そのため、これらのインターフェイスを使用すると、マルウェアの実際の目的がセキュリティ・システムにすぐに明らかになります。 

検出を防ぐため、DarkSideは、正規の実行ファイルのように、使用するすべてのAPIをインポートテーブルですぐに利用できるようにはしていません。その代わりに、使用する前に、ハッシュ化された名前と暗号化された名前を動的に解決します。

DarkSideが使用する動的なWinAPIの解像度。
DarkSideが使用する動的なWinAPIの解像度。

データ復元を防ぐ

システム管理者が犯罪者に金銭を支払うことなく、感染したデータを迅速かつ容易に復元できれば、ランサムウェア攻撃は成功しない。DarkSideの作者は、身代金の支払いを確実にするために複数のテクニックを取り入れている。

バックアップへの対応

ランサムウェアは、標的にされたマシンで標準的なバックアップ・ソリューションが使えないようにする。Windowsには、このような状況に対処するためのシャドウ・コピーと呼ばれる機能がある。これは、コンピュータ・ファイルのバックアップ・コピーを作成し、必要なときに復元できるようにするものだ。このアプローチの主な制限は、バックアップ・ファイルが元のファイルと同じシステム上に保存されることである。マルウェアがシステムを危険にさらすと、バックアップファイルはすぐに削除されてしまう。

シャドウコピーバックアップのリストを取得するためのコマンド。
シャドウコピーバックアップのリストを取得するためのコマンド。

さらに、マルウェアはバックアップを名前で検索することができる:

ランサムウェアによるバックアップの検索と削除。
ランサムウェアによるバックアップの検索と削除。

最後に、DarkSideはさまざまなバックアップソリューションを無効にしようとし、名前から探します。

組み込み設定から終了させるサービスのリスト。
組み込み設定から終了させるサービスのリスト。
DarkSideがバックアップ関連サービスの停止と削除に使用するプロセス。
DarkSideがバックアップ関連サービスの停止と削除に使用するプロセス。

対称暗号と非対称暗号の正しい使い方

第一世代のランサムウェアの多くは適切な暗号化を欠いていたため、被害者はシステム上のファイルを無料で復元することができた。残念ながら、そのような時代はとうの昔に過ぎ去り、現代のマルウェア・ファミリーはこの過ちを繰り返しません。

現在の主な違いは、対称型暗号が非対称型暗号の使用によって強化されていることだ。前者は、データの暗号化と復号化の両方に同じ秘密鍵を使用するため、それを傍受するだけでデータへのアクセスを復元できる。

一方、非対称暗号化では秘密鍵と公開鍵という概念を用いる。暗号化は公開鍵を使って行われるが、復号化は秘密鍵なしでは不可能だ。DarkSideマルウェアは、公開鍵のみをマルウェアに埋め込み、秘密鍵は秘匿することで、この機能を適切に実装しています。

 対称暗号に対する非対称暗号の主な欠点は、暗号化速度である。DarkSideの作者は、両方の長所を生かすため、対称暗号化アルゴリズム(カスタムマトリックスを使用したSalsa20)を使用して被害者のファイルを暗号化し、対応する対称鍵を非対称公開鍵(RSA-1024)で暗号化します。

カスタムマトリックスによる対称Salsa20暗号化アルゴリズム。
カスタムマトリックスによる対称Salsa20暗号化アルゴリズム。

DarkSideが最新のランサムウェア技術を実演

DarkSideは、その目的を達成するために、長い歴史を持つ複数のテクニックを組み合わせた最新のランサムウェアファミリーの一例に過ぎない。また、人気を集めているRaaSモデルの有効性も浮き彫りにしている。このモデルでは、各攻撃に複数の関係者が関与し、各関係者の強みを生かした分業が行われる。

RaaSでは、経験豊富なマルウェア作成者が中核となるランサムウェアコードの開発に専念し、標的組織のネットワークにアクセスすることを専門とする関連会社に配備を任せる。DarkSideの場合、40人以上の被害者が総額9,000万ドルのビットコインを支払っており、その内訳は開発グループに1,550万ドル、関連会社に7,470万ドルだと推定されている3。

このDarkSideの技術的分析が、ランサムウェアのテクニックをより深く理解し、自社の防御およびインシデントレスポンス能力を評価する一助となれば幸いです。また、DarkSideの検出に役立つように、IoCと埋め込み文字列を復号化するためのスクリプトがこの記事の最後に用意されています。

マルウェア・キルチェーンの初期段階で異常な動作やアクティビティを検出できるネットワーク・モニタリング・ツールを使用することで、最終的なペイロードが実行される前にランサムウェアを封じ込める最善のチャンスが得られることは言うまでもありません。また、このようなツールは、タイムリーな対応を支援するために、ログやpcapだけでなく、実用的なフォレンジック情報も提供します。

参考文献

  1. 「コロニアル・パイプラインはハッカーに500万ドルの身代金を支払った」、CNBC、2021年5月13日。
  2. 「ロシアのグループDarkSideが主張する植民地パイプラインのハッキングがホワイトハウスの緊急命令を促した」NBCニュース、2021年5月10日。
  3. 「DarkSideランサムウェアはビットコインで9000万ドル以上の利益を得ている」、Elliptic、2021年5月18日。

IOC

  • 0a0c225f0e5ee941a79f2b7701f1285e4975a2859eb4d025d96d9e366e81abb9
  • baroquetees[.]com
  • rumahsia[.]com

スクリプト

埋め込まれた文字列を復号化するIDAPythonスクリプトを示します:

# Author: Nozomi Networks Labs

import idautils

import idaapi

import idc

import struct

  

def is_utf16_heur(string):

    counter = 0

    for val in string:

        if val == '\x00':

            counter += 1

    if counter/float(len(string)) > 0.4:

        return True

    return False

 

def chunks(lst, n):

    for i in range(0, len(lst), n):

        yield lst[i:i + n]

 

def decrypt_block(enc_string, key_matrix):

    dec_string = []

    for enc_block in chunks(list(enc_string), 255):

        temp_key_matrix = key_matrix.copy()

        bl = 0

        for i in range(len(enc_block)):

            bl = (bl +temp_key_matrix[i+1]) & 0xFF

            al = temp_key_matrix[i+1]

            ch = temp_key_matrix[bl]

            temp_key_matrix[bl] = al

            temp_key_matrix[i+1] = ch

            al = (al+ ch) & 0xFF

            al= temp_key_matrix[al]

            enc_block[i]= enc_block[i] ^ al

        dec_string += enc_block

    dec_string = ''.join(map(lambda x: chr(x), dec_string))

    return dec_string

 

def guess_encoding(dec_string):

    utf16_flag = False

    if is_utf16_heur(dec_string):

        try:

            dec_string_print =dec_string.encode('latin-1').decode('utf-16le')

            idc.set_inf_attr(INF_STRTYPE,STRTYPE_C_16)

            utf16_flag = True

        except Exception as e:

            pass

    if not utf16_flag:

        dec_string_print = dec_string

        idc.set_inf_attr(INF_STRTYPE, STRTYPE_C)

    # dec_string_print = dec_string_print.replace('\r','\\r').replace('\n', '\\n')

    return dec_string_print

 

def decrypt_all(enc_func, key_matrix):

    for ref in idautils.CodeRefsTo(enc_func, True):

        arg_addr = idc.prev_head(ref)

        if idc.print_insn_mnem(arg_addr) == 'push':

            enc_string_addr =idc.get_operand_value(arg_addr, 0)

            if enc_string_addr == 0:

                print('Warning:wrong address of the encrypted string at %x: %x' % (arg_addr, enc_string_addr))

                continue

            enc_string_size =struct.unpack('<I', idc.get_bytes(enc_string_addr-4, 4))[0]

            if enc_string_size <0xFFFF:

                enc_string =idc.get_bytes(enc_string_addr, enc_string_size)

            else:

                print('Warning:excessively long encrypted string at %x - %x' % (arg_addr, enc_string_addr))

                exit(1)

            dec_string =decrypt_block(enc_string, key_matrix)

            dec_string_print =guess_encoding(dec_string)

            print('%x: %s' %(enc_string_addr, dec_string_print))

           idaapi.patch_bytes(enc_string_addr, dec_string.encode('latin-1'))

           idc.create_strlit(enc_string_addr, enc_string_addr+enc_string_size)

        else:

            print('Warning: non-standardargument at %x: %x' % (ref, arg_addr))

  

print('Start decryption')

with open('c:\\work\\key_matrix.bin', 'rb') as fi:

    key_matrix = list(fi.read())

decrypt_all(idc.get_screen_ea(), key_matrix)

print('Done!')