Tier 3 + Tier 4 deploy runbook in AGENTS.md

Repo has all the code paths for Tier 3 (real exploit fire via msfrpcd)
and Tier 4 (real malware execution via chunked upload), but neither
lab host has run a single Tier-3 episode because msfrpcd and the
Metasploitable2 image aren't deployed there. 3009 episodes in flight
to date are all Tier 2 (mimic workloads in clean Alpine), which is
useful pipeline-validation data but cannot answer the actual research
question.

This commit makes the deploy push-button:

- AGENTS.md: new "Tier 3 + Tier 4 deploy" section listing the three
  prereqs (install-msfrpcd.sh, fetch-metasploitable2.sh, setup_bridge.sh),
  the foreground verify command (run_tier3_demo.py), and the Tier-4
  promotion path (MB API key → fetch_sample.py → manifest edit →
  orchestrator restart).

- samples/manifest.toml: clearer per-entry comment showing the
  4-step sha256 → real-binary promotion path. Replaces the earlier
  "TBD" placeholder which suggested a single edit unlocks Tier 4
  when in fact you need to fetch the binary too.

The fleet runner already auto-detects msfrpcd (orchestrator/fleet.py
_msfrpcd_available()); once the lab-host operator-AI lands the
prereqs, episodes flip to Tier 3 with no orchestrator config change.
Tier 4 follows automatically the next time the deterministic
selector picks a sample whose sha256 file exists in samples/store/.
This commit is contained in:
max 2026-04-30 22:57:23 -05:00
parent 321ea63803
commit 02b9d0a645
2 changed files with 129 additions and 4 deletions

116
AGENTS.md
View file

