Building a Cyber Range Inside Proxmox: Kali vs. Metasploitable on an Isolated Bridge

A step-by-step build log for a fully isolated cyber range running three layers deep:
VMware Workstation hosting Proxmox, with Kali and Metasploitable 2 wired onto a private
bridge that never touches the home network. Includes every gotcha I hit — no DHCP, an
IP-range mismatch, and a noVNC console that fought back — plus the nmap scan that proved
it all works.

What this build is

The goal was a safe place to practice offensive tooling: an attacker box (Kali) and a deliberately vulnerable target (Metasploitable 2) that can freely attack each other but cannot reach my home network or the internet. The twist is that it runs nested three layers deep:

My desktop (Debian, ASUS)
└── VMware Workstation
    └── Proxmox VE 9.2.2   (host "promox", 192.168.95.134, reached via VMware NAT)
        ├── vmbr0  → management bridge, talks to VMware NAT / outside world
        └── vmbr1  → ISOLATED lab bridge, no uplink, no DHCP
            ├── VM 100  metasploitable2   (192.168.100.10)
            └── VM 101  kali-linux        (192.168.100.3)

The single most important design decision: both lab VMs live on vmbr1, a bridge with no uplink. That is what keeps Metasploitable — a machine built to be broken into — from ever being reachable from anything I care about.


Prerequisites (what already existed before this session)

  • VMware Workstation on the desktop, with a Proxmox VE guest already installed.
  • Inside Proxmox: two bridges, vmbr0 (management) and vmbr1 (isolated), and two VMs, 100 (metasploitable2) and 101 (kali-linux), both attached to vmbr1.
  • Proxmox root password known. Kali user ny2honolulu with its own password. Metasploitable default login msfadmin / msfadmin.

If you’re rebuilding and the VMs or vmbr1 don’t exist yet, create vmbr1 under Datacenter → promox → System → Network as a Linux Bridge with no bridge port (that “no port” is what makes it isolated), then set each VM’s network device to bridge=vmbr1.


The rebuild, clean version

This is the path I should have taken in order, with the dead ends removed. The messy reality is in the “Gotchas” section below.

1. Reach Proxmox

The VMware console only shows a login banner with the web address. Everything real happens over the network. From a desktop terminal:

bash

ssh root@192.168.95.134

First-ever connection asks to trust the host key — type yes. Then the root password. Prompt becomes root@promox:~#. (The web UI at https://192.168.95.134:8006 also works; accept the self-signed cert warning. But SSH gives working copy/paste, which the browser console does not.)

Read the prompt before every command. root@promox is the Proxmox host. It is not Metasploitable or Kali. Nearly every mistake in this build came from typing the right command into the wrong machine.

2. Confirm isolation before powering anything on

bash

qm config 100 | grep net
qm config 101 | grep net

Both must show bridge=vmbr1. If either says vmbr0, stop and fix it — that would put a vulnerable box on the network-facing bridge. Expected:

net0: virtio=BC:24:11:20:98:E8,bridge=vmbr1     # VM 100
net0: virtio=BC:24:11:3C:E9:FE,bridge=vmbr1     # VM 101

3. Start both VMs

bash

qm start 100
qm status 100      # want: status: running
qm start 101
qm status 101      # want: status: running

No output from qm start = success. qm start returns immediately; the machine being running and being ready to log into are two different things (it can take a minute).

4. Give each VM a static IP on the same subnet

This is the step that has no shortcut, because vmbr1 has no DHCP server. Nothing hands out addresses, so each VM must be set by hand, and both must agree on the subnet.

Kali was already configured (statically, via NetworkManager) at 192.168.100.3/24, so the lab subnet is 192.168.100.0/24. Metasploitable therefore gets an address in the same range. Open the Metasploitable console, log in msfadmin/msfadmin, and:

bash

sudo ifconfig eth0 192.168.100.10 netmask 255.255.255.0 up
ifconfig eth0

Look for inet addr:192.168.100.10.

Confirm Kali’s address from its terminal (it should already be set):

bash

ip a          # eth0 should show: inet 192.168.100.3/24 ... valid_lft forever

valid_lft forever = a permanent, hand-set address (a DHCP lease would count down). If Kali ever comes up without an address, add one:

bash

sudo ip addr add 10.10.10.20/24 dev eth0     # example only — match whatever subnet you use

