Step 114. Reconnaissance 2: Vulnerability Scanners and Reading Their Results — A Scanner Is a Candidate Generator

Step 114. Reconnaissance 2: Vulnerability Scanners and Reading Their Results — A Scanner Is a Candidate Generator

Level 2 — Introduction to Security and the Basics of Attack Skills | Difficulty ★★★☆☆ | Estimated time: 3 hours

Prerequisites: you’ve built the target’s service inventory (the port/service/version table) in Step 113. You can use nmap’s basic options (-sV, --script). A Linux terminal (including WSL) is ready.

  • What you need: a Linux terminal and nmap. Today’s scan targets are strictly 127.0.0.1 (your own computer) and, if you have the lab, Metasploitable2 (hereafter MS2).
  • Caution: ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime. Every hands-on measurement in this chapter was performed against 127.0.0.1 only, on WSL Linux (Nmap 7.94SVN). MS2-targeted output and commercial scanner screens are marked as "Screen example."

In Step 113 we drew a "map" of the target — which ports are running which services at which versions. Today we handle the tool that automatically stamps "this is a candidate for a breach" marks onto that map: the vulnerability scanner. Scanners are convenient, but they also lie. So today’s real goal is not how to "run" a scanner — it is how to read a scanner’s claims critically.


1. Learning Objectives

By the end of this chapter, you will be able to:

  • Automatically collect vulnerability candidates with nmap’s vuln script category
  • Extract CVE numbers from scanner output and organize them into a list
  • Explain why false positives occur and cross-validate scan results against version information
  • Follow a workflow that treats the scanner not as a "conclusion" but as a "candidate generator"
  • Narrow the port and script scope to manage time when a scan is slow

2. Background Knowledge — Today’s Tools and Concepts

Today’s Tools at a Glance

Category Details
Language/environment Linux terminal (WSL or lab VM), nmap 7.94’s NSE scripts
Today’s commands nmap --script vuln, --script "http-vuln-*" (narrowing scope), grep CVE resultsfile, --script-help
Concepts needed CVE numbers, false positives and false negatives, Step 113’s service inventory
Today’s artifact A vulnerability candidate list + a confidence judgment for each candidate (high / needs verification)

2-1. What a Vulnerability Scanner Does

A vulnerability scanner is the final process of reconnaissance. After identifying services and versions, it checks a database for "this version has these known vulnerabilities" and pulls out candidates. There are three representative kinds.

  • nmap’s NSE vuln category: vulnerability check scripts built into the nmap we already know. Today’s measurement environment (nmap 7.94SVN on WSL) had 50 built-in scripts with vuln in the name.
  • Dedicated scanners like Nessus / OpenVAS: commercial and open-source scanners with tens of thousands of checks. They produce pretty reports from a web interface, at the cost of heavy installation and licensing. Today we only look at them as screen examples.
  • Scripts tied to external APIs like vulners: they query an external vulnerability DB with the versions they found. The weakness is that they need the internet — today’s measurement exposes this weakness plainly.

2-2. False Positives and False Negatives — A Scanner’s Two Lies

A scanner’s judgment is mostly "version-number comparison." Two kinds of errors arise from this.

  • False positive (says vulnerable when it isn’t): distributions (like Ubuntu) often fix a vulnerability by porting just the patch backward (backporting) without raising the version number. The scanner sees only the version and says "vulnerable," but it’s actually already fixed. Conversely, there are also cases where the configuration differs so the attack conditions don’t hold.
  • False negative (misses what’s there): new vulnerabilities not yet in the scanner’s DB, services hiding on nonstandard ports, and services that hide their banners don’t get caught. "The scanner said it’s clean" does not mean "it’s safe."

That’s why a scanner’s output is not a conclusion but a candidate list. Whether something is actually breachable must be confirmed in the next stage (manual verification, exploitation), and that confirmation begins in Step 117.

2-3. The Procedure for Reading Results

In professional reconnaissance, the order for processing scanner results is fixed.

  1. Run the scan and save the results to an -oN file
  2. Extract only the CVE numbers from the output into a list (grep)
  3. Cross-check against Step 113’s inventory (services and versions) — is the version the scanner claims the same as the version I verified myself?
  4. Attach a confidence level to each candidate — "high (version match + attack conditions hold)" or "needs verification (version is the only basis)"
  5. Set verification priorities and hand off to the next stage

