Tier-3: fix QEMU boot, catalog admission, verify module

Bug 14 (vm/launch_target.sh): Metasploitable2 requires -machine pc
(i440fx), -cpu kvm32, -drive if=ide, and -device e1000. The previous
config (-machine q35, -cpu host, -drive if=virtio, virtio-net-pci)
caused a kernel panic at boot because /dev/vda != the grub root=/dev/sda1.
Services never started; the b'' probe fix (Bug 10) then correctly waited
out the full timeout with no result.

Bug 15 (scripts/install-tier-3-4.sh): verify step used vsftpd_234_backdoor
which is requires_bridge=true and has a hardcoded port-6200 backdoor.
Changed to distccd_command_exec with TARGET_PORTS="5632:3632,4444:4444".

manifest.toml: admit distccd_command_exec and unreal_ircd_3281_backdoor
to the module catalog. Both use cmd/unix/bind_perl (bind shell, no guest
egress, SLIRP-safe). distccd returns a valid protocol response so MSF's
handler runs and session_open fires. Verified against Metasploitable2
sourceforge image sha256 a8c019c3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Elliott Kolden 2026-05-05 16:41:41 -06:00
parent b73f5559dc
commit b29d30a1b2
4 changed files with 91 additions and 19 deletions

View file

@ -259,13 +259,70 @@ exploit fires, so Metasploit's handler is called and sessions open.
--- ---
## Bug 14 — QEMU launch config incompatible with Metasploitable2 (boot hang)
**Symptom:** Every `_wait_for_tcp` probe returns `b''` for the full timeout
(even after the Bug 10 fix). No service — FTP, Samba, distccd, IRC — ever
becomes reachable. The VM consumes CPU (QEMU runs) but nothing listens.
**Root cause (three compounding issues in `launch_target.sh`):**
1. `-drive if=virtio` presents the disk as `/dev/vda`. Metasploitable2's GRUB
was built for VMware SCSI (`/dev/sda`). Ubuntu 8.04's kernel command line
says `root=/dev/sda1`. The kernel can't mount root on `/dev/vda` → kernel
panic immediately after decompression. Services never start.
2. `-machine q35` is a PCIe chipset (Sandy Bridge era). Old ISA-emulated
devices and BIOS assumptions in Ubuntu 8.04 break under q35.
3. `-cpu host` exposes AVX/XSAVE and other modern CPU features. Linux 2.6.24
doesn't know how to save/restore these in context switches; the kernel
freezes or mishandles the first SIMD operation during boot.
**Fix:** Three changes in `vm/launch_target.sh`:
- `-machine q35``-machine pc` (i440fx, the classic PC compatible machine)
- `-drive if=virtio``-drive if=ide` (Ubuntu 8.04 libata presents this as
`/dev/sda`, matching the GRUB `root=` line)
- `-cpu host` (KVM) → `-cpu kvm32` (safe 32-bit KVM model, no exotic flags)
- `-device virtio-net-pci``-device e1000` (Intel e1000: universally
supported since Linux 2.2, in every kernel config Metasploitable2 uses)
**Files:** `vm/launch_target.sh`
---
## Bug 15 — Tier-3 verify uses vsftpd (bridge-only, always fails on SLIRP)
**Symptom:** `install-tier-3-4.sh` verify step always fails. The vsftpd
module's backdoor opens port 6200 (hardcoded in the binary and the MSF
module). On SLIRP, all slots would need to share the same host port 6200,
which QEMU refuses. The verify is killed by `_wait_for_tcp` or by the exploit
itself never reaching a session.
**Root cause:** The verify step was left on `vsftpd_234_backdoor` after Bug 5
marked that module `requires_bridge = true`. The verify subprocess doesn't
have a bridge configured and doesn't set up the extra hostfwd for port 6200.
**Fix:** Changed verify to `distccd_command_exec` with correct SLIRP port
mappings: `TARGET_PORTS="5632:3632,4444:4444"` and `--target-port 5632`.
distccd doesn't hardcode a backdoor port — the bind shell uses the
fleet-assigned `LPORT`. No bridge needed.
**Files:** `scripts/install-tier-3-4.sh`
---
## Net result after all fixes ## Net result after all fixes
With fixes 113 applied: With fixes 115 applied:
- Metasploitable2 boots correctly under KVM (pc machine, kvm32 CPU, ide disk,
e1000 network). Services start ~6070 s after QEMU launch.
- `_wait_for_tcp` correctly waits until a service is genuinely listening - `_wait_for_tcp` correctly waits until a service is genuinely listening
(returns only on `socket.timeout` or non-empty banner data). (returns only on `socket.timeout` or non-empty banner data).
- `distccd_command_exec` and `unreal_ircd_3281_backdoor` are now available - `distccd_command_exec` and `unreal_ircd_3281_backdoor` are admitted to the
on SLIRP-only runs; `samba_usermap_script` is removed. module catalog; both are SLIRP-compatible with `cmd/unix/bind_perl`.
- `samba_usermap_script` removed from catalog (NoReply, sessions never open).
- `msgpack.unpackb` accepts integer session ID keys without crashing. - `msgpack.unpackb` accepts integer session ID keys without crashing.
- The verify step uses `distccd_command_exec` on SLIRP+hostfwd.
- Sessions open, workloads execute, episodes complete with `session_open` - Sessions open, workloads execute, episodes complete with `session_open`
events. events.

View file

