分析供應鏈惡意軟體工件
概述
供應鏈攻擊會危害合法的軟體發佈管道,以透過受信任的更新機制傳遞惡意軟體。著名的例子包括 SolarWinds SUNBURST(2020 年,影響超過 18,000 名客戶)、3CX SmoothOperator(2023 年,源自 Trading Technologies 的連鎖供應鏈攻擊),以及大量的 npm/PyPI 套件污染活動。分析工作包括將被木馬化的二進位檔案與合法版本進行比較、識別建置工件中的注入程式碼、檢查程式碼簽章異常,以及追溯從初始入侵到 payload 傳遞的完整感染鏈。截至 2025 年,供應鏈攻擊佔所有資料外洩事件的 30%,比前幾年增加了 100%。
使用時機
- 在調查需要分析供應鏈惡意軟體工件之安全事件時。
- 在建置此領域的偵測規則或威脅狩獵查詢時。
- 當 SOC 分析師需要此類分析的標準化程序時。
- 在驗證相關攻擊技術的資安監控覆蓋範圍時。
先決條件
- Python 3.9+ 以及
pefile、ssdeep、hashlib套件。 - 二進位比對工具(BinDiff、Diaphora)。
- 程式碼簽章驗證工具(sigcheck、codesign)。
- 軟體組成分析 (SCA) 工具。
- 能夠存取合法軟體版本以進行比較。
- 套件儲存庫監控(npm、PyPI、NuGet)。
工作流程
步驟 1:二進位比較分析
#!/usr/bin/env python3
"""Compare trojanized binary against legitimate version."""
import hashlib
import pefile
import sys
import json
def compare_pe_files(legitimate_path, suspect_path):
"""Compare PE file structures between legitimate and suspect versions."""
legit_pe = pefile.PE(legitimate_path)
suspect_pe = pefile.PE(suspect_path)
report = {"differences": [], "suspicious_sections": [], "import_changes": []}
# Compare sections
legit_sections = {s.Name.rstrip(b'\x00').decode(): {
"size": s.SizeOfRawData,
"entropy": s.get_entropy(),
"characteristics": s.Characteristics,
} for s in legit_pe.sections}
suspect_sections = {s.Name.rstrip(b'\x00').decode(): {
"size": s.SizeOfRawData,
"entropy": s.get_entropy(),
"characteristics": s.Characteristics,
} for s in suspect_pe.sections}
# Find new or modified sections
for name, props in suspect_sections.items():
if name not in legit_sections:
report["suspicious_sections"].append({
"name": name, "reason": "New section not in legitimate version",
"size": props["size"], "entropy": round(props["entropy"], 2),
})
elif abs(props["size"] - legit_sections[name]["size"]) > 1024:
report["suspicious_sections"].append({
"name": name, "reason": "Section size significantly changed",
"legit_size": legit_sections[name]["size"],
"suspect_size": props["size"],
})
# Compare imports
legit_imports = set()
if hasattr(legit_pe, 'DIRECTORY_ENTRY_IMPORT'):
for entry in legit_pe.DIRECTORY_ENTRY_IMPORT:
for imp in entry.imports:
if imp.name:
legit_imports.add(f"{entry.dll.decode()}!{imp.name.decode()}")
suspect_imports = set()
if hasattr(suspect_pe, 'DIRECTORY_ENTRY_IMPORT'):
for entry in suspect_pe.DIRECTORY_ENTRY_IMPORT:
for imp in entry.imports:
if imp.name:
suspect_imports.add(f"{entry.dll.decode()}!{imp.name.decode()}")
new_imports = suspect_imports - legit_imports
if new_imports:
report["import_changes"] = list(new_imports)
# Check code signing
report["legit_signed"] = bool(legit_pe.OPTIONAL_HEADER.DATA_DIRECTORY[4].Size)
report["suspect_signed"] = bool(suspect_pe.OPTIONAL_HEADER.DATA_DIRECTORY[4].Size)
return report
def hash_file(filepath):
"""Calculate multiple hashes for a file."""
hashes = {}
with open(filepath, 'rb') as f:
data = f.read()
for algo in ['md5', 'sha1', 'sha256']:
h = hashlib.new(algo)
h.update(data)
hashes[algo] = h.hexdigest()
return hashes
if __name__ == "__main__":
if len(sys.argv) < 3:
print(f"Usage: {sys.argv[0]} <legitimate_binary> <suspect_binary>")
sys.exit(1)
report = compare_pe_files(sys.argv[1], sys.argv[2])
print(json.dumps(report, indent=2))
驗證標準
- 透過二進位比對識別出被木馬化的元件。
- 注入的程式碼已被隔離並單獨分析。
- 程式碼簽章異常已記錄。
- 已根據建置工件重建感染時間線。
- 已評估受影響系統的下游影響範圍。
- 已萃取出 IOCs 以用於偵測和封鎖。