Ransomware succeeds because it turns everyday system actions—reading files, writing files, renaming files, and running processes—into rapid disruption. Python is a practical language for learning those mechanics in a controlled lab, because it can generate repeatable file activity and help you analyze the logs that defenders rely on.
This article walks through a safe ransomware-style training lab, a Python “impact simulator” that produces realistic signals without encrypting anything, and a Python detection workflow that helps you spot mass file changes early.
What ransomware does in real incidents
Most ransomware incidents follow a pattern that looks simple on paper but moves fast in practice:
- An attacker gets in (phishing, stolen credentials, exposed remote access, or a vulnerable service).
- They expand access (privilege escalation, credential theft, lateral movement).
- They prepare the environment (discovery, disabling protections, targeting backups).
- They trigger impact (mass file modification, data encryption, service disruption).
- They pressure the victim (extortion note, deadlines, threats).
Encryption is only one part of that chain. Defensive training improves when you measure what your tools see at each step, especially the signals that show up right before impact.
What you will build in this lab

This guide stays focused on outcomes a security team can use right away. You will build:
- A safe impact simulator in Python that creates high-rate file writes and renames inside a dedicated folder.
- A local activity log so you can validate what happened and when.
- A Python detection script that flags burst file activity and extension churn from exported telemetry.
- A repeatable test plan that helps you tune alerts and reduce false positives.
- A response drill that matches how incident responders triage ransomware-style events.
Everything runs in a lab directory you control. The simulator does not encrypt files and does not touch anything outside your target path.
Set up a safe environment
Use isolation you can reset
A ransomware training lab needs two features: separation and easy rollback.
- Run inside a VM when possible.
- Use a host-only or NAT network, not a bridged connection.
- Take a snapshot before each test run.
- Keep test data separate from your real documents.
Create a dedicated target folder
Pick a single folder and treat it as the only writable target.
Windows example
C:\Lab\RansomSim\Targets\
Linux example
/home/lab/ransom_sim/targets/
Keep the lab folder empty at first. Create it fresh for each run so your results stay clear.
Turn on logging that captures ransomware-style signals
Ransomware detection depends on correlating file operations with the process and user that triggered them. Your telemetry options depend on your stack, but these sources are common:
- Endpoint telemetry (EDR)
- Windows Event Logs
- Sysmon (Windows)
- File integrity monitoring
- SIEM ingestion of endpoint logs
Sysmon basics (Windows)
Sysmon is popular in labs because it can record process creation and file create events with useful detail. If you already have EDR, keep it enabled. The goal is to see what it catches and what it misses.
A practical baseline includes:
- Process creation events (process name, parent, command line, user)
- File create/write events for the lab directory
- Optional: network connections for the lab host
Export logs for analysis
You need a simple export format so Python can parse it.
Common options:
- Export to CSV from your SIEM query results
- Export Sysmon or Security logs to EVTX, then convert to CSV/JSON
- Use EDR export functions (vendor-dependent)
Keep a standard naming pattern like:
logs/run_2026-02-23_01.csvlogs/run_2026-02-23_01.json
That makes comparisons easy.
Understand the signals you should expect
Ransomware-style impact produces patterns that stand out:
- One process touches many files in a short window.
- File activity shifts from “normal” to “sustained burst.”
- Many files get new extensions or new names.
- Changes spread across directories quickly.
A strong training approach sets a baseline first. Run a few normal workloads in the lab folder (copy files, unzip a large archive, install a program) and record the file event rate. That baseline helps you avoid alerts that fire on routine admin work.
Build a safe “impact simulator” in Python
The simulator should generate the same shape of activity that ransomware produces, while staying harmless.
Design goals
The simulator should:
- Operate only inside your lab target directory.
- Create test files that contain harmless content.
- Modify and rename those files quickly to simulate high churn.
- Produce a local activity log for validation.
- Support speed controls (slow vs fast) so you can tune thresholds.
Safety rules to enforce
Add guardrails so the simulator refuses to run outside your lab directory:
- Require an absolute path.
- Require the path to include a lab marker like
RansomSim. - Refuse to run if the folder is too high-level (like
C:\or/home). - Refuse to follow symlinks if you are on Linux.
These checks matter in training. Lab mistakes happen.
Safe simulator code
Paste the following into a file like ransom_sim.py. This code creates dummy files and simulates mass modification through copy-and-rename operations. It does not encrypt anything.
import os
import json
import time
import random
import string
from pathlib import Path
LAB_MARKER = "RansomSim"
def _rand_suffix(n=6) -> str:
return "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(n))
def safety_check(target_dir: Path) -> None:
# Require absolute path
if not target_dir.is_absolute():
raise ValueError("Target path must be absolute.")
# Require a lab marker in the path
if LAB_MARKER.lower() not in str(target_dir).lower():
raise ValueError("Target path must include the lab marker 'RansomSim'.")
# Refuse very high-level directories
forbidden = {Path("/"), Path.home(), Path("C:\\"), Path("C:/")}
for f in forbidden:
if str(target_dir).lower() == str(f).lower():
raise ValueError("Target path is too broad. Use a dedicated lab folder.")
def seed_files(target_dir: Path, count: int) -> list[Path]:
target_dir.mkdir(parents=True, exist_ok=True)
files = []
for i in range(count):
p = target_dir / f"doc_{i:05d}.txt"
p.write_text(f"LAB FILE {i}\nThis is safe test content.\n", encoding="utf-8")
files.append(p)
return files
def simulate_activity(files: list[Path], run_id: str, out_log: Path, ops_per_sec: int) -> None:
out_log.parent.mkdir(parents=True, exist_ok=True)
events = []
delay = 1.0 / max(1, ops_per_sec)
note = files[0].parent / "RECOVERY_TEST_NOTE.txt"
note.write_text(
f"This is a lab simulation run.\nRun ID: {run_id}\nTimestamp: {time.time()}\n",
encoding="utf-8"
)
events.append({"ts": time.time(), "op": "write_note", "path": str(note)})
for p in files:
# Read current content (safe)
content = p.read_text(encoding="utf-8")
# Write a modified copy
copy_path = p.with_suffix(p.suffix + f".{_rand_suffix()}.copy")
copy_path.write_text(content + f"\nModified in run {run_id}\n", encoding="utf-8")
events.append({"ts": time.time(), "op": "write_copy", "path": str(copy_path)})
# Rename the original to simulate extension churn (still safe)
renamed = p.with_suffix(p.suffix + ".test")
p.rename(renamed)
events.append({"ts": time.time(), "op": "rename", "path": str(renamed)})
time.sleep(delay)
out_log.write_text(json.dumps({"run_id": run_id, "events": events}, indent=2), encoding="utf-8")
def main():
import argparse
parser = argparse.ArgumentParser(description="Safe ransomware-style impact simulator (lab only).")
parser.add_argument("--target", required=True, help="Absolute path to lab target directory")
parser.add_argument("--files", type=int, default=2000, help="Number of files to seed")
parser.add_argument("--ops-per-sec", type=int, default=100, help="Operations per second")
parser.add_argument("--run-id", default=f"run_{int(time.time())}", help="Run identifier")
parser.add_argument("--log", default="logs/sim_log.json", help="Path to local JSON log output")
args = parser.parse_args()
target_dir = Path(args.target)
safety_check(target_dir)
files = seed_files(target_dir, args.files)
simulate_activity(files, args.run_id, Path(args.log), args.ops_per_sec)
print(f"Done. Run ID: {args.run_id}")
print(f"Log written to: {args.log}")
if __name__ == "__main__":
main()
Run examples
Windows
python ransom_sim.py --target "C:\Lab\RansomSim\Targets" --files 3000 --ops-per-sec 150 --log "logs\run_01.json"
Linux
python3 ransom_sim.py --target "/home/lab/RansomSim/Targets" --files 3000 --ops-per-sec 150 --log "logs/run_01.json"
Run it in three modes to see how your alerts behave:
- Slow:
--ops-per-sec 10 - Medium:
--ops-per-sec 100 - Fast:
--ops-per-sec 300
Build a Python detection script for burst file activity
The simulator creates two useful data streams:
- Your endpoint telemetry (Sysmon/EDR/SIEM)
- The local simulator log (ground truth)
A detection workflow should look at endpoint telemetry first, then use the local log to confirm what happened.
Detection heuristics that work well in training
Keep rules explainable and adjustable:
- Burst threshold: one process causes more than X file events in Y seconds.
- Extension churn: many files gain a new suffix within a short window.
- Directory spread: many subdirectories get touched rapidly.
Example: analyze exported CSV telemetry
The format of your CSV depends on your tool, so the script below expects a simple schema you can map to:
timestamp(epoch or ISO time)process_nameprocess_iduserfile_pathaction(create/write/rename)
If your export uses different headers, rename columns at export time or adjust the script.
import csv
import datetime as dt
from collections import defaultdict
from pathlib import Path
def parse_time(s: str) -> float:
# Supports epoch seconds or ISO format
s = s.strip()
try:
return float(s)
except ValueError:
# Example ISO: 2026-02-23T10:15:20.123Z
return dt.datetime.fromisoformat(s.replace("Z", "+00:00")).timestamp()
def load_events(csv_path: Path) -> list[dict]:
events = []
with csv_path.open("r", encoding="utf-8", newline="") as f:
r = csv.DictReader(f)
for row in r:
row["ts"] = parse_time(row["timestamp"])
events.append(row)
events.sort(key=lambda x: x["ts"])
return events
def burst_detection(events: list[dict], window_sec: int = 60, threshold: int = 200) -> list[dict]:
# Sliding window count per process_id
hits = []
idx = 0
counts = defaultdict(int)
window = []
for e in events:
window.append(e)
counts[e["process_id"]] += 1
# Remove old events from window
while window and (e["ts"] - window[0]["ts"]) > window_sec:
old = window.pop(0)
counts[old["process_id"]] -= 1
# Flag any process above threshold at this point
for pid, c in list(counts.items()):
if c == threshold:
sample = next((x for x in reversed(window) if x["process_id"] == pid), None)
if sample:
hits.append({
"process_id": pid,
"process_name": sample.get("process_name", ""),
"user": sample.get("user", ""),
"count_in_window": c,
"window_sec": window_sec,
"timestamp": e["ts"]
})
return hits
def extension_churn(events: list[dict], suffix: str = ".test", threshold: int = 200) -> dict:
churn = 0
for e in events:
if e.get("action", "").lower() == "rename" and e.get("file_path", "").lower().endswith(suffix.lower()):
churn += 1
return {"suffix": suffix, "rename_count": churn, "threshold": threshold, "flag": churn >= threshold}
def main():
import argparse
parser = argparse.ArgumentParser(description="Ransomware-style burst activity detection (lab telemetry).")
parser.add_argument("--csv", required=True, help="Path to exported telemetry CSV")
parser.add_argument("--window-sec", type=int, default=60)
parser.add_argument("--burst-threshold", type=int, default=200)
parser.add_argument("--suffix", default=".test")
parser.add_argument("--suffix-threshold", type=int, default=200)
args = parser.parse_args()
events = load_events(Path(args.csv))
bursts = burst_detection(events, window_sec=args.window_sec, threshold=args.burst_threshold)
churn = extension_churn(events, suffix=args.suffix, threshold=args.suffix_threshold)
print("\n=== Burst detections ===")
if not bursts:
print("No burst thresholds met.")
else:
for b in bursts[:20]:
ts = dt.datetime.utcfromtimestamp(b["timestamp"]).isoformat() + "Z"
print(f"{ts} | {b['process_name']} (PID {b['process_id']}) | user={b['user']} | {b['count_in_window']} events/{b['window_sec']}s")
print("\n=== Extension churn ===")
print(churn)
if __name__ == "__main__":
main()
How to use it
Export your telemetry for the time window of the simulation run, then run:
python detect_burst.py --csv logs/telemetry_run_01.csv --window-sec 60 --burst-threshold 250 --suffix ".test" --suffix-threshold 500
The output gives you a clear starting point:
- Which process IDs triggered the burst
- Which user context ran it
- Whether rename churn exceeded your threshold
Tune detections with baselines, not guesses
False positives burn time and reduce trust in alerts. Tuning is a structured loop:
- Run a normal workload and record event rates.
- Run the simulator in slow mode and record event rates.
- Run the simulator in fast mode and record event rates.
- Set thresholds that trigger on fast mode but stay quiet on normal workloads.
- Validate against more “normal” noise: large file copies, unzip operations, software installs.
A practical first pass for a lab folder often looks like:
- Burst threshold: 200–500 file events per minute per process
- Rename churn threshold: 500+ renames per run
- Directory spread: 20+ subdirectories in a short window
Your environment decides the final numbers.
Test endpoint protection features that target ransomware-style behavior
Controlled folder access (Windows Defender)
Controlled folder access blocks or audits untrusted processes that try to modify protected folders. Treat it as a tuning project:
- Start in audit mode.
- Run simulations.
- Review what got flagged.
- Add allow rules for known-good tools that you rely on.
- Switch to enforce mode for selected folders.
Run your simulator against:
- A folder that CFA protects
- A folder that CFA does not protect
Compare the difference. That comparison teaches more than reading product docs.
A “ransomware day” lab exercise
This format works for solo practice or a class session.
Phase 1: Preparation
- Snapshot the VM.
- Enable logging and confirm you can export it.
- Create the lab folder and confirm the simulator safety check passes.
Phase 2: Baseline
- Copy a large folder into the target directory.
- Unzip a large archive in the target directory.
- Record file event rates and process names.
Phase 3: Simulation
Run three modes:
- Slow (10 ops/sec)
- Medium (100 ops/sec)
- Fast (300 ops/sec)
Write down:
- Time-to-alert (if any)
- Alert titles and severity
- Process chain details (parent process, command line, user)
- Whether protection features blocked changes
Phase 4: Detection review
- Export telemetry for each run.
- Run the Python detector.
- Compare:
- Burst hit processes
- Rename churn
- Differences between slow and fast modes
Phase 5: Response drill
Treat the fast run as a real incident and write a short incident note:
- What endpoint triggered the alert?
- What user context ran the process?
- What path showed the highest churn?
- What containment step would you take first?
- What would you check next (shares, servers, backups)?
This drill builds muscle memory for the first 30 minutes of a ransomware response.
Incident response workflow for ransomware-style alerts
A clean response sequence reduces damage and preserves evidence.
1) Contain quickly
- Isolate the host from the network if impact looks active.
- Stop the process if you can do it safely.
- Disable compromised accounts if the user context looks wrong.
2) Scope before you wipe
- Identify other endpoints with similar burst file activity.
- Look for the same process name, hash, or command line pattern.
- Check for lateral movement indicators around the same time window.
3) Recover with discipline
- Validate backups before restore.
- Restore in stages.
- Rotate credentials used on affected hosts, especially admin accounts.
- Keep a timeline of actions and timestamps.
Ransomware response often fails when teams restore too early without confirming the attacker no longer has access. A short pause to scope can prevent repeat impact.
Common mistakes and fixes
Mistake: Running tests outside the lab directory
Fix: Keep the simulator path guardrails. Treat a refusal to run as a success.
Mistake: Alert rules that only match one file extension
Fix: Use behavior thresholds and directory spread. Attackers change extensions easily.
Mistake: No baseline
Fix: Record normal activity first. Baselines make thresholds defensible.
Mistake: No restore testing
Fix: Run restore drills quarterly. Backups matter only when they restore fast.
Quick checklist you can paste into a playbook
- Create a dedicated lab folder and verify isolation.
- Enable process and file telemetry (Sysmon/EDR).
- Run normal workloads to capture baseline activity.
- Run the simulator in slow, medium, and fast modes.
- Export telemetry for each run.
- Use Python detection to flag burst and churn patterns.
- Tune thresholds until normal workloads stay quiet.
- Document the first-response steps for a burst alert.
- Repeat monthly and track time-to-alert improvements.
Key takeaways
- Python is useful for ransomware training because it can generate controlled file churn and help you analyze telemetry in a repeatable way.
- Ransomware detection often comes down to burst file activity tied to a specific process and user context.
- A safe impact simulator can teach the same lessons defenders need without encrypting anything.
- Baselines make your thresholds credible and reduce false positives.
- Protection features like controlled folder access become more effective after tuning with realistic simulations.
- Incident response improves when teams rehearse containment, scoping, and recovery as a single workflow.