3. Follow Along

3-1. Preparation — A Practice Service and a Look at the Built-in Scripts

As in Step 81, scanning an empty computer teaches nothing, so let’s open a practice web server.

Input (terminal 1)

mkdir -p /tmp/steplab && cd /tmp/steplab
echo "<h1>hello plain world</h1>" > index.html
python3 -m http.server 8000

Don’t close this window for the rest of the exercises. In another terminal, let’s count how many of today’s protagonists — the vuln scripts — exist.

Input (terminal 2)

ls /usr/share/nmap/scripts/ | grep -c vuln

Output (measured 2026-09-09):

50

How to read it: this nmap has 50 scripts at the vuln category level. They’re divided by service — HTTP (http-vuln-*), SMB (smb-vuln-*), SMTP (smtp-vuln-*), and so on. Running all 50 at once is what --script vuln does — and you’ll soon learn with your body why that’s a problem.

Why: knowing the tool’s scale is the starting point of time management. Running "all 50 against every service on the target" is heavier than it sounds.

3-2. A Vulnerability Scan with Narrowed Scope

To get a feel for it, let’s pick only the HTTP-service vulnerability scripts (http-vuln-*) and run them against port 8000.

Input

nmap --script "http-vuln-*" -sV -p 8000 127.0.0.1

Output (measured 2026-09-09):

Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-09-09 15:12 KST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00014s latency).

PORT     STATE SERVICE VERSION
8000/tcp open  http    SimpleHTTPServer 0.6 (Python 3.12.3)
|_http-server-header: SimpleHTTP/0.6 Python/3.12.3

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.30 seconds

How to read it: more than 20 http-vuln-* scripts all ran (6.30 seconds), yet not a single script reported an additional vulnerability. The |_http-server-header line is not a vulnerability report but a banner reconfirmation — this script belongs to the version category, so it tags along automatically when -sV is on (verified by measurement: the script file’s categories = {"version"}). Python’s simple server is a clean target that matches none of the CVEs the scanner knows.

Why: first get the feel that "the scanner saying nothing" is also a normal result. Only then does the contrast with the next step’s "chatty result" register as extraordinary.

3-3. The Whole vuln Category — And the Time Bomb

Now let’s run the entire category. But first, make a prediction. If you run 50 scripts against a single port 8000, how long will it take?

Input

time nmap --script "vuln and not vulners" -p 8000 127.0.0.1 -oN /tmp/steplab/vuln_all.txt

Output (measured 2026-09-09 — run with a 200-second limit, aborted on timeout):

real    3m20.003s   ← after 200 seconds, timeout forcibly terminated the scan

The scan never finished, and the results file was empty (nmap writes the file only when a scan completes). What ate the time? Let’s interrogate the suspect with --script-help.

Input

nmap --script-help http-slowloris-check

Output (measured 2026-09-09, excerpt):

http-slowloris-check
Categories: vuln safe
  Tests a web server for vulnerability to the Slowloris DoS attack without
  actually launching a DoS attack.
  ...
  The script opens two connections to the server, each without the final CRLF.
  After 10 seconds, second connection sends additional header. Both connections
  then wait for server timeout.

How to read it: because this script measures "how long the server holds onto incomplete connections," by design it waits until the server’s timeout expires. The check itself is built to consume time. With scripts like this mixed in, running the whole vuln category can take minutes even on a single port.

Why: in real work, "the scan won’t finish" is an accident that blows up the day’s schedule. This is why assignment guides warn that "vuln scripts can take 30+ minutes — narrow ports with -p." Now that we know the culprit, let’s exclude the slow scripts and run again.

3-4. Finishing the Run Without the Slow Scripts

Input

time nmap --script "vuln and not vulners and not http-slowloris-*" -sV -p 8000 127.0.0.1 -oN /tmp/steplab/vuln_fast.txt

Output (measured 2026-09-09):

PORT     STATE SERVICE VERSION
8000/tcp open  http    SimpleHTTPServer 0.6 (Python 3.12.3)
|_http-csrf: Couldn't find any CSRF vulnerabilities.
|_http-dombased-xss: Couldn't find any DOM based XSS.
|_http-stored-xss: Couldn't find any stored XSS vulnerabilities.
|_http-server-header: SimpleHTTP/0.6 Python/3.12.3

