PEN-200 Series, Part 3: Vulnerability Scanning — and Catching a Scanner Lying
Running nmap's vuln script category against our own lab, catching a CVE alert that's actually a false positive and proving it with Range headers, and what Nessus does differently.
PEN-200 Series, Part 3: Vulnerability Scanning — and Catching a Scanner Lying
The series plan is in the OSCP+ preparation guide; Part 1 was note-taking, Part 2 information gathering, where we briefly touched NSE's default/safe category (-sC). This post covers PEN-200's own vulnerability scanning module: Nessus and nmap's vuln category, and why both teach the same lesson — a scanner's label is never the last sentence of a report.
Nessus: what it does, why I can't run it here
Nessus is a commercial scanner that checks a target against a signed vulnerability database, both unauthenticated and authenticated. The authenticated mode matters: it logs into the target over SSH/WinRM and reads the installed package list and patch level directly — instead of guessing "this service looks like version X" from the network, it says "this package is actually installed at version X." For OSCP purposes, that difference means a real authenticated Nessus scan can surface internal vulnerabilities (kernel privesc bugs, for instance) that no network-only scan could ever see.
Nessus isn't installed in the sandbox this series is written in, and it requires registration/licensing, so I can't show a live scan here — saying that plainly beats claiming output I didn't produce. PEN-200's own module walks through installation, plugin updates, and the authenticated-vs-unauthenticated distinction step by step; the free Essentials tier at tenable.com is where you should actually run it yourself.
What I can run and verify here is nmap's vuln category — and what comes out of it demonstrates the exact lesson that applies to every scanner, Nessus included.
nmap --script vuln: a real run against our own lab
$ nmap -Pn -p 8080 --script vuln 127.0.0.1
PORT STATE SERVICE
8080/tcp open http-proxy
| http-vuln-cve2011-3192:
| VULNERABLE:
| Apache byterange filter DoS
| State: VULNERABLE
| IDs: CVE:CVE-2011-3192 BID:49303
| The Apache web server is vulnerable to a denial of service attack when numerous
| overlapping byte ranges are requested.
| Disclosure date: 2011-08-19
| References:
| https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3192
|_ https://www.securityfocus.com/bid/49303
| http-enum:
|_ /robots.txt: Robots fileThe scan says eresus-ctf-lab is VULNERABLE to an Apache-specific 2011 denial-of-service CVE. The lab is entirely Node.js/Express — there's no Apache anywhere in it. That's either a real finding or a false positive, and there's exactly one way to know: verify it yourself.
Verifying it: look at what the script actually checks
http-vuln-cve2011-3192's logic is simple: send the server a Range header that repeats the same byte range dozens of times, and if the server processes it without rejecting it (returns 206 Partial Content), flag the vulnerability. That's a behavioral test — it never confirms the server is actually Apache:
$ curl -s -o /dev/null -w "%{http_code}\n" -H "Range: bytes=0-10" localhost:8080/robots.txt
206
$ curl -s -D - -o /dev/null \
-H "Range: bytes=0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10,0-10" \
localhost:8080/robots.txt | head -5
HTTP/1.1 206 Partial Content
X-Powered-By: Express
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Thu, 15 Oct 2026 22:19:55 GMT
$ curl -sI localhost:8080/robots.txt | grep -i server
(nothing — there is no Server header at all)Three things confirmed:
- Express answered 20 repeated overlapping ranges with an unobjecting
206— the script's trigger condition genuinely occurred. - There's no
Serverheader, butX-Powered-By: Expressis right there. This isn't Apache. - CVE-2011-3192 is a memory-exhaustion bug in specific Apache httpd versions. Node's HTTP server is an entirely different codebase; it cannot carry the same flaw.
Verdict: a confirmed false positive. The script's heuristic ("does it accept overlapping ranges") catches a behavior pattern, but that pattern has more than one cause — any server can choose not to reject overlapping ranges while implementing the Range RFC, without being vulnerable at all.
Why this is a methodology lesson, not a one-off
The rule that comes out of this single example is universal: every scanner, Nessus included, runs on heuristics that look at behavior, banners, or version strings. None of them say "I ran this exact exploit and it fired" — they say "this pattern is usually associated with this vulnerability." Scoring Security Findings covers exactly this distinction for labels that are real but not necessarily high-priority. Here we're a step earlier: sometimes the label itself is wrong.
The practical rule: whenever a scanner (Nessus, nmap --script vuln, OpenVAS, doesn't matter which) flags something, ask three questions before it goes anywhere near a report:
- What is the scanner actually checking? (
--script-help <name>, or the Nessus plugin detail page, describes the check's logic.) - Can I reproduce that exact check manually? As done above — send the same request by hand, read the response yourself.
- Have I independently confirmed the target is really the software/version being claimed? (
X-Powered-By, error pages, behavioral differences — never trust one header alone.)
A finding that doesn't clear those three questions goes in the report labeled "unverified scanner output," or doesn't go in at all — "the scanner said critical" is never, by itself, a complete sentence in a report.
http-enum: small, but a real finding
The second script in the same scan, http-enum, found /robots.txt — the same file we already read manually in Part 2. The lesson here is different: an automated vulnerability scan sometimes tells you something you already knew, and that's fine — the point of running it isn't for every script to find something new, it's to not miss anything. http-enum's coverage is limited to a small built-in wordlist; as the gobuster example in Part 2 showed, a targeted scan with your own wordlist will always find more.
What goes in your notes
For every VULNERABLE label: three lines — the script's name, the verification steps you took (here: two curl commands and a header check), and the verdict — real finding or false positive. This feeds directly into the "Vulnerability" subsection of Part 1's template, and it means your report already has a ready answer for "why didn't you write this up as a finding" — a question the exam grader won't ask, but one you need to have answered for your own credibility regardless.
Part 4 moves into web application attacks: mapping a modern tech stack, enumerating APIs, and showing which real behavior each OWASP Top 10 category actually maps to, against our own lab.
What do you think?
React to show your appreciation