Pick the subnet first, then make both machines match it. My original mistake was setting Metasploitable to 10.10.10.10 while Kali sat on 192.168.100.3. Same switch, different networks — so they couldn’t talk even though the cable was fine.

5. Prove the network works

From Kali:

bash

ping -c 4 192.168.100.10

Success looks like:

64 bytes from 192.168.100.10: icmp_seq=1 ttl=64 time=12.0 ms
64 bytes from 192.168.100.10: icmp_seq=2 ttl=64 time=1.59 ms
...
4 packets transmitted, 4 received, 0% packet loss
  • ttl=64 unchanged → same local network, no router in between.
  • First ping slow (~12 ms), rest fast (~1 ms) → the first one waited on an ARP lookup (“who has 192.168.100.10?”); Kali cached the MAC, so the rest were instant. (ARP is the exact protocol behind Black Hat Python’s ARP-cache-poisoning chapter.)

6. Scan the target

From Kali:

bash

sudo nmap -sV 192.168.100.10
  • -sV = service/version detection: nmap talks to each open port to name the software and its version (the version is what you look up for known vulns).
  • Save a baseline copy for later comparison:

bash

sudo nmap -sV -oN ~/metasploitable_baseline.txt 192.168.100.10

-oN writes normal-format output to a file while still printing to screen.


Gotchas I actually hit (so future-me can skip the pain)

Promiscuous-mode hint at Proxmox boot. VMware pops “…attempted to enable promiscuous mode on adapter ‘Ethernet0’. This is not allowed…”. Harmless for this lab — the lab bridge vmbr1 lives entirely inside Proxmox and never asks VMware for promiscuous mode. It only matters if you want Proxmox guests on vmbr0 to sniff the VMware network, which we don’t. Click OK.

“recovering journal” at Proxmox boot. Means the previous shutdown wasn’t clean. It self-repaired and reported clean. Avoid it by shutting down properly (see the shutdown section), not by pulling the virtual plug.

Web UI middle column was blank. The Proxmox web interface sometimes didn’t draw the options panel (Summary / Shell / Console). Fixes that worked: hard refresh with Ctrl+F5, reset zoom with Ctrl+0. When it stayed broken, I just used SSH instead — more reliable anyway.

ifconfig: command not found on Proxmox. Modern Debian dropped ifconfig; use ip a. Metasploitable (Ubuntu 8.04, from 2008) still has ifconfig. Same task, different tool, because the two machines are from different eras.

No IPv4 on Metasploitable at first. eth0 had only an IPv6 fe80:: address and its TX packets counter kept climbing — it was firing DHCP requests into the void. Root cause: no DHCP on vmbr1. Fix: the static-IP step above. (This is not a broken virtio driver — the driver loaded fine; nobody answered the DHCP request.)

Kali booted to the SeaBIOS boot menu instead of logging in. A stray keypress during startup dropped it into the BIOS “Select boot device” menu. Choose the hard disk option (2. lsi ... QEMU HARDDISK), not the DVD/CD (that would boot the installer ISO). Worth removing the installer ISO from the virtual DVD drive later so it can’t boot by accident:

bash

qm config 101 | grep -E "boot|ide2|sata"      # look for a leftover .iso

Very slow Kali boot. Nested virtualization is slow, and it gets much slower if the CPU virtualization feature isn’t passed through. Sanity checks, run on the Proxmox host:

bash

ls -l /dev/kvm                                  # missing = software emulation = very slow
qm status 101 --verbose | grep -E "^cpu|^mem|^maxmem|uptime"

If /dev/kvm is missing, the fix is on the VMware side: enable “Virtualize Intel VT-x/EPT or AMD-V/RVI” in the Proxmox VM’s VMware Processor settings.

noVNC console quirks. The browser console blanks after ~10 idle minutes (click in it + press Enter to wake — you’re still logged in). It needs a click inside the black area to take keystrokes; a hollow cursor block means the terminal doesn’t have focus. Maximizing made it blink (a resize loop between noVNC and Kali) — fix by leaving it un-maximized, or open the noVNC sidebar → gear → set Scaling mode = Local scaling. If it stops responding entirely, F5 reloads the console without touching the VM.

sudo “Sorry, try again” then silence. A mistyped password gets one retry. After a correct one, sudo remembers it for ~15 minutes, so repeat commands run silently with no prompt. No output = success. And never type a password at a normal $ prompt — it lands in ~/.bash_history in plaintext (a thing attackers grep for after a break-in — including on this very box later).


