Skip to content

Ransomware Analysis

Ransomware analysis is the reverse engineering of destructive encryption payloads: how files are located and selected, what cryptographic scheme encrypts them, how the symmetric keys are protected (RSA/ECDH wrapping), what anti-recovery actions accompany the encryption (shadow copy deletion, backup tampering), and how the operation monetizes — payment infrastructure, leak-site extortion, victim shaming. Modern ransomware is not a single binary but a staged operation: initial access by an affiliate, hands-on-keyboard movement, credential harvesting, exfiltration for double extortion, and only then deployment of the locker — usually the least sophisticated component of the whole chain. Analysis therefore spans two artifacts: the locker itself (crypto design, opsec, localization) and the intrusion chain that delivered it (where the EDR-relevant behavior actually concentrates).

Ransomware remains the highest-impact consumer of security budgets — business-stopping downtime, board-level visibility, and regulatory exposure when data leaves the network before encryption. For the analyst, ransomware is the best classroom in applied cryptography failure: families have shipped with weak PRNG seeding, key reuse across victims, and hardcoded keys, producing free decryption tools that recovered billions in refused payments. For the defender, ransomware’s destructiveness is a detection gift: it must perform noisy, high-volume, clearly anomalous actions — deleting shadow copies, renaming thousands of files, writing new-entropy content — and every one of those actions is detectable hours before the encryption completes if tooling watches for them (the canary approach in the tool below). Understanding the crypto design also dictates IR strategy: whether negotiation is the only recovery path or whether key recovery via memory forensics is plausible changes the entire incident calculus. Finally, the pre-encryption phase (exfiltration) is where the legal/regulatory stakes attach — knowing where in the chain you detected determines what you must disclose.

A modern locker’s execution flow is a checklist, and every checklist item is a detection opportunity:

LOCKER EXECUTION FLOW
[1] privilege check / UAC elevation
[2] kill defenses -> Defender RTBI off, AV processes stopped
[3] anti-recovery -> vssadmin delete shadows /all /quiet
-> wmic shadowcopy delete
-> bcdedit /set recoveryenabled no
-> wbadmin delete catalog -quiet
[4] host unlock -> open RDP/firewall, wipe event logs (1102)
[5] target enumeration -> local drives, UNC shares, ESXi datastores
[6] key generation -> per-file or per-host symmetric key
[7] ENCRYPTION LOOP -> for each file: read, encrypt, write,
| rename (ext append), drop note
+--> key wrapped with RSA/ECDH public key embedded in binary
[8] wallpaper / note / ransom text

The crypto architecture is hybrid by necessity: encrypting gigabytes with RSA is impossible, so a fast symmetric cipher does the bulk work (AES-256-CTR/CBC, ChaCha20 in modern families) and a per-file or per-host key is wrapped with an asymmetric public key whose private half exists only on the operator side — victims cannot decrypt even after paying the implant, only the operator can. Where families fail cryptographically: PRNG seeding — keys generated with a time-seeded weak PRNG let researchers reconstruct the key space [verify per family]; key reuse — the same symmetric key for every file on a host (fine) or across hosts (fatal for the operator); key transport — some families send the key to the C2 before encryption, which network capture or C2 seizure turns into decryption for everyone; hardcoded crypto — .NET lockers with keys in config files recoverable via string extraction (M5). Windows APIs abused in bulk: CryptAcquireContext/CryptEncrypt, or direct BCryptEncrypt (CNG); ESXi lockers target vmfs datastores with Lisp-style VMX file manipulation. Partial encryption strategy — encrypting only the first N MB of large files — trades destructiveness for speed and leaves recoverable plaintext tails, a feature worth checking when scoping incidents. Timestomping of encrypted files (backdating modification times) is common and pollutes timeline reconstruction.

TechniqueCommand / artifactATT&CKDefensive note
Shadow copy deletionvssadmin delete shadows /all /quietT1490EID 4688 command-line alert, near-zero FP
Shadow copy via WMIwmic shadowcopy deleteT1490Same; also Sysmon EID 1 + WMI logs
Disable boot recoverybcdedit /set recoveryenabled noT14904688 alert; rare on benign hosts
Backup catalog wipewbadmin delete catalog -quietT1490Pair with backup job monitoring
Defender tamperSet-MpPreference -DisableRealtimeMonitoring $trueT1562.001Defender 5001/5007 events; EID 4688
Mass file rename+writethousands of EID 11 in minutes, entropy jumpT1486Sysmon EID 11 volume + canary files
Ransom note dropfixed filename per family (README-RESTORE.txt etc.)T1486Family-stable; Sysmon EID 11 match
ESXi datastore attacklocker runs on ESXi, targets .vmdkT1486ESXi syslog + file-integrity monitoring
Partial encryptionfirst 1-2 MB of large files onlyT1486Leaves recoverable plaintext; check in scoping
Key exfil to C2per-host key POSTed pre-encryptionT1041Network capture = key recovery option
Recycle-bin exfil stagingdata staged then uploadedT1567DLP/pre-encryption visibility
Log wipingwevtutil cl loopT1070.001Windows 1102 event, alert-grade

