分析勒索軟體加密機制 是做什麼的?
使用時機
- 發生勒索軟體感染,復原作業需要了解所使用的加密方案時
- 評估在不支付贖金的情況下是否能解密(實作缺陷、已知解密工具)
- 對勒索軟體進行逆向工程,以識別加密演算法、金鑰衍生及金鑰儲存機制
- 在發現勒索軟體加密實作存在弱點時開發解密工具
- 根據加密方法對勒索軟體樣本進行分類,以便歸屬於已知勒索軟體家族
請勿在未先於加密檔案的測試副本上驗證解密方法的情況下,直接用於正式資料復原作業。
先決條件
- Ghidra 或 IDA Pro,用於逆向工程勒索軟體二進位檔
- Python 3.8+,搭配
pycryptodome函式庫,用於測試加解密常式 - 加密檔案的樣本及其對應的明文原始檔(已知明文配對)
- 存取勒索軟體二進位檔(如有需要,需先解包)
- 熟悉對稱式(AES、ChaCha20)和非對稱式(RSA)加密演算法
- NoMoreRansom.org 資料庫,用於檢查現有的免費解密工具
工作流程
步驟 1:識別加密演算法
判斷勒索軟體使用哪種加密演算法:
# 檢查匯入表中是否使用 Windows Crypto API
import pefile
pe = pefile.PE("ransomware.exe")
crypto_apis = {
"CryptAcquireContextA": "Windows CryptoAPI",
"CryptAcquireContextW": "Windows CryptoAPI",
"CryptGenKey": "Windows CryptoAPI 金鑰產生",
"CryptEncrypt": "Windows CryptoAPI 加密",
"CryptImportKey": "Windows CryptoAPI 金鑰匯入",
"BCryptOpenAlgorithmProvider": "Windows CNG (現代加密)",
"BCryptEncrypt": "Windows CNG 加密",
"BCryptGenerateKeyPair": "Windows CNG 非對稱式金鑰產生",
}
print("Crypto API 匯入:")
for entry in pe.DIRECTORY_ENTRY_IMPORT:
for imp in entry.imports:
if imp.name and imp.name.decode() in crypto_apis:
print(f" {entry.dll.decode()} -> {imp.name.decode()}: {crypto_apis[imp.name.decode()]}")
常見勒索軟體加密方案:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
AES-256-CBC + RSA-2048: 最常見的混合式方案 (LockBit、REvil、Conti)
AES-256-CTR + RSA-4096: 串流加密模式變體 (BlackCat/ALPHV)
ChaCha20 + RSA-4096: 現代串流加密 (Hive、Royal)
Salsa20 + ECDH: Curve25519 金鑰交換 (Babuk)
AES-128-ECB: 弱模式 - 可能可透過已知明文攻擊解密
僅 XOR: 簡易加密 - 總是可以復原
自訂演算法: 通常含有實作缺陷
步驟 2:分析金鑰產生與管理
逆向工程金鑰的產生與儲存方式:
勒索軟體中的金鑰管理模式:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 強(沒有金鑰無法復原):
- 使用 CryptGenRandom 為每個檔案產生 AES 金鑰
- AES 金鑰使用內嵌的 RSA 公鑰加密
- 加密後的金鑰附加到每個檔案或單獨儲存
- RSA 私鑰僅由攻擊者的 C2 伺服器持有
2. 弱(可能可以復原):
- AES 金鑰衍生自可預測的種子(時間戳記、PID)
- 所有檔案使用相同的 AES 金鑰(單一金鑰遭破解 = 完整復原)
- 金鑰在加密前傳輸至 C2(PCAP 中可能包含金鑰)
- 使用短重複金鑰進行 XOR(可暴力破解)
- PRNG 以 GetTickCount() 或 time() 作為種子(金鑰空間有限)
3. 缺陷實作:
- ECB 模式(保留明文模式)
- 初始化向量 (IV) 在檔案間重複使用
- 金鑰以明文形式儲存在記憶體中(可從記憶體傾印復原)
- 部分加密(僅加密前 N 個位元組)
步驟 3:檢查檔案加密常式
逆向工程檔案處理邏輯:
// 典型的勒索軟體檔案加密流程(Ghidra 反編譯的偽程式碼):
void encrypt_file(char *filepath) {
// 1. 檢查檔案副檔名是否在目標清單中
if (!is_target_extension(filepath)) return;
// 2. 為每個檔案產生 AES 金鑰(AES-256 為 32 位元組)
BYTE aes_key[32];
CryptGenRandom(hProv, 32, aes_key);
// 3. 產生隨機 IV(16 位元組)
BYTE iv[16];
CryptGenRandom(hProv, 16, iv);
// 4. 讀取檔案內容
HANDLE hFile = CreateFile(filepath, GENERIC_READ, ...);
BYTE *plaintext = read_entire_file(hFile);
// 5. 使用 AES-256-CBC 加密
aes_cbc_encrypt(plaintext, file_size, aes_key, iv);
// 6. 使用 RSA 公鑰加密 AES 金鑰
BYTE encrypted_key[256]; // RSA-2048 輸出
rsa_encrypt(aes_key, 32, rsa_pubkey, encrypted_key);
// 7. 寫入:encrypted_data + encrypted_key + IV 到檔案
write_file(filepath, encrypted_data, encrypted_key, iv);
// 8. 以勒索軟體的副檔名重新命名檔案
rename_file(filepath, strcat(filepath, ".locked"));
}
步驟 4:檢查加密弱點
測試實作中是否存在可被利用的缺陷:
from Crypto.Cipher import AES
import os
import struct
# 測試 1:檢查是否對多個檔案使用相同的金鑰
# 比較已知檔案的加密版本
def check_key_reuse(file1_enc, file2_enc):
with open(file1_enc, "rb") as f:
data1 = f.read()
with open(file2_enc, "rb") as f:
data2 = f.read()
# 提取 IV(位置取決於勒索軟體家族)
# 如果 IV 相同且檔案共享加密區塊 -> 使用相同金鑰
iv1 = data1[-16:] # 範例:IV 在結尾
iv2 = data2[-16:]
if iv1 == iv2:
print("[!] 偵測到相同的 IV - 可能重複使用金鑰")
# 測試 2:檢查是否使用可預測的金鑰衍生
# 如果金鑰衍生自時間戳記,則疊代可能的值
def brute_force_timestamp_key(encrypted_file, known_header, timestamp_range):
with open(encrypted_file, "rb") as f:
encrypted_data = f.read()
for ts in timestamp_range:
# 以勒索軟體相同的方式衍生金鑰
import hashlib
key = hashlib.sha256(str(ts).encode()).digest()
iv = encrypted_data[-16:]
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(encrypted_data[:16])
if decrypted[:len(known_header)] == known_header:
print(f"[!] 找到金鑰!時間戳記:{ts}")
return key
return None
# 測試 3:檢查是否為 ECB 模式(保留模式)
def check_ecb_mode(encrypted_file):
with open(encrypted_file, "rb") as f:
data = f.read()
# ECB 模式會為相同的明文區塊產生相同的密文區塊
blocks = [data[i:i+16] for i in range(0, len(data), 16)]
unique = len(set(blocks))
total = len(blocks)
if unique < total * 0.95:
print(f"[!] 可能為 ECB 模式:{total-unique} 個重複區塊(共 {total} 個)")
步驟 5:嘗試復原金鑰
利用已識別的弱點復原金鑰:
# 復原方法 1:從記憶體傾印中提取金鑰
# Volatility 外掛用於掃描 AES 金鑰排程
# vol3 -f memory.dmp windows.yarascan --yara-rule "aes_key_schedule"
# 復原方法 2:已知明文攻擊(弱演算法)
def xor_key_recovery(encrypted_file, known_plaintext):
"""從已知明文-密文配對中復原 XOR 金鑰"""
with open(encrypted_file, "rb") as f:
ciphertext = f.read()
key = bytes(c ^ p for c, p in zip(ciphertext, known_plaintext))
# 尋找重複金鑰的長度
for key_len in range(1, 256):
candidate = key[:key_len]
if all(key[i] == candidate[i % key_len] for i in range(min(len(key), key_len * 4))):
print(f"XOR 金鑰(長度 {key_len}):{candidate.hex()}")
return candidate
return None
# 復原方法 3:在 NoMoreRansom 上檢查現有的解密工具
# https://www.nomoreransom.org/en/decryption-tools.html
步驟 6:記錄加密分析結果
將分析結果整理成結構化報告:
分析應記錄以下項目:
- 識別出的演算法(AES、RSA、ChaCha20、自訂)
- 金鑰大小與作業模式(CBC、CTR、ECB、GCM)
- 金鑰產生方法(CSPRNG、可預測種子、靜態金鑰)
- 金鑰儲存位置(附加到檔案、登錄檔、C2 傳輸)
- 檔案修改模式(完整加密、部分加密、僅加密檔頭)
- 目標檔案副檔名
- 勒索訊息格式與支付基礎架構
- 解密可行性評估(可能/不可能/部分可能)
- 建議的復原方式
關鍵概念
| 術語 | 定義 |
|---|---|
| 混合加密 | 結合對稱式加密(AES)以快速加密檔案,以及非對稱式加密(RSA)以安全地包裝金鑰;這是勒索軟體的標準做法 |
| 金鑰包裝 | 使用攻擊者的 RSA 公鑰加密每個檔案的對稱式金鑰,使只有攻擊者的私鑰才能解密它 |
| ECB 模式 | 電子密碼本模式會獨立加密每個區塊;保留明文中的模式,這是一個可實現部分復原的重大弱點 |
| 已知明文攻擊 | 使用已知的原始檔案及其加密版本來推導加密金鑰;對 XOR 和弱串流加密有效 |
| 金鑰排程 | AES 金鑰在記憶體中的展開形式;可在記憶體傾印中掃描,以便在加密金鑰被刪除前將其復原 |
| CSPRNG | 密碼學安全偽隨機數產生器;使用 CryptGenRandom 的勒索軟體會產生不可預測的金鑰 |
| 部分加密 | 某些勒索軟體僅為加快速度而加密前 N 個位元組或每隔 N 個區塊;未加密的部分可能有助於復原 |
工具與系統
- Ghidra:逆向工程套件,用於在組合語言層級分析勒索軟體的加密常式
- PyCryptodome:Python 密碼學函式庫,用於實作和測試解密常式
- NoMoreRansom.org:由 Europol 和安全廠商維護的免費解密工具儲存庫,適用於已知的勒索軟體家族
- Volatility:記憶體鑑識框架,用於從受感染系統的 RAM 傾印中提取加密金鑰
- CryptoTester:用於根據常數和程式碼模式識別加密演算法的工具
常見情境
情境:評估勒索軟體事件中解密的可行性
背景:某組織遭受勒索軟體攻擊,檔案伺服器被加密。管理階層在做出復原決策前,需要知道是否可以在不支付贖金的情況下解密。
方法:
- 根據勒索訊息、檔案副檔名和樣本雜湊值識別勒索軟體家族(查詢 ID Ransomware)
- 在 NoMoreRansom.org 上檢查是否有適用於此家族的現有免費解密工具
- 在 Ghidra 中逆向工程加密常式,以識別演算法和金鑰管理方式
- 測試是否存在實作弱點(金鑰重複使用、可預測種子、ECB 模式)
- 檢查事件中的 PCAP 是否擷取到傳送至 C2 的金鑰(如果金鑰在加密前已傳送)
- 掃描受影響機器的記憶體傾印,尋找 RAM 中的 AES 金鑰排程
- 報告結果:具體說明解密可能/不可能,並提供技術依據
陷阱:
- 在加密檔案的唯一副本上測試解密方法(務必使用副本進行操作)
- 未經驗證就假設所有檔案使用相同的金鑰(某些勒索軟體會為每個檔案產生不同的金鑰)
- 未檢查磁碟區陰影複製(vssadmin),勒索軟體可能未能刪除它們
- 在報告中將檔案加密演算法與金鑰包裝演算法混淆
輸出格式
勒索軟體加密分析
================================
樣本: lockbit3.exe
家族: LockBit 3.0 / LockBit Black
SHA-256: abc123def456...
加密方案
檔案加密: AES-256-CTR(每個檔案使用唯一金鑰)
金鑰包裝: RSA-2048(公鑰內嵌於二進位檔中)
金鑰產生: CryptGenRandom (CSPRNG - 不可預測)
IV 產生: 每個檔案 16 位元組隨機值
檔案結構: [encrypted_data][rsa_encrypted_key(256B)][iv(16B)][magic(8B)]
目標副檔名
總計: 412 個目標副檔名
類別: 文件 (.doc、.xls、.pdf)、資料庫 (.sql、.mdb)、
封存檔 (.zip、.7z)、原始碼 (.py、.java、.cs)
排除項目: .exe、.dll、.sys、.lnk(保留系統檔案)
實作分析
金鑰強度: 強 - 每個檔案使用隨機金鑰,無重複使用
模式安全性: 強 - CTR 模式搭配唯一的 nonce
金鑰儲存: RSA 加密的金鑰附加到每個檔案
陰影複製: 透過 vssadmin 和 WMI 刪除
解密可行性
無金鑰情況下: 不可能
- 未識別出任何實作缺陷
- RSA-2048 金鑰包裝可防止暴力破解
- CSPRNG 可防止金鑰預測
- 無現有的免費解密工具
復原選項
1. 從離線備份還原(建議)
2. 檢查磁碟區陰影複製(機率低 - 勒索軟體會刪除它們)
3. 如果機器尚未重新開機,可進行記憶體鑑識(金鑰可能仍留在 RAM 中)
4. 與攻擊者協商(最後手段 - 不保證能解密)