#!/usr/bin/env bash # Fetch the Metasploitable2 disk image with no operator interaction. # # Defaults to the SourceForge public mirror — the canonical # freely-redistributable copy of Metasploitable2 (Rapid7's own # download is registration-walled but the same VMDK is on # SourceForge, downloaded ~2M times). HTTPS protects the transport. # # Idempotent: if the qcow2 is already on disk we do nothing. # # Inputs (env, all optional): # IMAGE_URL — override the default mirror URL # IMAGE_SHA256 — verify against this hash. If unset and a sha256 # has been recorded by a prior successful fetch # ($OUT_DIR/metasploitable2.qcow2.sha256), use that. # If neither is available, do TOFU (trust on first # use): record the hash of what was downloaded so # subsequent runs verify against it. # OUT_DIR — where to drop the qcow2 (default vm/images/) # # Outputs: # $OUT_DIR/metasploitable2.qcow2 — the disk image # $OUT_DIR/metasploitable2.qcow2.sha256 — recorded archive hash set -euo pipefail # SourceForge public mirror. Direct-download URL — no auth, no # registration. /download 302s to a regional mirror. DEFAULT_IMAGE_URL='https://downloads.sourceforge.net/project/metasploitable/Metasploitable2/metasploitable-linux-2.0.0.zip' IMAGE_URL="${IMAGE_URL:-$DEFAULT_IMAGE_URL}" IMAGE_SHA256="${IMAGE_SHA256:-}" OUT_DIR="${OUT_DIR:-$(cd "$(dirname "$0")/../vm/images" 2>/dev/null && pwd)}" WORK_DIR="${WORK_DIR:-/tmp/cis490-metasploitable-fetch}" log() { printf '[fetch-metasploitable2] %s\n' "$*" >&2; } die() { log "FATAL: $*"; exit 1; } mkdir -p "$OUT_DIR" "$WORK_DIR" # Short-circuit if the qcow2 is already on disk. if [[ -f "$OUT_DIR/metasploitable2.qcow2" ]]; then log "$OUT_DIR/metasploitable2.qcow2 already present; nothing to do" exit 0 fi # Use the recorded sha256 from a prior successful fetch if present # and the env var didn't override it. This pins TOFU across runs # so a tampered re-download fails noisily. SHA_FILE="$OUT_DIR/metasploitable2.qcow2.sha256" if [[ -z "$IMAGE_SHA256" && -f "$SHA_FILE" ]]; then IMAGE_SHA256="$(awk '{print $1}' "$SHA_FILE")" log "using pinned sha256 from $SHA_FILE: $IMAGE_SHA256" fi ARCHIVE="$WORK_DIR/$(basename "$IMAGE_URL")" log "downloading $IMAGE_URL → $ARCHIVE" if [[ -f "$ARCHIVE" ]]; then log "archive already present in work dir; skipping download" else # -L follows SourceForge's redirect to the actual mirror. curl -fL --retry 3 --retry-delay 5 -o "$ARCHIVE.partial" "$IMAGE_URL" mv "$ARCHIVE.partial" "$ARCHIVE" fi ACTUAL="$(sha256sum "$ARCHIVE" | awk '{print $1}')" if [[ -n "$IMAGE_SHA256" ]]; then if [[ "$ACTUAL" != "$IMAGE_SHA256" ]]; then die "sha256 mismatch: expected $IMAGE_SHA256, got $ACTUAL" fi log "sha256 ok" else log "no sha256 supplied — first-run TOFU; pinning $ACTUAL for future runs" fi # Always (re)record so future runs verify against the working hash. echo "$ACTUAL $(basename "$ARCHIVE")" > "$SHA_FILE" # Extract — handle either zip or 7z, since various mirrors choose one # or the other. case "$ARCHIVE" in *.zip) ( cd "$WORK_DIR" && unzip -o "$ARCHIVE" ) ;; *.7z|*.7zip) command -v 7z >/dev/null || die "7z not installed"; \ ( cd "$WORK_DIR" && 7z x -y "$ARCHIVE" ) ;; *) die "unsupported archive type: $ARCHIVE" ;; esac VMDK="$(find "$WORK_DIR" -name 'Metasploitable*.vmdk' -print -quit)" [[ -n "$VMDK" ]] || die "no Metasploitable*.vmdk in extracted archive" log "converting $VMDK → qcow2" command -v qemu-img >/dev/null || die "qemu-img required (apt install qemu-utils)" qemu-img convert -O qcow2 "$VMDK" "$OUT_DIR/metasploitable2.qcow2" # Best-effort cleanup of the work dir — keeps lab-host disk clean. rm -rf "$WORK_DIR" log "done: $OUT_DIR/metasploitable2.qcow2" log "Tier-3 ready when msfrpcd is up. See scripts/install-msfrpcd.sh."