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

2.2 KiB
Raw Blame History

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

// 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×