The payoff: the scan

sudo nmap -sV 192.168.100.10 → 1 host up, 23 open ports, scanned in 147 seconds. Confirmed the right target via MAC Address: BC:24:11:20:98:E8 (Proxmox Server Solutions GmbH) and Service Info: Hosts: metasploitable.localdomain.

PortServiceVersionWhy it matters
21/tcpftpvsftpd 2.3.4Backdoored build — :) username opens a root shell on 6200. The classic first exploit.
22/tcpsshOpenSSH 4.7p1Ancient; weak-cred / brute-force practice.
23/tcptelnetLinux telnetdPlaintext login — perfect sniffer capture target.
25/tcpsmtpPostfix smtpdMail enumeration.
53/tcpdomainISC BIND 9.4.2DNS practice.
80/tcphttpApache 2.2.8 (DAV/2)Vulnerable web apps (DVWA, Mutillidae).
111/tcprpcbindRPC #100000Feeds NFS below.
139,445/tcpnetbios-ssnSamba smbd 3.xSamba usermap exploit → root.
512,513,514exec/login/shellr-servicesTrust-based remote exec, no real auth.
1099/tcpjava-rmiGNU Classpath grmiregistryJava RMI exploit.
1524/tcpbindshellMetasploitable root shellA root shell already listening — nc 192.168.100.10 1524, no exploit needed.
2049/tcpnfsRPC #100003Misconfigured NFS exposes the filesystem.
2121/tcpftpProFTPD 1.3.1Second FTP daemon.
3306/tcpmysqlMySQL 5.0.51aBlank/weak root.
5432/tcppostgresqlPostgreSQL 8.3Default creds.
5900/tcpvncVNC (protocol 3.3)Weak VNC password.
6000/tcpX11(access denied)X server exposure.
6667/tcpircUnrealIRCdBackdoored build → RCE.
8009/tcpajp13—Tomcat AJP connector.
8180/tcphttpApache Tomcat/Coyote JSP 1.1Tomcat manager, weak creds.

Not shown: 977 closed tcp ports (reset) = nmap probed 1000 common ports; 977 said “nothing here” with a TCP reset. Real hardening is about making that open list short. Metasploitable’s is deliberately enormous — this table is basically a picture of “allow all inbound” from the attacker’s side.

Where to go next (in rough order of ease):

  1. 1524 root bindshell — nc 192.168.100.10 1524. Instant root, zero exploit. Best first demo of “what an open backdoor is.”
  2. 21 vsftpd 2.3.4 backdoor (CVE-2011-2523) — the canonical teaching exploit.
  3. 80 / 8180 web apps — browse http://192.168.100.10 from Kali.
  4. 23 telnet — capture the plaintext login with a sniffer (Black Hat Python).

Clean shutdown (do this, don’t pull the plug)

From the Proxmox SSH session:

bash

qm shutdown 100
qm shutdown 101

qm shutdown asks each OS to power off properly (avoids the “recovering journal” repair). qm stop is the force-kill — save it for a frozen VM. A hard reset also wipes any hand-set (non-persistent) IP, so shut down cleanly to keep tomorrow’s setup shorter.


Study connections

Black Hat Python. This range is now a live playground for most of the book:

  • TCP client (Ch. 2) → connect to the 1524 root shell.
  • TCP proxy → sit between Kali and Metasploitable’s FTP/HTTP and read raw bytes.
  • Sniffer + Scapy → capture the telnet (23) login in cleartext. Log in over telnet from a second terminal while the sniffer runs and watch the password cross the wire.
  • ARP-cache poisoning → the slow-first-ping you saw is ARP; the attack forges ARP replies. An isolated network is exactly where that’s safe to try.

AWS DVA-C02.

  • vmbr1 (no address, no DHCP, no uplink) ≈ a VPC subnet with no route to an internet gateway: instances reach each other and nothing else.
  • Setting static IPs by hand ≈ what AWS DHCP does automatically inside a VPC. 192.168.100.0/24 is exactly the kind of private CIDR you’d give a subnet.
  • The 23-open-ports scan is the argument for security groups: default-deny, open only what’s needed. A well-configured EC2 host should scan almost empty.
  • AWS forbids promiscuous mode outright; the equivalent capability is VPC Traffic Mirroring. Different worlds: the home lab lets you choose; AWS says no.

References / further reading