Wednesday, May 05, 2010

UDP scan with ICMP port unreachable and scapy

As you probably now, scanning UDP opened ports is painful because UDP is not connected, so you cannot rely on TCP SYN/SYN-ACK to find opened ports.

The ultimate network scanner nmap knows how to perform an UDP scan:
$ nmap -sU -p1-65535 <target>
You can add useful options such as -sV (probe open ports to determine service/version info). The scan is much longer than a TCP scan, but sometimes it works. Yes, sometimes. Recently I started a simple UDP server (with socat) on a random port and challenged myself to find it within the whole 1-65535 range: I appeared to be unable to find it with nmap (I'm probably misusing nmap).

Let's go back to how UDP scanning works. The most common technique, well-described in nmap's Art of port Scanning, relies on ICMP port unreachable replies that the target may send after UDP packets sent to ports that are not opened. Thanks to these ICMP packets, we can deduce ports which can be opened. Note that the remote host has perfectly the right not to send these ICMP packets, so this technique could be useless in some situations (firewall?).

Let's see how ICMP port unreachable scanning works with this example: we try to send \n to UDP ports 1233,1234,1235 with socat, but only port 1234 is opened.
$ echo | socat - UDP:target:1233
socat[13786] E read(3, 0x80c3a68, 8192): Connection refused
$ echo | socat - UDP:target:1234
$ echo | socat - UDP:target:1235
socat[13792] E read(3, 0x80c3a68, 8192): Connection refused

Here is the tcpdump display explaining what happened (C=Client, S=Server):
1. IP C > S.1233: UDP, length 1
2. IP S > C      : ICMP A.B.C.D udp port 1233 unreachable, length 37
3. IP C > S.1234: UDP, length 1
4. IP C > S.1235: UDP, length 1
5. IP S > C      : ICMP A.B.C.D udp port 1235 unreachable, length 37

You can see that the server did not reply any ICMP port unreachable for port 1234, giving us a hint that this port could be opened. Nmap should report this port as opened|filtered.

Ok then, why not spamming the target with many UDP packets and just see what are the replies? Because there is a throttle: the host will not reply to every packet. So we have to send our UDP packets not so fast - otherwise we'll miss some replies - and not so slow - otherwise the scan will last for days.

This is when we grab our favourite network packet manipulation tool scapy and with a single line, we can perform a very good UDP scan:
>>> ans,unans = sr(IP(dst='target')/UDP(dport=[(1,65535)]),inter=0.5,retry=10,timeout=1)
.*..*.*..*. [...]
Finished to send 65535 packets.
Begin emission:..*..*.*..*. [...]
Finished to send 500 packets.
Begin emission:..*..*.*..*.. [...]
Received 6553512 packets, got 65534 answers, remaining 1 packets
(<Results: TCP:0 UDP:0 ICMP:65534 Other:0>, <Unanswered: TCP:0 UDP:1 ICMP:0 Other:0>)
>>> unans.summary()
IP / UDP source:sourceport > target:1234

Options to sr (send & receive packets at layer 3):
  • inter: means the interval in seconds between packets sent
  • retry: after all packets have been sent, those with no answer are sent once again
  • timeout: maximum wait time after the last packet have been sent
    see Scapy documentation for more information.
Here I performed the UDP scan by sending the packets at 0.5s interval, which is a good compromise for speed and reliability. I used 10 passes (you can do more) in order to filter out false positives, and after the 10th pass, the only unanswered port is the one I was looking for. Obviously this is a very long scan (about 10hours) because it's basically ports to scan * interval between packets (plus any packets retried), but it proved to work well.