As in most penetration testing/red teaming companies we at Z-Labs use Nmap security scanner tool quite extensively. During one of our network penetration testing engagements we have noticed that UDP scanning (-sU) functionality misbehaves and does not correctly detect opened UDP ports. Soon after that we identified in Nmap (versions: 7.91 and 7.90) subtle implementation issue that resulted in corrupted UDP packets sent during a scan.

Before we jump into technical details of the bug that we have discovered it’s worth to mention how an UDP scanning is implemented in Nmap. So a main difference between TCP and UDP scanning is that the UDP scanning to trigger a response from a service it scans does need protocol specific payload (i.e. content of a SNMP packet looks completely different than a DHCP or DNS request packet). By design empty UDP packets do not elicit a response from a port that is scanned, so as a consequence Nmap is not able to differentiate port state between opened and filtered therefore reporting it as a open|filtered (see here for more details). To overcome this limitation and to significantly improve the effectiveness of UDP scanning (-sU), Nmap uses nmap-payloads that contains long list of UDP port specific packet payloads. Below payload for DNS specific ports (e.g. UDP 53) taken directly from nmap-payloads is shown:

udp 53,5353,26198 "\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00"
...

This way UDP scanning in Nmap is much more effective in detecting wide range of various UDP services. Unfortunately, while working on the Nmap script for detecting critical issues in VMWare ESXi servers we’ve noticed that most of UDP packets sent over the wire during Nmap -sU scan are corrupted thus making the whole scanning process mostly ineffective. After spending some time in the debugger to root cause this issue we concluded that a bug was introduced in a 9c83be38331adff3d23b154eadace9263edfab48 commit by mishandling memory management (accessing C++ STL vector that has already gone out of scope). Detailed issue analysis can be found together with our initial bug report: here and here. Interestingly this bug “survived” in Nmap source code unseen since September 2020.

After discovering it, Z-Labs proposed the patch which was already merged by Nmap Team. The patch boiled down to the following modifications (C++ reference to the STL vector was used instead of copying whole vector):

...
- std::vector<struct payload> portPayloadVector;
  std::vector<struct payload>::iterator portPayloadVectorIterator;
  proto_dport key(IPPROTO_UDP, dport);
  int portPayloadVectorSize;

  portPayloadIterator = portPayloads.find(key);

  if (portPayloadIterator != portPayloads.end()) {
-   portPayloadVector = portPayloads.find(key)->second;
+   std::vector<struct payload>& portPayloadVector = portPayloads.find(key)->second;
...

In August Nmap with fixed UDP scanning (-sU) functionality was finally released. Sadly it seems that severity of this issue is not a common knowledge among security professionals. So if you’re doing any penetration testing related job, be sure to use latest version of Nmap (i.e. v7.92) - as of time of writing this post even Kali Linux still ships with an old version.