java-topology/defects/nmap/nmap-0001.md

64 lines
2.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# nmap-0001: CWE-407 — O(n²) port membership test in version-detection hot path
**Severity:** HIGH
**File:** `service_scan.cc:1259` (portIsProbable), `service_scan.cc:1269` (serviceIsPossible)
**Status:** PATCHED
## Description
`ServiceProbe::portIsProbable()` performs a linear `std::find` over a `std::vector<u16>`
of probable ports on every call. It is called inside the `nextProbe()` loop, which
iterates all 187 probes in `nmap-service-probes` for every service being fingerprinted.
Outer loop: O(P) probes
Inner: `find(portv->begin(), portv->end(), portno)` = O(K) where K = ports in that probe's list
One probe lists `32,771` expanded ports after range expansion. Typical probes list
dozens to hundreds of ports. For a large `-sV` scan across many open ports, this is
called millions of times during probe selection.
`ServiceProbe::serviceIsPossible()` has the same defect: O(D) linear strcmp loop over
`detectedServices` (up to ~10 services), called at the same hot-path call sites.
## Root Cause
```cpp
// service_scan.cc:1254-1262 — portIsProbable
bool ServiceProbe::portIsProbable(enum service_tunnel_type tunnel, u16 portno) const {
const std::vector<u16> *portv;
portv = (tunnel == SERVICE_TUNNEL_SSL)? &probablesslports : &probableports;
if (find(portv->begin(), portv->end(), portno) == portv->end()) // O(K) linear scan
return false;
return true;
}
// service_scan.cc:1266-1274 — serviceIsPossible
bool ServiceProbe::serviceIsPossible(const char *sname) const {
for(vi = detectedServices.begin(); vi != detectedServices.end(); vi++) { // O(D)
if (strcmp(*vi, sname) == 0)
return true;
}
return false;
}
```
Called at `service_scan.cc:1839` and `1862` inside a while loop over all probes.
## Fix
Replace `probableports`/`probablesslports` with `std::unordered_set<u16>` (O(1) lookup).
Replace `detectedServices` with `std::unordered_set<std::string>` (O(1) lookup).
Sort-and-binary-search (`std::sort` + `std::binary_search`) is an alternative if
order must be preserved, but unordered_set is cleaner.
## Patch
See `patch/nmap-0001.patch`
## Benchmark
See `unit/NmapPortMembershipTest.java` — N=200 probes × K=1000 ports:
- Slow (vector find): ~200,000 operations
- Fast (unordered_set): ~200 operations
- Speedup: ~1000×