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.

6 min read
ibrahimsql
1,040 words

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

Bash
$ 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 file

The 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:

Bash
$ 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:

  1. Express answered 20 repeated overlapping ranges with an unobjecting 206 — the script's trigger condition genuinely occurred.
  2. There's no Server header, but X-Powered-By: Express is right there. This isn't Apache.
  3. 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:

  1. What is the scanner actually checking? (--script-help <name>, or the Nessus plugin detail page, describes the check's logic.)
  2. Can I reproduce that exact check manually? As done above — send the same request by hand, read the response yourself.
  3. 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.

---
Share this post:

What do you think?

React to show your appreciation

Related Posts