adversary-yaml-creator · git:20261031.1c4f270 · 2026-10-31 · sha256 fa494fcecefe15dc
adversary-yaml-creator git:20261031.1c4f270A
Immutable. This exact content never changes and is served at /api/v1/blob/fa494fcecefe15dc.
---
name: adversary-yaml-creator
description: "Create LazyOwn adversary YAMLs in one shot: APT playbooks (playbooks/apt_*.yaml), orchestration chains (chains/*.yaml) and exploit definitions (exploits/*.yaml). Knows the exact schemas, token grammars and the four validation gates. For lazyaddons use /lazyaddon-creator instead."
trigger: /adversary-yaml-creator
---
# /adversary-yaml-creator
Given an adversary name, a target service, or a CTF goal, write the YAML that makes
LazyOwn act on it: one exploit definition, one orchestration chain, and (for APT
groups) one playbook. No iteration: copy the real schema below, run the self-checks,
write the files. The three engines reload catalogs on load, so a valid file is
production on the next rank/sweep.
## What to create for each input
| Input | Files |
|-------|-------|
| APT group (not yet in playbooks/) | `playbooks/apt_<slug>.yaml` + `chains/apt_<slug>.yaml` |
| Exploitable service/CVE (not yet in exploits/) | `exploits/<id>.yaml` |
| CTF goal / lab path (not yet in chains/) | `chains/<goal>.yaml` (+ paired fallback chain when the path has a quieter variant) |
| Operator tool (installed CLI, GitHub tool) | use `/lazyaddon-creator` instead |
Naming: `^[a-z][a-z0-9_]{0,63}$`, filename equals the identifier. One file per
artifact. Never guess versions or exploit outcomes: `versions: []` means any.
## File 1 — playbooks/apt_<slug>.yaml
Schema consumed by modules/apt_playbooks.py (AptPlaybookEngine). Keys are slugified
(`Sandworm Team` -> `sandworm_team`), so chain `defaults.playbook` uses the slug.
```yaml
apt_name: Crowdstrike-Threat
aliases: []
source_urls: [https://attack.mitre.org/groups/G0000]
description: One paragraph, who/why/how.
platforms: [windows, linux]
phases:
- name: initial_access # killchain-style phase name
technique_id: T1190
technique_name: Exploit Public-Facing Application
description: What this phase does for this group.
atomic_tests:
- atomic_id: T1190 # ART guideline id; manual:true means operator pastes the command
name: Short label
manual: true
caldera_abilities: # optional
- ability_id: "<uuid>"
detection_hints:
- Concrete observable behaviour
```
Set `manual: true` unless the technique maps to a lab-runnable atomic test you have
verified exists under an `atomic_id` that emulation knows.
## File 2 — chains/<name>.yaml
Schema v2, consumed by modules/chain_registry.py + modules/chain_runner.py via
`apt_chain <name> <target>`. Validation is strict: unknown enabled/chains fail to load;
`fallback` and `extends` targets must exist as sibling files; a chain is never its own
fallback; self-extends is an error.
```yaml
name: ctf_mymachine # == filename
version: 2
description: One line goal.
enabled: true
tags: [ctf, golden-path]
platforms: [linux]
total_timeout: 3600 # seconds, <= 86400
vars: # optional; every var becomes a shell-quoted token
wordlist: /usr/share/wordlists/dirb/common.txt
fallback: ctf_mymachine_quiet # optional; file MUST exist
extends: apt_chain # APT chains only; own steps replace the parent wholesale
defaults:
playbook: <apt-slug>
steps:
- id: bind # ^[a-z][a-z0-9_]{0,63}$
phase: recon # recon | enum | exploit | post_exploitation | ...
run: assign target {target} # {target}, {playbook}, vars, later captures all resolve; shell-quoted
timeout: 300 # seconds, <= 3600
retries: 1 # 0-5
continue_on_error: true # keep the chain alive on soft failures
parallel: true # runs concurrently with other parallel steps
skip_if_exists: sessions/scan_{target}.nmap.xml
capture: step_output # exposes 2000-char stripped output to later steps
expect: "SHELL" # step fails when the capture lacks this text
```
Rules that break chains:
- First token of `run` matching a live shell verb (assign, lazynmap, atomic_exploit,
c2_server, c2_deploy, c2_loot, apt_playbook, attack_plan, atomic_emulate,
create_session_json, lazycreds, hydra, nuclei, gobuster, ...) executes through cmd2;
anything else runs as a raw safe-subprocess shell line. Pipes work; keep each line
one pipeline.
- Single braces only. `{{` never validates (`render_text_token_braces`).
- Evidence fields are optional (evidence, evidence_sha256, required_fields,
benign_alternatives, budget, depends_on) — hunting verdicts only apply when set.
- Validate dry: `apt_chain <name> <target> --dry-run`.
## File 3 — exploits/<id>.yaml
Schema v1 consumed by modules/atomic_exploit_bridge.py. Services/products/versions
are lowercased substrings matched against sessions/scan_<target> nmap results.
```yaml
id: generic_<vector> # == filename
mitre_id: T1190
cve: '' # '' when the vector has no CVE
title: One-line what it proves
severity: critical|high|medium|low
confidence: 0.0-1.0 # >0.7 requires a proven lab run
platforms: [linux]
services: [redis]
products: [redis]
versions: []
requires_binaries: [redis-cli, curl]
commands: # ordered; first SHELL signal stops the chain
- run: printf 'id\n' | nc -w 8 {rhost} {port}
timeout: 15 # 5-600
shell_indicators: [uid=0] # merged with engine defaults
enabled: true
```
Tokens: `{rhost}` target, `{lhost}` attacker, `{lport}` callback, `{port}` resolved
service port. Optional `exec:` block (trigger/shell_port/probe/timeout) turns the
exploit into a multiplexed session usable by emulation; without it the exploit can
sweep but never emulate. Confidence above 0.7 without a proven run is a violation of
the no-guessing rule.
## One-shot self-check (run before declaring done)
```bash
python3 -c "from modules.apt_playbooks import AptPlaybookEngine; e=AptPlaybookEngine(); print(bool(e.get('<slug>')))"
python3 -c "from modules.chain_registry import validate_chain, CHAINS_DIR; import yaml, sys; \
data=yaml.safe_load(open(CHAINS_DIR/'<name>.yaml')); print(validate_chain(data, filename='<name>', chains_dir=CHAINS_DIR))"
python3 modules/atomic_exploit_bridge.py validate
python3 -m pytest tests/test_placeholder_coverage.py -q
```
All four must pass. A failing chain file never loads: the loop silently loses the
path; the bridge refuses the exploit; the playbook engine warns and skips the file.
Do not paper over schema errors with defaults — fix the YAML.
## Completion contract
- Every new adversary gets both the playbook and its `defaults.playbook` chain
(six-line `extends: apt_chain` file), so `apt_chain apt_<slug> <target>` works.
- Every new exploit declares only services/products the box truly exposes.
- Every new CTF chain declares `continue_on_error` on soft steps and `retries`
where the target is noisy, plus an explicit `vars:` wordlist when content
discovery is included (never embed lists inline).
- Artifacts land in `sessions/`; nothing writes to payload.json here.