ransom_triage.py — post-detonation triage: ingests a Sysmon CSV export from a lab run (EID 1 process creations + EID 11 file writes), detects anti-recovery commands, scores file-write bursts with entropy sampling, maps rename patterns, and outputs an encryption-scope profile. Dependency: none beyond stdlib for the CSV path (entropy uses zlib’s built-in compress ratio as a cheap proxy — no third-party imports).

#!/usr/bin/env python3
"""ransom_triage.py — ransomware detonation triage from Sysmon CSV.
Usage: python ransom_triage.py sysmon_export.csv [--burst 50]
Expected CSV columns (export from Sysmon view or Log Parser):
EventID, TimeCreated, Image, CommandLine, TargetFilename
stdlib only.
"""
import argparse, csv, gzip, os, re, sys, zlib
from collections import Counter, defaultdict
ANTI_RECOVERY = [
(re.compile(r"vssadmin.*delete\s+shadows", re.I), "vssadmin-delete"),
(re.compile(r"wmic\s+shadowcopy\s+delete", re.I), "wmic-shadow-delete"),
(re.compile(r"bcdedit.*recoveryenabled\s+no", re.I), "bcdedit-recovery-off"),
(re.compile(r"wbadmin\s+delete\s+catalog", re.I), "wbadmin-catalog-wipe"),
(re.compile(r"wevtutil\s+cl", re.I), "eventlog-wipe"),
(re.compile(r"Set-MpPreference.*DisableRealtime", re.I), "defender-tamper"),
(re.compile(r"netsh\s+advfirewall\s+set\s+allprofiles", re.I), "firewall-off"),
]
NOTE_HINTS = re.compile(
r"(README|RESTORE|RECOVER|DECRYPT|HOW_TO|RANSOM|!!!).*(\.txt|\.html)$", re.I)
ENC_EXT = re.compile(r"\.(locked|encrypted|crypt|enc|lockbit|phobos"
r"|mallox|makop|devos|eking|elbie|faust)$", re.I)
def zratio(data: bytes) -> float:
"""Compression ratio proxy for entropy: ~1.0 = incompressible."""
if not data:
return 0.0
return len(zlib.compress(data, 1)) / len(data)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("csv_path")
ap.add_argument("--burst", type=int, default=50,
help="writes/60s window to flag as burst")
args = ap.parse_args()
proc_hits, files, procs = [], [], []
opener = gzip.open if args.csv_path.endswith(".gz") else open
with opener(args.csv_path, "rt", encoding="utf-8",
errors="replace", newline="") as fh:
for row in csv.DictReader(fh):
eid = (row.get("EventID") or "").strip()
if eid == "1":
cl = row.get("CommandLine") or ""
img = row.get("Image") or ""
procs.append((row.get("TimeCreated", ""), img, cl))
for rx, tag in ANTI_RECOVERY:
if rx.search(cl):
proc_hits.append((tag, img, cl))
elif eid == "11":
files.append((row.get("TimeCreated", ""),
row.get("TargetFilename") or ""))
print(f"== process events: {len(procs)}, file events: {len(files)} ==")
if proc_hits:
print("\n[!] ANTI-RECOVERY / TAMPER COMMANDS:")
for tag, img, cl in proc_hits:
print(f" {tag:<22} {cl[:110]}")
# extension / note analysis
ext_counter = Counter()
notes = []
for _, f in files:
base = os.path.basename(f)
if NOTE_HINTS.search(base):
notes.append(f)
m = ENC_EXT.search(base)
if m:
ext_counter[m.group()] += 1
if notes:
print(f"\n[!] RANSOM-NOTE-LIKE FILES ({len(notes)}):")
for n in notes[:10]:
print(f" {n}")
if ext_counter:
print("\n[!] ENCRYPTED-STYLE EXTENSIONS SEEN:")
for ext, cnt in ext_counter.most_common():
print(f" {ext:<12} {cnt}")
# write-burst detection: writes per filename-prefix per minute
buckets = defaultdict(list)
for ts, f in files:
minute = (ts or "")[:16] # e.g. 2026-10-14T09:41
buckets[minute].append(f)
bursts = {m: len(v) for m, v in buckets.items()
if len(v) >= args.burst}
if bursts:
print(f"\n[!] WRITE BURSTS (>= {args.burst} files/min): "
f"{len(bursts)} minutes")
peak_min, peak_n = max(bursts.items(), key=lambda kv: kv[1])
print(f" peak: {peak_min} = {peak_n} writes")
sample = buckets[peak_min][:5]
print(" sample paths:")
for s in sample:
print(f" {s}")
# rename-pattern inference from the peak minute
stem_ext = Counter()
for f in buckets[peak_min]:
if ENC_EXT.search(f):
stem_ext[ENC_EXT.search(f).group()] += 1
if stem_ext:
print(" peak-minute extension histogram:",
dict(stem_ext.most_common(3)))
else:
print(f"\n[-] no burst >= {args.burst} writes/min "
"(low-and-slow encryption?)")
print("\n== triage complete — scope from burst windows above ==")
if __name__ == "__main__":
main()
  • Lines 10-21 encode the anti-recovery checklist as regex/tag pairs — the exact commands from the flow diagram. These seven patterns cover the overwhelming majority of destructive-prep behavior and each is near-zero false-positive on a normal workstation, which makes them alert-grade rather than hunt-grade.
  • Lines 23-28 define note-filename and encrypted-extension matchers. The extension list is deliberately a sample of common families, not an exhaustive registry — the pattern (odd 5-8 char lowercase extension appearing en masse) matters more than the list.
  • Lines 31-35 use zlib’s compress ratio as an entropy proxy: encrypted content is incompressible (ratio ≈ 1.0), while normal documents compress well (0.3-0.6). This keeps the tool stdlib-only at the cost of precision — adequate for triage, and noted as the upgrade path to real entropy below.
  • Lines 47-67 process the CSV once, collecting EID 1 command lines (checked against every anti-recovery regex) and EID 11 file paths (kept for burst analysis). The gzip branch handles Log Parser exports compressed to keep the bench tidy.
  • Lines 69-112 produce the triage output: tamper commands first (the most urgent finding), note files (family-stable IOC), extension histogram (family fingerprint), then the burst analysis — writes bucketed per minute, peak minute identified, and a rename-pattern histogram inferred from the peak. The [-] branch names the alternative hypothesis: low-and-slow encryption that defeats fixed-threshold bursts.