Nmap done: 1 IP address (1 host up) scanned in 79.29 seconds

How to read it: it finished in 79 seconds, and this time three scripts politely reported "Couldn’t find any." How you read this output matters — "Couldn’t find any XSS" does not mean "there is no XSS"; it means "this script’s checking method did not discover one." The possibility of a false negative is written right into the sentence.

Why: a scanner’s negative result is not proof of a negative. Reading the tone of this one line is half of the critical reading skill we learn today.

3-5. The Trap of External-API Scripts — vulners

The vuln category also contains the vulners script, which queries an external vulnerability DB (vulners.com) with the versions it finds. What happens if you run it in an isolated lab?

Input

nmap --script vulners -sV -p 8000 127.0.0.1

Output (measured 2026-09-09):

PORT     STATE SERVICE VERSION
8000/tcp open  http    SimpleHTTPServer 0.6 (Python 3.12.3)
|_http-server-header: SimpleHTTP/0.6 Python/3.12.3

Nmap done: 1 IP address (1 host up) scanned in 6.70 seconds

How to read it: no report at all. Not even an error message. Unable to reach the external DB, it quietly gave up. A fine example that "no output" can mean not "no vulnerabilities" but "check impossible."

Why: when a scanner is silent, you must always distinguish "it’s clean" from "it couldn’t run." In an environment with a blocked network, like today’s measurement, the latter is the correct answer.

3-6. Extracting CVEs from Results — Against MS2 (Screen Example)

Let’s see how the results change when we run the same scan against the lab’s MS2 (Metasploitable2). MS2 is a practice virtual machine made deliberately vulnerable, so output like the following is normal (this environment has no MS2, so this is marked as a Screen example).

Input (in the lab, assuming MS2’s IP is 192.168.56.101)

nmap --script vuln -sV 192.168.56.101 -oN ms2_vuln.txt

Screen example (excerpt):

21/tcp   open  ftp     vsftpd 2.3.4
|_smtp-vuln-cve2010-4344: ...
| sslv2-drown:
|   ciphers: ...
| vulners:
|   cpe:/a:vsftpd:vsftpd:2.3.4:
|       CVE-2011-2523  10.0    https://vulners.com/cve/CVE-2011-2523
...
|_ssl-ccs-injection: VULNERABLE: ...

Input — extract only the CVE numbers from the results file into a list:

grep -oE 'CVE-[0-9]{4}-[0-9]+' ms2_vuln.txt | sort -u

Screen example:

CVE-2004-2687
CVE-2011-2523
CVE-2015-3306
...

How to read it: for an old system like MS2, dozens of results are normal (consistent with the assignment guide’s warning). grep -oE ... | sort -u is the professional technique for compressing scan results into a "CVE candidate list." Against our environment’s practice-server results file, this command returned 0 hits (measured 2026-09-09) — remember the contrast between a clean target and a vulnerable target.

Why: raw scanner output can’t go into a report. Only with this step of summarizing into a CVE list can you move to the next stage (reading each CVE in the NVD, in Step 115).

3-7. Cross-Validation — Do We Believe the Scanner?

The final process is checking the scanner’s claims against information you gathered yourself. The procedure goes like this (lab basis).

  1. If the scanner said "vsftpd 2.3.4 has CVE-2011-2523," check whether the version you verified yourself with -sV in Step 113 really is 2.3.4 — banner manipulation might have fooled it.
  2. Attach a confidence level to each candidate. For example: "CVE-2011-2523 — version match, public exploit exists → high confidence," "CVE-2004-2687 — version is the only basis → needs verification."
  3. This list becomes the raw material for choosing actual exploits in Steps 116–117.

Why: building the doubt list — "of the things the scanner called vulnerable, which actually break?" — is today’s completion criterion and the habit that decides the quality of an entire penetration test.


4. Missions & Exercises