@ -110,14 +110,21 @@ netflow_bucket_ms = 100
# Every entry MUST carry `verified_against` and `last_verified`. The # Every entry MUST carry `verified_against` and `last_verified`. The
# absence of either drops the module from the active catalog. # absence of either drops the module from the active catalog.
# #
# Empty as of 2026-05-04: PIPELINE.md §3 found 0/67 session_open on # distccd and unreal_ircd admitted 2026-05-05 on elliott-thinkpad.
# samba_usermap_script against the SourceForge Metasploitable2 image, # Both use cmd/unix/bind_perl (host connects in via SLIRP hostfwd —
# and no in-house verified target exists yet. §5 step 3 builds the # no bridge egress). Unlike samba_usermap_script, these exploits return
# target VM, step 4 re-admits modules with verification recorded. # a valid protocol response after firing, so Metasploit's handler runs
# Until then, Tier-3 episodes do not run — the dataset is honest # and session_open fires. Verified against the SourceForge Metasploitable2
# Tier-2 only. # image (sha256 a8c019c3...) with the QEMU fixes (Bug 14) applied.
[catalog] [[catalog.modules]]
modules = [] name = "distccd_command_exec"
verified_against = "metasploitable2-sourceforge-a8c019c3"
last_verified = "2026-05-05"
[[catalog.modules]]
name = "unreal_ircd_3281_backdoor"
verified_against = "metasploitable2-sourceforge-a8c019c3"
last_verified = "2026-05-05"
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# [targets] — target VM images (§4.2) # [targets] — target VM images (§4.2)

View file

@ -91,17 +91,22 @@ else
fi fi
# --- 4. Tier-3 verify -------------------------------------------------- # --- 4. Tier-3 verify --------------------------------------------------
# Uses distccd_command_exec (SLIRP-safe bind_perl, no bridge required).
# vsftpd_234_backdoor hardcodes port 6200 which collides across SLIRP
# slots and requires the host-only bridge — not usable as a SLIRP verify.
# distccd: service on guest:3632 → host:5632; bind shell on guest:4444.
if [[ -z "${SKIP_VERIFY:-}" ]]; then if [[ -z "${SKIP_VERIFY:-}" ]]; then
log "[4/5] verify Tier-3 fire (vsftpd_234_backdoor)" log "[4/5] verify Tier-3 fire (distccd_command_exec)"
set -a set -a
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$ETC_ROOT/msfrpc.env" . "$ETC_ROOT/msfrpc.env"
set +a set +a
PY="$INSTALL_ROOT/.venv/bin/python" PY="$INSTALL_ROOT/.venv/bin/python"
[[ -x "$PY" ]] || PY="$(command -v python3)" [[ -x "$PY" ]] || PY="$(command -v python3)"
if ! sudo -E -u cis490 "$PY" "$INSTALL_ROOT/tools/run_tier3_demo.py" \ if ! TARGET_PORTS="5632:3632,4444:4444" PORT_BASE=5632 \
--module vsftpd_234_backdoor \ sudo -E -u cis490 "$PY" "$INSTALL_ROOT/tools/run_tier3_demo.py" \
--target-port 2021 \ --module distccd_command_exec \
--target-port 5632 \
--data-root "$DATA_ROOT/data" \ --data-root "$DATA_ROOT/data" \
--target-boot-timeout 240 \ --target-boot-timeout 240 \
> /tmp/cis490-tier3-verify.log 2>&1; then > /tmp/cis490-tier3-verify.log 2>&1; then

View file

@ -89,7 +89,10 @@ fi
CPU_FLAGS=() CPU_FLAGS=()
if [[ "$ACCEL" == "kvm" ]]; then if [[ "$ACCEL" == "kvm" ]]; then
CPU_FLAGS=(-cpu host) # kvm32: safe 32-bit KVM model. -cpu host exposes XSAVE/AVX/etc. that
# Ubuntu 8.04's 2.6.24 kernel (Metasploitable2) can't handle, causing
# a boot hang before any services start.
CPU_FLAGS=(-cpu kvm32)
fi fi
AGENT_SOCK="$RUN_DIR/agent.sock" AGENT_SOCK="$RUN_DIR/agent.sock"
@ -101,13 +104,13 @@ AGENT_SOCK="$RUN_DIR/agent.sock"
# host-side collector simply gets no rows. Harmless. # host-side collector simply gets no rows. Harmless.
exec qemu-system-x86_64 \ exec qemu-system-x86_64 \
-name cis490-target \ -name cis490-target \
-machine q35,accel="$ACCEL" \ -machine pc,accel="$ACCEL" \
"${CPU_FLAGS[@]}" \ "${CPU_FLAGS[@]}" \
-smp 1,sockets=1,cores=1,threads=1 \ -smp 1,sockets=1,cores=1,threads=1 \
-m "$RAM_MIB" \ -m "$RAM_MIB" \
-drive file="$IMAGE",format=qcow2,if=virtio,snapshot=on \ -drive file="$IMAGE",format=qcow2,if=ide,snapshot=on \
-netdev "$NETDEV" \ -netdev "$NETDEV" \
-device virtio-net-pci,netdev=n0 \ -device e1000,netdev=n0 \
-device virtio-serial-pci,id=cis490vs0 \ -device virtio-serial-pci,id=cis490vs0 \
-chardev socket,id=cis490agent,path="$AGENT_SOCK",server=on,wait=off \ -chardev socket,id=cis490agent,path="$AGENT_SOCK",server=on,wait=off \
-device virtserialport,chardev=cis490agent,name=cis490.guest.agent \ -device virtserialport,chardev=cis490agent,name=cis490.guest.agent \