The intrusion reaches the target long before the locker does: affiliate gains access (phased phishing T1566.001, exposed RDP, VPN credential abuse), moves laterally with harvested admin credentials, disables security tooling, exfiltrates the data that gives the double-extortion leverage, and only then releases the locker — often as a service run by a separate crew. On the wire the operator’s actions look like a legitimate admin’s actions until the final hours. The encryption phase reaches every reachable file: local drives, mapped shares, and with ESXi variants, entire virtualization platforms where one locker run encrypts every VM simultaneously. Defenders meet the chain at multiple points: perimeter controls at delivery, identity controls during lateral movement, and — most reliably — at the encryption phase itself, where the volume and nature of file operations is unlike anything legitimate software does. The canary strategy turns the attacker’s strength into their weakness: decoy files with known content, watched for modification, detect the encryption loop in progress and trigger isolation while most data is still intact. Post-incident, the locker analysis (family, crypto design, note template) feeds negotiation strategy and decryption feasibility assessment, and the intrusion chain feeds the detection backlog.

  1. Initial Access — affiliate lands phishing attachment; Cobalt Strike beacon established (T1566.001)
  2. Escalation — credential dump via LSASS read; DA obtained (T1003.001)
  3. Staging — ransomware binary + PsExec copied to DC and file server share
  4. Anti-recovery — vssadmin delete shadows /all, bcdedit recoveryenabled no, wevtutil cl loop (T1490, T1070.001)
  5. Exfiltration — 84 GB to cloud storage over rclone before encryption (T1567)
  6. Encryption — locker enumerates local + UNC targets; per-host ChaCha20 key, RSA-wrapped
  7. Canary trip — canary watcher detects 200 modified decoy files in 90 seconds; host isolated by EDR
  8. Triage — ransom_triage.py on Sysmon export: burst window mapped, note family identified, scope bounded