Mission — A Vulnerability Candidate List with Confidence Judgments

  1. With the practice server from 3-1 running, execute nmap --script "vuln and not vulners and not http-slowloris-*" -sV -p 8000 127.0.0.1 -oN vuln_fast.txt and record the elapsed time.
  2. Count how many scripts in the results file reported "Couldn’t find."
  3. If your lab has MS2, run nmap --script vuln -sV <MS2_IP> -oN ms2_vuln.txt (it takes time — you may narrow with -p), and build a CVE candidate list with grep -oE 'CVE-[0-9]{4}-[0-9]+' ms2_vuln.txt | sort -u.
  4. Pick 3 interesting CVEs from the list and organize, for each, "the basis the scanner presented (version? response test?)" and "whether it matches the Step 113 inventory" in a table.
  5. Attach a confidence level (high / needs verification) to each candidate, with a one-line rationale for each judgment.

Exercises

Exercise 1. Name two representative reasons false positives occur.

Exercise 2. When a scan result says Couldn't find any stored XSS vulnerabilities, explain the difference between what this sentence guarantees and what it does not.

Exercise 3. Using one script we examined today as evidence, explain why running the whole --script vuln category can take minutes even on a single port.

Exercise 4. When a scanner is silent (no reports at all), name two possible interpretations and what you would check to tell them apart.


5. Model Answers & Completion Criteria

Mission Model Answer

Items 1–2 are confirmed directly by measurement. In the 2026-09-09 measurement environment, the scan took 79.29 seconds, and there were three "Couldn’t find" reports (CSRF, DOM XSS, stored XSS). In your environment the numbers may differ depending on the open services.

The CVE extraction command for item 3 is as confirmed in the Screen example:

grep -oE 'CVE-[0-9]{4}-[0-9]+' ms2_vuln.txt | sort -u

For items 4–5, a table like this is sufficient (MS2-based example):

CVE Scanner’s basis Matches inventory? Confidence One-line rationale
CVE-2011-2523 vsftpd 2.3.4 version match Match (verified with -sV on port 21) High Version match + many public exploits
CVE-2004-2687 distccd version estimate Needs checking Needs verification No version in the banner — only an estimate
CVE-2015-3306 Assumes mod_copy module present Needs checking Needs verification Whether the module is actually loaded is unconfirmed

How to verify: ① Does the results file contain the scan date/time and full command (-oN‘s automatic record)? ② Is the CVE list deduplicated with sort -u? ③ Does "version match or not" enter the confidence judgment as a basis — if everything is "high" with no grounds, do it again.

Exercise Answers

Answer 1. First, distribution backport patches — the vulnerability is fixed but the version number stays the same, so the scanner classifies it as "vulnerable." Second, attack conditions not holding — the version is indeed vulnerable, but the relevant feature (module/setting) is disabled, so the attack doesn’t actually work.

Answer 2. What it guarantees: the fact that "this script’s checking method (signatures, test payloads) found nothing." What it does not guarantee: that XSS actually doesn’t exist. Other input paths, other payloads, and pages behind authentication are outside this check. A negative result is not proof of a negative.

Answer 3. A script like http-slowloris-check must measure "how long the server holds incomplete connections," so by design it waits for the server’s timeout (--script-help measurement: "Both connections then wait for server timeout"). With scripts like this mixed into the category, a full run stretches to minutes — in the 2026-09-09 measurement it hit the 200-second limit and was aborted, while excluding it finished in 79 seconds.

Answer 4. One interpretation is "it’s truly clean"; the other is "the checks didn’t run." Today’s vulners script ending silently after failing to reach the external DB (6.70 seconds, no output — measured) is an example of the latter. How to tell them apart: check whether the scan completed normally (time, the Nmap done line), whether the scripts actually ran (--script-trace, or the required conditions in each script’s help), and whether the script needs external resources.

Completion Criteria Checklist

  • [ ] I can explain the difference (scope and time) between --script vuln and --script "http-vuln-*"
  • [ ] I can narrow scope with a script selection expression (and not ...) when a scan is slow
  • [ ] I extracted CVE numbers from a results file into a deduplicated list
  • [ ] I can distinguish and explain false positives and false negatives
  • [ ] I can accurately interpret the meaning of a "Couldn’t find" sentence
  • [ ] I can state the procedure for cross-validating scanner results against the Step 113 inventory
  • [ ] Mission: I completed the confidence-judgment table for 3 candidates

6. Common Pitfalls & Fixes

Wall 1. The vuln scan never finishes

Symptom (measured 2026-09-09): checking a single port with --script "vuln and not vulners" hadn’t completed after 200 seconds, so timeout forcibly terminated it, and the -oN results file was empty.

