http://www.perlmonks.org?node_id=885496

davis has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to prove that a given UDP port is closed on the local machine (e.g. we've got an arbitrary security restriction that says "UDP port 111 (rpcbind)" shouldn't be open!). I know that I can test TCP open-/closed- ness by checking with an IO::Socket::INET, and that UDP is harder because it doesn't do a connection handshake. There's therefore effectively almost no such thing as "closed" (sort of), so after reading some advice here I'm trying to send an empty datagram, then check for an error. However, the following test passes. I'm looking for a test failure, because rpcbind is currently open on my machine:
#!/usr/bin/perl use warnings; use strict; use Data::Dumper; use Test::More tests => 1; use IO::Socket; my $expected_closed_port = 111; my $dead_socket = IO::Socket::INET->new(PeerAddr => 'localhost', PeerP +ort => $expected_closed_port, Proto => "udp"); if($dead_socket) { # It could be a UDP connection, and there's no such concept as + UP or DOWN, so we need to try sending an # empty datagram, and check for an error $dead_socket->send(""); my $foo; setsockopt( $dead_socket, SOL_SOCKET, SO_RCVTIMEO, pack('L!L!' +, +10, 0) ); my $return_value = recv($dead_socket, $foo, 1, 0); if(defined($return_value)) { ok(0, "I shouldn't be able to connect to port $expected_cl +osed_port on localhost"); } else { ok(1, "I shouldn't be able to connect to port $expected_cl +osed_port on localhost"); } }
The ok/not ok is a little funky because of the other stuff that my "real" code is doing, but this is a working example of the unwanted passing of a test. Can anyone see where I'm going wrong? I believe recv is supposed to return undef on failure, but it doesn't appear to.

davis

Replies are listed 'Best First'.
Re: Proving a UDP port is closed
by marto (Cardinal) on Feb 01, 2011 at 15:27 UTC

    "we've got an arbitrary security restriction that says "UDP port 111 (rpcbind)" shouldn't be open!"

    As an observarion to this statement and as a response to UDP port scanning in general you may be interested in reading The art of port scanning:

    "UDP ICMP port unreachable scanning : This scanning method varies from the above in that we are using the UDP protocol instead of TCP. While this protocol is simpler, scanning it is actually significantly more difficult. This is because open ports don't have to send an acknowledgement in response to our probe, and closed ports aren't even required to send an error packet. Fortunately, most hosts do send an ICMP_PORT_UNREACH error when you send a packet to a closed UDP port. Thus you can find out if a port is NOT open, and by exclusion determine which ports which are. Neither UDP packets, nor the ICMP errors are guaranteed to arrive, so UDP scanners of this sort must also implement retransmission of packets that appear to be lost (or you will get a bunch of false positives). Also, this scanning technique is slow because of compensation for machines that took RFC 1812 section 4.3.2.8 to heart and limit ICMP error message rate. For example, the Linux kernel (in net/ipv4/icmp.h) limits destination unreachable message generation to 80 per 4 seconds, with a 1/4 second penalty if that is exceeded. At some point I will add a better algorithm to nmap for detecting this. Also, you will need to be root for access to the raw ICMP socket necessary for reading the port unreachable. The -u (UDP) option of nmap implements this scanning method for root users.

    Some people think UDP scanning is lame and pointless. I usually remind them of the recent Solaris rcpbind hole. Rpcbind can be found hiding on an undocumented UDP port somewhere above 32770. So it doesn't matter that 111 is blocked by the firewall. But can you find which of the more than 30,000 high ports it is listening on? With a UDP scanner you can!"

Re: Proving a UDP port is closed
by Limbic~Region (Chancellor) on Feb 01, 2011 at 14:31 UTC
    davis,
    Wouldn't it just be easier to parse a system call to netstat?

    Cheers - L~R

      I would recommend reading the man page for nmap;

      "Upon hitting a closed port on the target machine, the UDP probe should elicit an ICMP port unreachable packet in return. This signifies to Nmap that the machine is up and available. Many other types of ICMP errors, such as host/network unreachables or TTL exceeded are indicative of a down or unreachable host. A lack of response is also interpreted this way. If an open port is reached, most services simply ignore the empty packet and fail to return any response. This is why the default probe port is 31338, which is highly unlikely to be in use. A few services, such as chargen, will respond to an empty UDP packet, and thus disclose to Nmap that the machine is available."

      AND

      " -sU (UDP scans) While most popular services on the Internet run over the TCP protocol, UDP3 services are widely deployed. DNS, SNMP, and DHCP (registered ports 53, 161/162, and 67/68) are three of the most common. Because UDP scanning is generally slower and more difficult than TCP, some security auditors ignore these ports. This is a mistake, as exploitable UDP services are quite common and attackers certainly donĀ“t ignore the whole protocol. Fortunately, Nmap can help inventory UDP ports. UDP scan is activated with the -sU option. It can be combined with a TCP scan type such as SYN scan (-sS) to check both protocols during the same run. UDP scan works by sending an empty (no data) UDP header to every targeted port. If an ICMP port unreachable error (type 3, code 3) is returned, the port is closed. Other ICMP unreachable errors (type 3, codes 1, 2, 9, 10, or 13) mark the port as filtered. Occasionally, a service will respond with a UDP packet, proving that it is open. If no response is received after retransmissions, the port is classified as open|filtered. This means that the port could be open, or perhaps packet filters are blocking the communication. Versions scan (-sV) can be used to help differentiate the truly open ports from the filtered ones. A big challenge with UDP scanning is doing it quickly. Open and filtered ports rarely send any response, leaving Nmap to time out and then conduct retransmissions just in case the probe or response were lost. Closed ports are often an even bigger problem. They usually send back an ICMP port unreachable error. But unlike the RST packets sent by closed TCP ports in response to a SYN or connect scan, many hosts rate limit ICMP port unreachable messages by default. Linux and Solaris are particularly strict about this. For example, the Linux 2.4.20 kernel limits destination unreachable messages to one per second (in net/ipv4/icmp.c). Nmap detects rate limiting and slows down accordingly to avoid flooding the network with useless packets that the target machine will drop. Unfortunately, a Linux-style limit of one packet per second makes a 65,536-port scan take more than 18 hours. Ideas for speeding your UDP scans up include scanning more hosts in parallel, doing a quick scan of just the popular ports first, scanning from behind the firewall, and using --host-timeout to skip slow hosts. "

        I'm aware of nmap, thanks. The problem is that my code above shows _no_difference_ between a closed and an open UDP port. I understand that I might have to try timing out, and that I might have to lump all "connection failed" responses together, and I'm completely fine with that.

        Edit: Holy cow. I just realised that gman and I joined PM about 2 weeks apart. Almost 10 years ago!

        davis

      *nods at L~R

      Hmmm. Maybe; I hadn't really thought of that... However, I'm not sure that a netstat would show all services that are started by inetd (the reason I switched to rpcbind here is because more people are likely to have rpcbind available, hence the failure's easier to prove). I'm also trying to get as close to evidential proof as possible -- e.g. "You want port 1234 closed?": "Let's prove it's closed by trying to connect to it"


      davis

        davis,
        The netstat command shows network information and is not concerned with what services/processes are running. Also, inetd isn't magical and can't establish a connection without a listener. In order for the server to accept connections on a port then there has to be a listener for that port. The netstat command can be used to show what listeners are out there.

        As far as proving it by attempting to connect to it, I would have no way of testing presently.

        Cheers - L~R