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>
178 lines
8.1 KiB
Bash
Executable file
178 lines
8.1 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Tier-3 + Tier-4 deploy orchestrator. Idempotent. ZERO operator
|
|
# interaction — including no API key, no signup, no manual upload.
|
|
#
|
|
# Steps (each idempotent on its own):
|
|
# 1. install-msfrpcd.sh — auto-install metasploit-framework via
|
|
# Rapid7 omnibus + drop systemd unit
|
|
# 2. fetch-metasploitable2.sh — pull the disk image from the
|
|
# SourceForge public mirror (TOFU)
|
|
# 3. setup_bridge.sh — bring up br-malware host-only bridge
|
|
# for callback-payload modules
|
|
# 4. Tier-3 verify — fire vsftpd_234_backdoor against the
|
|
# freshly-fetched VM, confirm session
|
|
# lands and an episode is recorded
|
|
# 5. Tier-4 deploy — clone theZoo (public security-research
|
|
# repo, no auth), extract one real
|
|
# binary per manifest family, stage at
|
|
# samples/store/<sha256>, rewrite
|
|
# manifest.toml in place. MANDATORY:
|
|
# the deploy fails if zero samples land.
|
|
#
|
|
# Inputs (env, all optional):
|
|
# SKIP_VERIFY — set to skip the live Tier-3 fire test
|
|
# SKIP_BRIDGE — set to skip bridge setup (limits to non-callback modules)
|
|
# SKIP_TIER4 — set to skip Tier-4 deploy entirely (DEPRECATED;
|
|
# leaves you with mimic-only data, defeats the project)
|
|
#
|
|
# Run as root from anywhere on the lab host. Sub-scripts handle their
|
|
# own root checks.
|
|
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
INSTALL_ROOT="${INSTALL_ROOT:-/opt/cis490}"
|
|
DATA_ROOT="${DATA_ROOT:-/var/lib/cis490}"
|
|
ETC_ROOT="${ETC_ROOT:-/etc/cis490}"
|
|
|
|
log() { printf '[install-tier-3-4] %s\n' "$*" >&2; }
|
|
die() { log "FATAL: $*"; exit 1; }
|
|
|
|
[[ $EUID -eq 0 ]] || die "must run as root"
|
|
|
|
# Resolve script paths — prefer $INSTALL_ROOT (production) over
|
|
# $REPO_ROOT (dev clone) so a re-run under systemd uses the same
|
|
# scripts the orchestrator does.
|
|
script_path() {
|
|
local name="$1"
|
|
if [[ -x "$INSTALL_ROOT/scripts/$name" ]]; then echo "$INSTALL_ROOT/scripts/$name"; return
|
|
elif [[ -x "$REPO_ROOT/scripts/$name" ]]; then echo "$REPO_ROOT/scripts/$name"; return
|
|
elif [[ -x "$INSTALL_ROOT/vm/$name" ]]; then echo "$INSTALL_ROOT/vm/$name"; return
|
|
elif [[ -x "$REPO_ROOT/vm/$name" ]]; then echo "$REPO_ROOT/vm/$name"; return
|
|
else die "$name not found in $INSTALL_ROOT or $REPO_ROOT"
|
|
fi
|
|
}
|
|
|
|
# --- 1. msfrpcd --------------------------------------------------------
|
|
log "[1/5] install metasploit-framework + msfrpcd unit"
|
|
"$(script_path install-msfrpcd.sh)"
|
|
|
|
if ! systemctl is-active --quiet cis490-msfrpcd; then
|
|
log "starting cis490-msfrpcd"
|
|
systemctl enable --now cis490-msfrpcd
|
|
fi
|
|
sleep 3
|
|
if ! ss -ltn 2>/dev/null | grep -q ':55553'; then
|
|
log "cis490-msfrpcd not listening on 127.0.0.1:55553 yet — waiting up to 30s"
|
|
for _ in $(seq 1 30); do
|
|
ss -ltn 2>/dev/null | grep -q ':55553' && break
|
|
sleep 1
|
|
done
|
|
fi
|
|
ss -ltn 2>/dev/null | grep -q ':55553' || \
|
|
die "msfrpcd never bound to :55553 — check 'journalctl -u cis490-msfrpcd'"
|
|
log "msfrpcd ✓"
|
|
|
|
# --- 2. metasploitable2 image ------------------------------------------
|
|
log "[2/5] fetch Metasploitable2 disk image"
|
|
OUT_DIR="$DATA_ROOT/vm/images"
|
|
install -d -m 0755 -o cis490 -g cis490 "$OUT_DIR"
|
|
OUT_DIR="$OUT_DIR" "$(script_path fetch-metasploitable2.sh)"
|
|
chown cis490:cis490 "$OUT_DIR/metasploitable2.qcow2" 2>/dev/null || true
|
|
log "metasploitable2.qcow2 ✓"
|
|
|
|
# --- 3. bridge ---------------------------------------------------------
|
|
if [[ -z "${SKIP_BRIDGE:-}" ]]; then
|
|
log "[3/5] bring up br-malware host-only bridge"
|
|
"$(script_path setup_bridge.sh)" || log "bridge setup failed (non-fatal); only non-callback modules will fire"
|
|
log "br-malware ✓"
|
|
else
|
|
log "[3/5] SKIP_BRIDGE set — limiting to non-callback modules"
|
|
fi
|
|
|
|
# --- 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
|
|
log "[4/5] verify Tier-3 fire (distccd_command_exec)"
|
|
set -a
|
|
# shellcheck disable=SC1091
|
|
. "$ETC_ROOT/msfrpc.env"
|
|
set +a
|
|
PY="$INSTALL_ROOT/.venv/bin/python"
|
|
[[ -x "$PY" ]] || PY="$(command -v python3)"
|
|
if ! TARGET_PORTS="5632:3632,4444:4444" PORT_BASE=5632 \
|
|
sudo -E -u cis490 "$PY" "$INSTALL_ROOT/tools/run_tier3_demo.py" \
|
|
--module distccd_command_exec \
|
|
--target-port 5632 \
|
|
--data-root "$DATA_ROOT/data" \
|
|
--target-boot-timeout 240 \
|
|
> /tmp/cis490-tier3-verify.log 2>&1; then
|
|
log "verify run failed — log at /tmp/cis490-tier3-verify.log; dumping last 30 lines:"
|
|
tail -30 /tmp/cis490-tier3-verify.log >&2 || true
|
|
die "Tier-3 fire failed"
|
|
fi
|
|
if grep -q '^episode_id = ' /tmp/cis490-tier3-verify.log; then
|
|
log "Tier-3 verified ✓ ($(grep '^episode_id = ' /tmp/cis490-tier3-verify.log))"
|
|
else
|
|
log "verify run finished but no episode_id seen — log at /tmp/cis490-tier3-verify.log"
|
|
fi
|
|
else
|
|
log "[4/5] SKIP_VERIFY set"
|
|
fi
|
|
|
|
# --- 5. Tier-4 deploy (MANDATORY, no auth required) --------------------
|
|
if [[ -n "${SKIP_TIER4:-}" ]]; then
|
|
log "[5/5] SKIP_TIER4 set — leaving this host on Tier 2/3 mimic-only."
|
|
log " This is NOT the recommended configuration; the project's"
|
|
log " training target is real-binary episodes."
|
|
else
|
|
log "[5/5] Tier-4 deploy (real malware fetch from theZoo — mandatory)"
|
|
|
|
command -v git >/dev/null || die "git not installed; need it to clone theZoo"
|
|
|
|
PY="$INSTALL_ROOT/.venv/bin/python"
|
|
[[ -x "$PY" ]] || PY="$(command -v python3)"
|
|
|
|
# theZoo clone lives on shared persistent storage so re-runs don't
|
|
# re-download. cis490 user owns it for periodic git pull.
|
|
THEZOO_DIR="${THEZOO_DIR:-/var/lib/cis490/theZoo}"
|
|
install -d -o cis490 -g cis490 -m 0755 "$(dirname "$THEZOO_DIR")"
|
|
|
|
if ! sudo -E -u cis490 "$PY" \
|
|
"$INSTALL_ROOT/tools/auto_fetch_samples.py" \
|
|
--thezoo-clone-dir "$THEZOO_DIR" \
|
|
> /tmp/cis490-tier4-deploy.log 2>&1; then
|
|
log "Tier-4 fetch failed — last 30 lines of /tmp/cis490-tier4-deploy.log:"
|
|
tail -30 /tmp/cis490-tier4-deploy.log >&2 || true
|
|
die "Tier-4 deploy failed; without real binaries this host produces only mimics"
|
|
fi
|
|
REAL_COUNT="$(ls "$INSTALL_ROOT/samples/store/" 2>/dev/null | wc -l)"
|
|
if [[ "$REAL_COUNT" -lt 1 ]]; then
|
|
log "auto_fetch_samples.py exited 0 but samples/store/ is empty — see /tmp/cis490-tier4-deploy.log"
|
|
tail -30 /tmp/cis490-tier4-deploy.log >&2 || true
|
|
die "Tier-4 deploy failed: no real binaries staged"
|
|
fi
|
|
log "Tier-4 ✓ ($REAL_COUNT real binaries staged in $INSTALL_ROOT/samples/store/)"
|
|
fi
|
|
|
|
# Restart the orchestrator now so the next wave actually runs Tier-3
|
|
# against the freshly-staged msfrpcd + samples. Skipped only if the
|
|
# unit isn't enabled yet (first install hasn't run `systemctl enable`).
|
|
if systemctl is-enabled --quiet cis490-orchestrator 2>/dev/null; then
|
|
log "restarting cis490-orchestrator to pick up new modules + samples"
|
|
systemctl restart cis490-orchestrator || \
|
|
log "WARN: orchestrator restart failed — check 'journalctl -u cis490-orchestrator'"
|
|
fi
|
|
|
|
log ""
|
|
log "================================================================="
|
|
log " Tier-3 deploy complete on $(hostname)"
|
|
log "================================================================="
|
|
log " - metasploit-framework + cis490-msfrpcd.service active"
|
|
log " - $OUT_DIR/metasploitable2.qcow2 staged"
|
|
log " - bridge: $(ip link show br-malware >/dev/null 2>&1 && echo up || echo skipped)"
|
|
log " - Tier-4: $(ls "$INSTALL_ROOT/samples/store/" 2>/dev/null | wc -l) real binaries staged"
|
|
log "================================================================="
|