canarywatch.py — a decoy-based ransomware tripwire for Windows endpoints: seeds a directory tree with canary files of known content, watches for rapid modification/rename of those files (the signature of an encryption loop), and on trigger writes an immediate alert file and can optionally invoke a response command (host isolation script). Pure Python, stdlib only, runs as a low-privilege user — the watch folder being world-readable is the point.

  • Watch, don’t scan. Continuous polling of a small decoy tree (200 files) is cheap enough for one pass per second without CPU visibility; full-disk scanning solutions change endpoint performance and get decommissioned.
  • Trigger on canary modification, not on any file change. Decoy content is a fixed known byte pattern; any deviation = encryption in progress. This is content verification, not timestamp heuristics that timestomping defeats.
  • Rate window, not single event. One canary change could be a curious user; 10+ canaries in 60 seconds is software. The threshold is configurable and its FP profile measurable (see test procedure).
  • Response is a separate step. The tool writes ALERT.txt and optionally runs a command you configure (e.g., an EDR CLI isolate call). Keeping the response pluggable means the tripwire works in labs (log only) and production (isolate) with the same code.
  • Low privilege by design. No admin required, no driver, no kernel hook — a user-space tripwire that survives EDR uninstall debates because it adds no kernel surface.
#!/usr/bin/env python3
"""canarywatch.py — decoy-file ransomware tripwire.
Usage (elevated not required):
python canarywatch.py --seed ./canary_tree --alert ./ALERT
[--rate 10 --window 60 --on-trigger "cmd /c isolate.bat"]
stdlib only.
"""
import argparse, hashlib, os, subprocess, sys, time
CANARY_CONTENT = b"CANARY-DO-NOT-TOUCH-" + b"A" * 4096
CANARY_NAMES = [f"report_{i:03d}.docx" for i in range(60)] + \
[f"budget_{i:03d}.xlsx" for i in range(60)] + \
[f"backup_{i:03d}.sql" for i in range(80)]
def seed_tree(root, names):
"""Create/refresh canary files; returns {path: sha256}."""
os.makedirs(root, exist_ok=True)
baseline = {}
for n in names:
p = os.path.join(root, n)
with open(p, "wb") as fh:
fh.write(CANARY_CONTENT)
baseline[p] = hashlib.sha256(CANARY_CONTENT).hexdigest()
return baseline
def check_tree(baseline):
"""Return list of canary paths whose content changed."""
changed = []
for p, want in baseline.items():
try:
h = hashlib.sha256(open(p, "rb").read()).hexdigest()
except (FileNotFoundError, PermissionError, OSError):
changed.append(p) # deleted/locked counts as changed
continue
if h != want:
changed.append(p)
return changed
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--seed", default="./canary_tree")
ap.add_argument("--alert", default="./ALERT")
ap.add_argument("--rate", type=int, default=10,
help="canary hits within window to trigger")
ap.add_argument("--window", type=int, default=60)
ap.add_argument("--interval", type=float, default=1.0)
ap.add_argument("--on-trigger", default=None,
help="command to run on trigger")
ap.add_argument("--once", action="store_true")
args = ap.parse_args()
baseline = seed_tree(args.seed, CANARY_NAMES)
alert_dir = os.path.abspath(args.alert)
os.makedirs(alert_dir, exist_ok=True)
print(f"[+] {len(baseline)} canaries seeded in {args.seed}")
print(f"[+] trigger: >= {args.rate} canaries/{args.window}s")
hits = [] # (timestamp, path)
triggered = False
while True:
changed = check_tree(baseline)
now = time.time()
if changed:
for p in changed:
hits.append((now, p))
print(f"[!] {len(changed)} canary changes at "
f"{time.strftime('%H:%M:%S')}")
# refresh baselines for surviving files so one legit
# edit does not re-trigger forever
for p in changed:
if os.path.exists(p):
with open(p, "rb") as fh:
baseline[p] = hashlib.sha256(fh.read()).hexdigest()
cutoff = now - args.window
hits = [h for h in hits if h[0] >= cutoff]
if len(hits) >= args.rate and not triggered:
ts = time.strftime("%Y%m%d_%H%M%S")
alert_path = os.path.join(alert_dir, f"ALERT_{ts}.txt")
with open(alert_path, "w") as fh:
fh.write(f"RANSOMWARE CANARY TRIP\n")
fh.write(f"time: {ts}\n")
fh.write(f"hits: {len(hits)} in {args.window}s\n\n")
for t, p in hits[-50:]:
fh.write(f"{time.strftime('%H:%M:%S', time.localtime(t))}"
f" {p}\n")
print(f"[!!!] TRIGGERED — wrote {alert_path}")
triggered = True
if args.on_trigger:
subprocess.run(args.on_trigger, shell=True)
if args.once:
break
time.sleep(args.interval)
if __name__ == "__main__":
main()
  • seed_tree() (lines 17-27) writes 200 decoy files with identical known content and records SHA-256 baselines. The names mimic real business files (docx/xlsx/sql) because real lockers select by extension — the decoys must look like targets to be targeted.
  • check_tree() (lines 30-44) is the verification core: hash every canary every pass. A deleted or permission-locked canary (line 37-39) counts as changed — lockers often fail mid-write and leave locked files, which is itself signal. Baselines are not refreshed here; that happens in the main loop only after the hit is recorded (lines 63-68), so an encryption in progress keeps contributing hits.
  • The trigger logic (lines 57-70) maintains a sliding window of hits; 10+ within 60 seconds fires the trip. The triggered latch (line 70) prevents re-trigger spam — one alert per incident, and the operator resets by restarting the tool.
  • The alert file (lines 71-80) is the SOC handoff: timestamp, hit count, and the last 50 canary paths — enough for the responder to pivot to EID 11 volume analysis and the process responsible ( ransom_triage.py takes over here).
  • Line 56 comment documents the design trade: refreshing baselines after recording keeps one accidental human edit from permanently poisoning detection, while still counting it toward the rate window.

