CIS490/scripts/install-msfrpcd.sh
Elliott Kolden 667f042707 Tier-3 bring-up: 9 bugs fixed on elliott-ThinkPad (2026-05-01)
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>
2026-05-02 12:26:19 -06:00

153 lines
5.8 KiB
Bash
Executable file

#!/usr/bin/env bash
# Install + configure ``msfrpcd`` for the Tier-3 exploit driver.
#
# Idempotent: re-running on a host that already has msfrpcd refreshes
# the systemd unit and credentials but doesn't reinstall the framework.
#
# Steps:
# 1. Install metasploit-framework via the host package manager (or
# report the right one-liner for that distro). Big download —
# ~1 GiB and several minutes.
# 2. Generate a strong password and store at /etc/cis490/msfrpc.env
# (mode 0640, owner root:cis490).
# 3. Drop /etc/systemd/system/cis490-msfrpcd.service that runs
# msfrpcd bound to 127.0.0.1:55553 with the generated password.
# 4. Enable + start.
#
# After this runs, ``MSFRPC_PASSWORD=$(. /etc/cis490/msfrpc.env;
# echo $MSFRPC_PASSWORD)`` makes tools/run_tier3_demo.py work zero-touch.
set -euo pipefail
ETC_ROOT="/etc/cis490"
ENV_FILE="$ETC_ROOT/msfrpc.env"
UNIT="/etc/systemd/system/cis490-msfrpcd.service"
PORT="${MSFRPC_PORT:-55553}"
USER_NAME="${MSFRPC_USER:-msf}"
log() { printf '[install-msfrpcd] %s\n' "$*" >&2; }
die() { log "FATAL: $*"; exit 1; }
[[ $EUID -eq 0 ]] || die "must run as root"
command -v systemctl >/dev/null || die "systemd not found"
# --- 1. install metasploit-framework -----------------------------------
# Auto-install paths per package manager. Rapid7's omnibus installer
# is the canonical zero-touch path for Debian/Ubuntu — it adds the
# apt repo, the GPG key, and apt-installs the framework. Other
# distros use their native package or fall back to the omnibus shell
# script.
if ! command -v msfrpcd >/dev/null && [[ ! -x /opt/metasploit-framework/bin/msfrpcd ]]; then
log "msfrpcd not found; installing metasploit-framework (~1 GiB)"
if command -v apt-get >/dev/null; then
# Rapid7's omnibus installer wraps the apt-repo + GPG-key
# bootstrap + apt install in a single script. We fetch and
# exec it non-interactively. The script does:
# 1. add apt.metasploit.com to /etc/apt/sources.list.d/
# 2. install the GPG key
# 3. apt-get install -y metasploit-framework
log "running Rapid7 omnibus installer"
TMP="$(mktemp -d)"
curl -fsSL \
https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb \
-o "$TMP/msfinstall"
chmod +x "$TMP/msfinstall"
DEBIAN_FRONTEND=noninteractive "$TMP/msfinstall" </dev/null
rm -rf "$TMP"
elif command -v pacman >/dev/null; then
log "pacman -S metasploit"
pacman -Sy --noconfirm metasploit
elif command -v dnf >/dev/null; then
# The omnibus installer also supports rpm distros via the
# same script — it auto-detects and uses dnf/yum.
log "running Rapid7 omnibus installer (dnf path)"
TMP="$(mktemp -d)"
curl -fsSL \
https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb \
-o "$TMP/msfinstall"
chmod +x "$TMP/msfinstall"
"$TMP/msfinstall" </dev/null
rm -rf "$TMP"
else
die "unknown package manager — install metasploit-framework manually, then re-run"
fi
fi
# After install, msfrpcd may live at /opt/metasploit-framework/bin/
# (omnibus) or on PATH (apt repo). Symlink so callers find it.
if ! command -v msfrpcd >/dev/null; then
if [[ -x /opt/metasploit-framework/bin/msfrpcd ]]; then
ln -sf /opt/metasploit-framework/bin/msfrpcd /usr/local/bin/msfrpcd
fi
fi
command -v msfrpcd >/dev/null || die "msfrpcd still missing after install — see journalctl"
# --- 2. generate password ----------------------------------------------
install -d -m 0755 -o root -g root "$ETC_ROOT"
if ! id -u cis490 >/dev/null 2>&1; then
useradd --system --no-create-home --shell /usr/sbin/nologin cis490
fi
if [[ ! -f "$ENV_FILE" ]]; then
log "generating msfrpc password"
PW="$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)"
install -m 0640 -o root -g cis490 /dev/stdin "$ENV_FILE" <<EOF
# Auto-generated by install-msfrpcd.sh — do not edit.
MSFRPC_HOST=127.0.0.1
MSFRPC_PORT=$PORT
MSFRPC_USER=$USER_NAME
MSFRPC_PASSWORD=$PW
EOF
else
log "$ENV_FILE exists; preserving existing password"
fi
# --- 3. systemd unit ----------------------------------------------------
# msfrpcd writes module cache + logs to $HOME/.msf4. With ProtectHome=true
# the service can't reach /root, so we redirect HOME to a path under
# /var/lib/cis490 that is always writable.
MSF_HOME="/var/lib/cis490/msf4"
install -d -m 0755 -o root -g root "$MSF_HOME"
log "installing systemd unit"
cat > "$UNIT" <<EOF
[Unit]
Description=CIS490 — Metasploit RPC daemon (loopback only)
Documentation=https://maxgit.wg/spectral/CIS490
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=$ENV_FILE
Environment=HOME=$MSF_HOME
# msfrpcd flags:
# -P <pw> password
# -U <user> username
# -a <ip> bind address (loopback only — Tier-3 driver runs locally)
# -p <port> port
# -f foreground (no daemonization, so systemd manages PID)
ExecStart=/usr/bin/env msfrpcd -P \${MSFRPC_PASSWORD} -U \${MSFRPC_USER} -a 127.0.0.1 -p \${MSFRPC_PORT} -f
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now cis490-msfrpcd
# --- 4. final smoke -----------------------------------------------------
sleep 2
if ! ss -ltn 2>/dev/null | grep -q ":$PORT"; then
log "WARN: nothing listening on 127.0.0.1:$PORT yet — check"
log " journalctl -u cis490-msfrpcd"
fi
log "done. To run a Tier-3 episode:"
log " set -a; . $ENV_FILE; set +a"
log " python tools/run_tier3_demo.py --module vsftpd_234_backdoor"