Nmap - detecting the network mapper

Detecting network scans

When we speak about detection, you can often hear “let’s detect attackers' scans”. I believe that sentence is thrown in order to detect intruders on early stages of an attack. However there are a few issues with this mindset as blindly detecting all types of scans made on a security perimeter will drive the SOC crazy with the amount of false positive and legit alerts generated. It will not improve the level of detection you have either because you will not be able to treat all the alerts and may miss the ones revealing the presence of intruders.

Imagine you have a house with a garden exposed on the street side (if you have it already, lucky you). Someone walks in the street and look at the inside of your garden over your wall. He did not go inside your home, nor did he do something illegal by watching your flowers. You are not going to report to the police all the people looking at your garden under the excuse that they could be potential thieves.

Now imagine somebody scanning ports of internet-exposed systems in your perimeter. Is this intruders? Is this scientists making statistics? Is this your network admin trying to debug? The answer to all of this is: it is too expensive to answer the question. Both in human resources and in pure budget. Scan detection on the Internet perimeter is a sub-optimization of your time as a blue teamer.

When is scan detection relevant then? Well, detecting scans from the Internet like the above example should only be a weak signal you can use in correlation once you detected some activity inside your perimeter. As in “we saw a webshell dropped from IP on the web server, let’s check if we have scans from in the security logs”. And that is if you are mature enough to tag the scans as “low” and only treat them if needed, have enough storage for all of the corresponding logs etc.

The real value of detecting scans is inside your perimeter. After the initial reconnaissance and first exploitation phase has been completed by an attacker. For example, when an attacker is looking to lateralize or gain access to more privileged machines on a network while he already compromised at least one system in it. He would then scan the potential other targets on the LAN. This gives real value to a scan alert because you know something is already wrong. Somebody is inside your garden and thus you are entitled to kick him out.

Detecting Nmap

One of the most used tools for network scanning is the network mapper: nmap. Nmap uses all sort of tricks on the network stack to determine if ports are open on a system, which protocol and software is running on the open port or if these are vulnerable to known exploits.

A good read before continuing is from the authors of Nmap on the detection of their own tool: https://nmap.org/book/nmap-defenses-detection.html.

Detecting the fingerprinting nmap does on several application-layer protocols is a good way to reveal attackers doing reconnaissance activity. Nmap uses recognizable strings and patterns for a lot of these protocols, the idea in this article is to detect a few of these. While this will not detect all scans made with the tool, the alerts it will provide will be trustworthy as the presented patterns are specific to nmap which will drastically reduce the number of false positives. Application-level scans are also often used by anyone trying to get insight on what is running on a port, which make it likely to occur with malicious intents.

It basically detects commands like:

$ nmap -sSVC
$ nmap -sS -script <application_script_name>

A lots of the patterns below where found on nmap’s github: nmap/nmap: Nmap - the Network Mapper. Github mirror of official SVN repository. It was then verified making pcaps of actual scans and studying the network traces.


Nmap sends particular DNS requests:

// https://github.com/nmap/nmap/blob/4b46fa7097673f157e7b93e72f0c8b3249c54b4c/scripts/dns-recursion.nse
transaction ID is 0xdead and request is www.wikipedia.org


Nmap sends probe for other protocols on port 21 as a first message, before the server sends its banner:

// DNS
request with transaction id 0x06 to domain "version.bind"

// RDP: https://github.com/nmap/nmap/blob/4b46fa7097673f157e7b93e72f0c8b3249c54b4c/nselib/rdp.lua

// SMB

// SIP

// etc. see above and below rules for other protocols

it also uses a specific password to authenticate:

// https://github.com/nmap/nmap/blob/4b46fa7097673f157e7b93e72f0c8b3249c54b4c/scripts/ftp-bounce.nse
// https://github.com/nmap/nmap/blob/4b46fa7097673f157e7b93e72f0c8b3249c54b4c/scripts/ftp-syst.nse
// https://github.com/nmap/nmap/blob/4b46fa7097673f157e7b93e72f0c8b3249c54b4c/scripts/ftp-anon.nse



Nmap has a specific User-Agent:

User-Agent: Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)

It requests resources with the following URIs:

# https://github.com/nmap/nmap/blob/4b46fa7097673f157e7b93e72f0c8b3249c54b4c/nselib/http.lua

# http-trane-info.nse

# Network equipment probes
# https://github.com/nmap/nmap/blob/210d3c29f47a42bbd3ca39bf7a35c2fb6faf9647/nselib/data/http-fingerprints.lua


Nmap uses a specific cookie (mstshash) value:

// https://github.com/nmap/nmap/blob/4b46fa7097673f157e7b93e72f0c8b3249c54b4c/nselib/rdp.lua


Nmap sends specific values for “From”, “To” and “Contact” headers:

// /usr/share/nmap/nmap-service-probes
From: <sip:nm@nm>
Contact: <sip:nm@nm>
To: <sip:nm2@nm2>


Nmap sends its name in the hostname field of the SESSION_SETUP_ANDX_REQUEST messages:



Nmap sends specific client protocol versions:


where <X> are version numbers.


Nmap sends a specific domain name in the SMTP EHLO command:

EHLO nmap.scanme.org

Bruteforce scripts

Numerous nmap bruteforce scripts uses “nmap” or a related string to probe for login/passwords:

/usr/share/nmap/scripts/mmouse-brute.nse:94:  local status = driver:login(nil, "nmap")
/usr/share/nmap/scripts/svn-brute.nse:50:  svn_client = "nmap-brute v0.1",
/usr/share/nmap/scripts/nessus-xmlrpc-brute.nse:103:  local status, response = authenticate(host, port, "nmap-ssl-test-probe", "nmap-ssl-test-probe")
/usr/share/nmap/scripts/nessus-xmlrpc-brute.nse:112:    status, response = authenticate(host, port, "nmap-ssl-test-probe", "nmap-ssl-test-probe")
/usr/share/nmap/scripts/socks-brute.nse:81:  local status, err = helper:authenticate({username="nmap", password="nmapbruteprobe"})
/usr/share/nmap/scripts/dpap-brute.nse:61:    local c = base64.enc("nmap:" .. password)


The drawback of the above is of course that this type of static signature detection is not resistant to a change in nmap’s code or scripts. That is inherent to this type of detection but it also makes the alerts trustworthy.

It is on purpose that comportemental scan detection was not quoted here as it requires way more maturity to be efficiently implemented.

To avoid generating numerous alerts, one can aggregate alerts made on the above signatures on a certain time range, such as 30 seconds to avoid spamming the SOC SIEM. Do not forget that only one of the detection probes is enough to say that nmap was used and that not all nmap scripts are used on every scan.

Happy hunting.