Creating Your Own Ransomware with Python (Safely, for Training and Defense)

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

Creating Ransomware

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.csv
  • logs/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_name
  • process_id
  • user
  • file_path
  • action (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:

  1. Run a normal workload and record event rates.
  2. Run the simulator in slow mode and record event rates.
  3. Run the simulator in fast mode and record event rates.
  4. Set thresholds that trigger on fast mode but stay quiet on normal workloads.
  5. 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.

Ashwin S

A cybersecurity enthusiast at heart with a passion for all things tech. Yet his creativity extends beyond the world of cybersecurity. With an innate love for design, he's always on the lookout for unique design concepts.