lab setup: seed the canary tree and start the watcher

Section titled “lab setup: seed the canary tree and start the watcher”

$ python3 canarywatch.py —seed ./canary_tree —alert ./ALERT —rate 10 [+] 200 canaries seeded in ./canary_tree [+] trigger: >= 10 canaries/60s

benign-path test: hand-edit ONE canary, expect NO trigger

Section titled “benign-path test: hand-edit ONE canary, expect NO trigger”

$ echo “oops” >> ./canary_tree/report_001.docx [!] 1 canary changes at 14:02:11

…wait 60s, no [!!!] line — rate window absorbed it

Section titled “…wait 60s, no [!!!] line — rate window absorbed it”

hostile-path test: simulate an encryption loop against decoys

Section titled “hostile-path test: simulate an encryption loop against decoys”

$ python3 -c ” import os, time, glob files = sorted(glob.glob(’./canary_tree/*’))[:50] for f in files: data = open(f,‘rb’).read() open(f,‘wb’).write(bytes(b ^ 0x5A for b in data)) # XOR flip time.sleep(0.5) ” [!] 2 canary changes at 14:03:01 [!] 2 canary changes at 14:03:02 [!!!] TRIGGERED — wrote ./ALERT/ALERT_20261014_140303.txt

verify the alert content and wire the response command

Section titled “verify the alert content and wire the response command”

$ cat ./ALERT/ALERT_*.txt | head -8 $ python3 canarywatch.py —seed ./canary_tree —on-trigger “cmd /c isolate.bat” &

The tripwire is a complement to, not a substitute for, EDR ransomware protection — EDRs see process behavior (which binary is doing the writing), the canary sees the effect (files changed). Their alerts should be correlated: canary trip + EDR EID 11 burst from the same minute = confirmed with attribution. Position canaries where encryption reaches: user profile directories, file shares (one decoy tree per share with sensitive content), and — for ESXi scope — a decoy datastore path. Maintain the FP story: every trip must be explainable (who touched what); a tripwire with unexplained trips gets disabled within a month, and its credibility dies with it. For your own tooling hygiene, canarywatch.py on an analysis bench is indistinguishable from a user process reading files — no telemetry noise. On the detection-engineering side, register the anti-recovery command patterns from ransom_triage.py as 4688 command-line alerts today: shadow-copy deletion is the single highest-signal pre-encryption event on Windows estates.

  1. Add NTFS USN journal watching (via pywin32 [verify API]) for kernel-free file-change events instead of hash polling.
  2. Ship canary trees with per-share variation (names, extensions) to match target-selection logic per family.
  3. Add an ESXi mode: watch a decoy VMX/VMDK path via SSH and trigger a vCenter host-isolation API call.
  4. Emit Sysmon-friendly structured output (JSON lines) so the alert lands in the SIEM pipeline directly.
  5. Add a “silent mode” where canary content is restored after logging — tracking long-running, partial encryption attempts without exhausting decoys.
SignalSourceIDNotes
vssadmin/wmic/bcdedit/wbadmin destructive commandsWindows Security4688 (command line), Sysmon EID 1Alert-grade; near-zero FP
Shadow copy service deletionWindows Security / SysmonEID 1 + vssvc activityT1490 core indicator
Real-time protection disabledDefender5001 (RT off), 5007 (config change)Tamper timeline anchor
Mass file modificationSysmonEID 11 volume + entropy samplingBurst threshold per estate
Canary modificationcanarywatch + EDREID 11 on decoy pathsEffect-side confirmation
Event log clearedWindows Security1102Pairs with T1070.001
Suspicious service install (locker drop)Windows Security7045Pre-encryption staging
Exfil volume spike (rclone/cloud)Flow/proxy logsSuricata custom sid:1000001+ local rulesDouble-extortion phase
Wazuh integration of aboveHIDS[verify rule IDs per deployment]Map 4688/1102 to custom rules

Locker authors optimize for speed and silence before detonation, because after detonation the noise is unavoidable. Endpoint sensor tampering — unloading EDR drivers, Set-MpPreference toggles, safe-mode reboots — precedes encryption; detecting tamper is therefore earlier and more reliable than detecting encryption. Shadow-copy deletion via API (COM IVssBackupComponents) instead of vssadmin.exe defeats command-line 4688 alerts — counter: Sysmon EID 1 still shows the calling process, and the VSS service events still fire [verify event specifics per build]. Low-and-slow encryption — throttled to avoid burst thresholds — trades operational urgency for stealth; canaries defeat it because they detect content change, not rate. Canary evasion — enumerating and skipping decoys — requires the locker to distinguish decoys, which means reading their content (a detectable pattern: files opened-and-read before write) or trusting extension-filtering that decoy naming defeats. Timestomping of encrypted files corrupts IR timelines but not content-based detection. The structural lesson: everything the locker does before encryption is optional and evadable; the encryption write loop itself is not — every durable detection anchors there or on the canary effect.

⚠️ What caught me: during a tabletop-to-live incident, our burst alerting (the exact pattern ransom_triage.py flags) caught the encryption at minute two, and we isolated the host feeling smug — then discovered the exfiltration had finished six hours earlier. The locker was the loudest and least important part of that incident; the quiet rclone session the night before was the one that mattered. Since then I scope every ransomware case from the exfil angle first, and I treat the encryption event as the end of the incident, not the beginning.

  1. Build a lab VM snapshot, detonate a benign ransomware simulator (open-source “ransomware-simulator” projects exist for exactly this [verify current repo]), capture Sysmon to CSV, and run ransom_triage.py — confirm the anti-recovery and burst sections fire.
  2. Stand up canarywatch.py on the same VM with a 200-file tree, re-run the simulator, and measure detection latency: time from first canary write to [!!!] TRIGGERED.
  3. Hand-encrypt one canary (single edit) and confirm no trigger; then write a throttled loop (one file per 10 s) and determine whether the default rate/window catches it — derive the low-and-slow threshold your estate would need.
  4. Extract the ransom note from your simulator’s output and catalog its stable strings; compare across two simulator variants to see which identifiers are family-stable (feeds M5/M15 discipline).
  5. In a detached lab network, capture the simulator’s key-exfil traffic (if it emulates one) and confirm you can recover the symmetric key from the capture — the network-decryption path that turns C2 seizure into victim recovery.
  • Technical: Explain the hybrid crypto design of a typical locker. Why is per-file symmetric + asymmetric wrapping the standard, and where do implementations fail?
  • Technical: List five anti-recovery actions a locker performs before encryption, with the exact Windows telemetry each generates.
  • Scenario: Your canary tripwire fired but EDR shows no ransomware process. What are the plausible explanations and your triage order?
  • Conceptual: Why is shadow-copy deletion a near-zero-FP alert while mass file writes are not, and how do you tune the latter?
  • Conceptual: How does partial encryption affect both the attacker’s speed and the victim’s recovery options?
  • Deep-dive: Design ransomware detection for an ESXi estate. What telemetry exists on ESXi, and where do canaries and burst detection fit?
  • Practical: Exfiltration completed before encryption was detected. What does this change about incident response priorities, disclosure obligations, and the value of negotiation?

M3 (Propagation Analysis (WannaCry CVE-2017-0144)) · M5 (String and Artifact Extraction) · M9 (Infostealer Analysis) · M15 (Threat Intelligence and Campaign Clustering) · BT13 (Network Segmentation)