@ -78,6 +78,122 @@ common silent failures it catches:
`cis490-doctor --json` is machine-readable for use by other agents.
## Tier 3 + Tier 4 deploy (lab-host one-time, ~20 min)
Tier 2 is the default after `install-lab-host.sh`: real Alpine guest,
mimic in-guest workloads. To get **real exploit fire** (Tier 3) and
**real malware execution** (Tier 4), each lab host needs three more
artifacts. The fleet runner auto-detects Tier-3 readiness via the
`_msfrpcd_available()` probe — once msfrpcd is up, episodes flip to
Tier 3 with no orchestrator config change.
### Prerequisites (per lab host)
```sh
# 1. Install Metasploit Framework + msfrpcd. Idempotent; ~1 GiB
# download the first time. Drops a strong password at
# /etc/cis490/msfrpc.env (mode 0640, root:cis490) and a systemd
# unit cis490-msfrpcd.service bound to 127.0.0.1:55553.
sudo /opt/cis490/scripts/install-msfrpcd.sh
sudo systemctl enable --now cis490-msfrpcd.service
systemctl is-active cis490-msfrpcd.service # → active
# 2. Fetch Metasploitable2 qcow2. Rapid7's official download is
# registration-walled; supply the URL+sha256 you obtained from
# your registration. Conversion from VMDK → qcow2 happens
# automatically. Lands at /var/lib/cis490/vm/images/metasploitable2.qcow2.
IMAGE_URL='<your-rapid7-or-mirror-url>' \
IMAGE_SHA256='<sha256-of-the-archive>' \
sudo OUT_DIR=/var/lib/cis490/vm/images \
/opt/cis490/scripts/fetch-metasploitable2.sh
# 3. (Optional but recommended.) Bring up the host-only bridge
# `br-malware` so callback-payload exploits (3 of the 5 bundled
# modules require it: distccd_command_exec, php_cgi_arg_injection,
# unreal_ircd_3281_backdoor) can land. Without the bridge, the
# fleet auto-restricts to non-callback modules
# (vsftpd_234_backdoor, samba_usermap_script).
sudo /opt/cis490/scripts/setup_bridge.sh
```
### Verify Tier-3 fire end-to-end
```sh
# This runs ONE Tier-3 episode in the foreground using whatever
# module + sample the deterministic selector picks for slot=0,
# episode=0 on this host. Should print `module = exploit/...`,
# fire it via msfrpcd, and a normal episode summary at the end.
sudo -u cis490 \
MSFRPC_PASSWORD="$(. /etc/cis490/msfrpc.env; echo $MSFRPC_PASSWORD)" \
/opt/cis490/.venv/bin/python \
/opt/cis490/tools/run_tier3_demo.py \
--module vsftpd_234_backdoor \
--target-port 21 --target-boot-timeout 240
```
If the run prints `module loaded: vsftpd_234_backdoor (exploit/unix/ftp/...)`
and `episode_id = 01...` at the end, Tier 3 is live. The orchestrator's
next wave will use Tier 3 for every episode.
### Tier-4 (real malware execution)
Tier 4 layers on top of Tier 3 — the exploit lands a session, then a
real binary is uploaded via the chunked path and executed inside the
session. Two prerequisites:
```sh
# 1. Add MalwareBazaar API key (free signup at https://bazaar.abuse.ch/).
echo "$MB_KEY" | sudo install -m 0600 -o cis490 -g cis490 /dev/stdin \
/opt/cis490/samples/.bazaar.token
# 2. Pick a sha256 for one of your sample families from MalwareBazaar
# and download the binary. Verifies sha256 on the way in; lands at
# /opt/cis490/samples/store/<sha256>.
sudo -u cis490 /opt/cis490/.venv/bin/python \
/opt/cis490/tools/fetch_sample.py <64-hex-sha256>
# 3. Edit /opt/cis490/samples/manifest.toml: add `source`, `sha256`,
# and `url` fields to the matching entry. The orchestrator's next
# selection that hits that sample will use the real binary
# (sample.kind == "real") — meta.sample.sha256 records it for the
# trainer.
sudo systemctl restart cis490-orchestrator
```
### Confirm Tier 3+4 are flowing
```sh
# On the Pi:
sudo -u cis490 /opt/cis490/.venv/bin/python -c "
import json
real = mimic = 0
modules = set()
for line in open('/var/lib/cis490/index.jsonl'):
pass # use the prune classifier instead
"
# or (better) rerun the diversity audit:
# the multi-host audit script the maintainer keeps for spot-checking
```
The on-Pi maintainer will see `meta.exploit.module_name` populated
and `meta.sample.kind == "real"` for the new episodes. If those stay
null after deploy, file an issue with the `journalctl -u cis490-orchestrator`
trace from the failed wave.
### Don't shortcut
- DO NOT install `metasploit-framework` system-wide outside
install-msfrpcd.sh. The script wires the systemd unit + creds; a
manual install will work but the orchestrator's `_msfrpcd_available()`
probe expects the unit to be on 127.0.0.1:55553 with a password at
`/etc/cis490/msfrpc.env`.
- DO NOT push your MalwareBazaar API key. `samples/.bazaar.token`
is gitignored; the env var path is preferred for one-shot fetches.
- DO NOT add bogus sha256 entries to `manifest.toml` — every sha256
is verified on download. A wrong value breaks `fetch_sample.py`
and confuses every replay.
## Securing the connection (mTLS) — DO NOT mint your own certs
The lab-host ↔ Pi connection is mTLS over WireGuard. **The cert

View file

@ -19,11 +19,20 @@ name = "xmrig-cryptominer"
family = "XMRig"
category = "cryptominer"
profile = "cpu-saturate"
# A real XMRig fetch goes here when MalwareBazaar pull is wired up:
# source = "MalwareBazaar"
# sha256 = "TBD"
# url = "https://bazaar.abuse.ch/sample/TBD/"
description = "Sustained 1-vCPU saturation, very low IO/net. Pure compute."
# To promote this entry to Tier-4 (real binary):
# 1. Pick a sha256 from https://bazaar.abuse.ch/ for this family.
# 2. Add `source`, `sha256`, `url` fields below.
# 3. On the lab host (one-time per host):
# export MALWAREBAZAAR_API_KEY=<key>
# sudo -u cis490 /opt/cis490/.venv/bin/python \
# /opt/cis490/tools/fetch_sample.py <sha256>
# The sha256 is verified on download; the binary lands at
# /opt/cis490/samples/store/<sha256>.
# 4. Restart cis490-orchestrator. Episodes that select this sample
# now run the real binary via the chunked upload path. If the
# binary isn't on disk, the orchestrator falls back to the mimic
# profile above — both kinds coexist via meta.sample.kind.
[[sample]]
name = "mirai-class-bot"