Cause: scripts like http-slowloris-check, whose check itself waits for the server timeout, are mixed into the category. Because nmap writes the file only when the scan finishes, aborting loses the record too.
Fix: exclude slow scripts with a selection expression — --script "vuln and not vulners and not http-slowloris-*" (measured: finished in 79 seconds). In real work, narrow the port range (-p) as well.

Wall 2. The vulners script says nothing at all

Symptom (measured 2026-09-09): running --script vulners -sV finished quietly in 6.70 seconds without a single line of vulnerability reports.

Cause: this script queries an external API (vulners.com) with versions. In a lab without internet or on a closed network, the check itself is impossible, and it moves on silently without an error.
Fix: don’t misread "silence = clean." Check with --script-help whether the script needs external resources, and if you’re in a blocked environment, substitute another means of checking (an offline NVD DB, searchsploit — Step 115).

Wall 3. The scanner dumps dozens of results and I don’t know where to start

Symptom (Screen example, MS2 target): dozens of CVEs appear.

Cause: MS2 is a system made deliberately vulnerable, so this is normal. The problem isn’t the scan — it’s the absence of priorities.
Fix: ① turn them into a list with grep -oE 'CVE-[0-9]{4}-[0-9]+' file | sort -u, ② cross-check versions against the Step 113 inventory, ③ sort by "is there a public exploit?" After these three filters, the candidates worth verifying fit on one hand.

Wall 4. The version looks different between the scanner and the inventory

Symptom: -sV said vsftpd 2.3.4, but another script based its judgment on a different version.

Cause: each script estimates versions through a different path (banner, response patterns, heuristics). A banner can also be changed by an administrator.
Fix: take the value you verified yourself (the -sV banner) as the standard, mark the scanner’s estimate as "weak basis," and lower its confidence. This is why cross-validation exists.

Wall 5. I attach the scanner report to my report as-is

Symptom: you paste the raw Nessus/nmap output wholesale into the results report.

Cause: scanner output is a "candidate," not a "conclusion." If false positives are reported as-is, you’ll have someone applying the wrong patches.
Fix: process in the order candidate list → cross-validation → confidence notation → manual verification, then report. Today’s mission table is that processing format.


7. Summary

Today’s Concepts

Concept One-line explanation
Vulnerability scanner A tool that checks versions and responses against a DB and automatically pulls out vulnerability "candidates"
False positive Says vulnerable when it isn’t — backport patches and unmet conditions are the main causes
False negative Misses what’s there — vulnerabilities outside the DB, hidden services
Reading a negative result "Couldn’t find" means not "there is none" but "this method didn’t find one"
Cross-validation The essential process of checking the scanner’s claims against your self-gathered inventory
Candidate generator The scanner’s proper place — the conclusion is drawn by a human’s verification

Today’s Commands

Command What it does
ls /usr/share/nmap/scripts/ | grep -c vuln Count built-in vuln scripts (measured: 50)
nmap --script "http-vuln-*" -sV -p port target Scan with only the HTTP vulnerability scripts
nmap --script "vuln and not vulners and not http-slowloris-*" ... Category scan excluding slow scripts
grep -oE 'CVE-[0-9]{4}-[0-9]+' resultsfile | sort -u Compress scan results into a CVE candidate list
nmap --script-help scriptname Check a script’s behavior, duration, and requirements
nmap ... -oN file A reproducible scan record with the full command written in

An Instinct More Important Than Commands

A vulnerability scanner is a diligent but thoughtless assistant. It dumps dozens of candidates based on version numbers alone; when it can’t find something it says only "couldn’t find," never "there is none"; and when the external DB is blocked, it goes quiet without even an error. The skill of using this assistant lies not in the run command but in the reading — the habit of asking "what is the basis of this report, and does it match what I verified myself?"

Remember the contrast today’s measurements showed. Against the clean practice server, 20 scripts finished in 6 seconds without a word, while one slow script held an entire scan hostage past 200 seconds. And the CVE numbers the scanner pulls out are not the end — they’re the beginning. The reading that deciphers each number in the NVD and Exploit-DB is what completes reconnaissance, and that way of reading is the subject of Step 115.


Once every box is checked, Step 114 is complete. Click the checkbox in the sidebar to save your progress.