Adaptive multi-agent cybersecurity simulation — model real attack chains, tune detection logic, and measure what actually matters: MTTD, MTTR, and blast radius.
I built this while thinking about a question that comes up a lot in security engineering: how do you reason about detection coverage before you're under attack?
Most threat modeling happens on whiteboards. This simulation lets you run an attacker against a defender on a specific network topology and watch the kill chain unfold — which techniques the attacker leans on, how quickly the defender picks up the signal, and what the blast radius looks like by the time containment kicks in.
It's not a replacement for real red team exercises, but it's a fast way to prototype detection tuning, model IR response delays, or understand why a supply chain attacker can be in your environment for weeks before anything fires.
Three built-in scenarios, each modeled on real threat patterns:
| Scenario | Threat | Key characteristic |
|---|---|---|
lateral_movement |
Perimeter breach → internal pivot | DMZ foothold → internal recon → DC compromise |
cloud_iam |
Compromised identity → privilege escalation | Misconfigured IAM roles, IMDS credential theft, silent S3 exfil |
supply_chain |
Malicious dependency → CI/CD compromise | Dependency confusion → pipeline takeover → poisoned artifact in prod |
| Technique ID | Name | Scenario |
|---|---|---|
| T1046 | Network Service Scanning | lateral_movement |
| T1190 | Exploit Public-Facing Application | lateral_movement |
| T1110.001 | Brute Force: Password Guessing | lateral_movement |
| T1021.001 | Remote Services: SSH | lateral_movement |
| T1547.001 | Boot or Logon Autostart | lateral_movement |
| T1048 | Exfiltration Over Alternative Protocol | lateral_movement |
| T1078.004 | Valid Accounts: Cloud Accounts | cloud_iam |
| T1580 | Cloud Infrastructure Discovery | cloud_iam |
| T1078 | Privilege Escalation via Misconfigured Role | cloud_iam |
| T1552.005 | Cloud Instance Metadata API (IMDS) | cloud_iam |
| T1530 | Data from Cloud Storage Object | cloud_iam |
| T1195.001 | Supply Chain: Software Dependencies | supply_chain |
| T1552.001 | Unsecured Credentials: CI/CD Secrets | supply_chain |
| T1072 | Software Deployment Tools | supply_chain |
git clone https://github.com/SentinelByte/cyber-sim-mas.git
cd cyber-sim-mas
pip install -e ".[dev]"
# list available scenarios
python run.py --list-scenarios
# run with verbose round-by-round output
python run.py --scenario lateral_movement --rounds 15 --verbose
# reproducible run — share the seed and anyone gets the same outcome
python run.py --scenario cloud_iam --rounds 20 --seed 42
# supply chain / CI-CD compromise
python run.py --scenario supply_chain --rounds 12 --verbose --seed 7Example output (--seed 13 gives a run where both attacker and defender are active):
$ python run.py --scenario lateral_movement --rounds 15 --seed 13
╭──────────────────────────────── cyber-sim-mas ─────────────────────────────────╮
│ Lateral Movement │
│ Attacker gains DMZ foothold and pivots toward internal assets │
│ │
│ Nodes: 5 · Rounds: 15 · Detection threshold: 3 · Techniques: 7 · │
│ seed=13 │
╰────────────────────────────────────────────────────────────────────────────────╯
Simulation Results — lateral_movement
╭─────────────────────────────┬──────────╮
│ Metric │ Value │
├─────────────────────────────┼──────────┤
│ Total rounds │ 15 │
│ MTTD (mean time to detect) │ 3 rounds │
│ MTTR (mean time to respond) │ 4 rounds │
│ Peak blast radius │ 100.0% │
│ Nodes compromised (final) │ 5 │
│ Attack success rate │ 83.3% │
│ Defender efficiency │ 20.0% │
╰─────────────────────────────┴──────────╯
Omit --seed for a fresh random run each time. Pass the same seed to anyone you want to discuss the same outcome with.
graph TD
CLI[run.py CLI] --> SIM[Simulation Engine]
SIM --> ATK[AttackerAgent]
SIM --> DEF[DefenderAgent]
SIM --> MET[SimulationMetrics]
ATK --> NET[Network]
DEF --> NET
NET --> N1[Node DMZ]
NET --> N2[Node Internal]
NET --> N3[Node Cloud/CICD]
ATK --> TECH[techniques.py\nMITRE ATT&CK]
ATK --> WEIGHTS[Adaptive Weights\nper-technique]
DEF --> PHASES[Response Phases\nMonitor → Investigate\n→ Contain → Remediate]
MET --> MTTD[MTTD]
MET --> MTTR[MTTR]
MET --> BLAST[Blast Radius]
SCEN[Scenarios] --> SIM
SCEN --> S1[lateral_movement]
SCEN --> S2[cloud_iam]
SCEN --> S3[supply_chain]
The attacker adapts its technique selection across rounds using a simple reinforcement heuristic — not ML, but a meaningful approximation of real adversarial behavior:
- Each technique starts with equal weight
1.0 - Success:
weight *= 1.3(capped at5.0) - Failure:
weight *= 0.75(floored at0.1) - Technique selection is a weighted random draw from applicable candidates for the current stage and node
This means the attacker naturally converges toward techniques that work on the specific network topology it's facing — brute-force SSH gets deprioritized after repeated failures, while silent credential theft techniques get amplified if they succeed early.
The mechanism is intentionally kept simple. The goal isn't to model a sophisticated APT — it's to demonstrate that even basic strategy adaptation changes detection and response dynamics in measurable ways.
Attacker assumptions:
- External attacker with no prior knowledge of the network
- Follows a simplified kill chain (Recon → Initial Access → Persistence → Lateral Movement → Exfiltration)
- Adapts technique prioritization based on success history
- Cannot bypass isolation — if a node is isolated, it's unreachable
Defender assumptions:
- Detection is probabilistic — depends on technique noise level and node detection difficulty
- Responses are delayed (configurable
response_delay) to model IR lead time - Can patch (keep node online, slower) or isolate (fast, breaks availability)
- Alert fatigue modeled via configurable
detection_threshold
Limitations:
- No actual network routing — reachability is zone-based, not graph-based
- Techniques don't chain (no credential reuse across nodes)
- Attacker has perfect knowledge of services once recon succeeds
- No notion of time-of-day, attacker dwell time, or concurrent attacks
- The simulation is discrete-round, not continuous — timing within a round isn't modeled
These limitations are deliberate scope constraints, not bugs. A simulation that's too complex to understand in 10 minutes doesn't serve the learning purpose.
cyber-sim-mas/
├── cybersim/
│ ├── agents/
│ │ ├── attacker.py # Adaptive kill-chain attacker
│ │ └── defender.py # Detection + IR response phases
│ ├── core/
│ │ ├── simulation.py # Engine (decoupled from display)
│ │ └── metrics.py # MTTD, MTTR, blast radius
│ ├── env/
│ │ ├── node.py # Asset model (CVSS, zone, state)
│ │ └── network.py # Topology + reachability
│ ├── scenarios/
│ │ ├── lateral_movement.py
│ │ ├── cloud_iam.py
│ │ └── supply_chain.py
│ └── techniques.py # MITRE ATT&CK technique definitions
├── tests/ # 52 unit + integration tests
├── .github/workflows/ci.yml # Lint, test, bandit scan
├── run.py # CLI entrypoint
└── pyproject.toml
pip install -e ".[dev]"
# run tests
pytest
# lint
ruff check cybersim/ tests/ run.py
# static security analysis
bandit -r cybersim/ -llTests cover node state transitions, attacker stage progression, adaptive weight behavior, defender detection phases, and full simulation invariants across all three scenarios. Results are seeded for determinism.
MIT — see LICENSE.
Built by SentinelByte.