Root causes and fixes documented in TIER3-BRINGUP.md. Summary:
1. BRIDGE env var leaked into Tier-3 subprocess → target VM used tap
instead of SLIRP; fix: env.pop("BRIDGE") in fleet _run_slot.
2. usable_modules filter conditioned on BRIDGE presence → bridge-requiring
modules selected on SLIRP runs; fix: always filter requires_bridge.
3. cmd/unix/interact creates no session.list entry → session_open_timeout
every episode; fix: switch samba_usermap_script to cmd/unix/bind_perl.
4. Per-slot LPORT hostfwd used wrong guest port (host:5444→guest:4444);
fix: extra_host_port:extra_host_port mapping so guest binds the
per-slot LPORT directly.
5. vsftpd backdoor port 6200 hardcoded → collision across concurrent slots;
fix: requires_bridge=true filters it from SLIRP fleet runs.
6. SLIRP false-positive in _wait_for_tcp → exploit fires before Samba
boots (~60 s too early); fix: replace TCP probe with serial console
_wait_for_serial_login that waits for actual "login:" prompt.
7. Stale QEMU survives orchestrator restart (start_new_session=True) →
holds hostfwd ports, new QEMU silently fails; fix: kill by pgid from
old pidfile before rmtree.
8. PORT_BASE default used privileged port 21; fix: default to 2021+slot*100.
9. msfrpcd 6.x returns bytes for all string values even with raw=False;
fix: MSFRpcClient._str() recursive decoder applied to all responses.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
173 lines
7.7 KiB
Bash
Executable file
173 lines
7.7 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 --------------------------------------------------
|
|
if [[ -z "${SKIP_VERIFY:-}" ]]; then
|
|
log "[4/5] verify Tier-3 fire (vsftpd_234_backdoor)"
|
|
set -a
|
|
# shellcheck disable=SC1091
|
|
. "$ETC_ROOT/msfrpc.env"
|
|
set +a
|
|
PY="$INSTALL_ROOT/.venv/bin/python"
|
|
[[ -x "$PY" ]] || PY="$(command -v python3)"
|
|
if ! sudo -E -u cis490 "$PY" "$INSTALL_ROOT/tools/run_tier3_demo.py" \
|
|
--module vsftpd_234_backdoor \
|
|
--target-port 2021 \
|
|
--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 "================================================================="
|