Key Takeaways
- UDP port scanning is crucial for identifying open ports and potential vulnerabilities, particularly for services that use UDP like SNMP, TFTP, and NFS.
- Unlike TCP, UDP is connectionless, meaning data can be sent without establishing a connection, making UDP scanning inherently less reliable for determining if ports are open or closed.
- Implementing a UDP port scanner in PHP involves using the fsockopen function with UDP specified, and managing timeouts to interpret whether a port is open.
- The PHP script for UDP port scanning can differentiate between closed ports and lost packets by analyzing the time taken to receive an ICMP Destination Unreachable message.
- Network conditions significantly affect the accuracy of UDP port scans, with poor connections leading to high rates of lost packets and false positives.
- Pre-scan adjustments, such as setting appropriate timeout values and conducting initial network condition tests, are crucial for minimizing errors in the main UDP port scanning process.
Portscanning serves a legitimate role in system administration/ownership. By confirming exactly what ports a computer accepts connections on, it’s possible to ensure that an operating system hasn’t opened unnecessary ports to the world at large as part of a default install. It also allows us to check that our system hasn’t been compromised and had ports opened to allow a cracker to send remote commands to our machine via the Internet.
While TCP port scanning of one’s own ports is common, many underestimate the potential hazards of open UDP ports — this can lead to compromise, or indicate that a compromise has occurred. As the nmap manpage says:
“There is also the cDc Back Orifice backdoor program which hides on a configurable UDP port on Windows machines. Not to mention the many commonly vulnerable services that utilize UDP such as snmp, tftp, NFS, etc.”
The single largest impediment to the average home user conducting port scans of their own machines is the lack of simple software to conduct the scan for them. Don’t get me wrong — nmap is one of the best portscanning tools available — but how comfortable is Joe Average User going to be using a *nix command line tool? It seems to me that a tool that only requires Mr. Average User to go to a specific URL and wait while a PHP script runs a TCP and UDP scan against their machine, and then returns the results of the scan to them, is just what the doctor ordered.
With this in mind, I started searching the Internet and found an implementation of a TCP port scanner that Jim Barcelona had coded and made available at php wizard
Great! With half of the work done, I should be able to whip out a UDP port scanner in no time. I mean, how much harder could coding a UDP port scanner be? Considerably harder, as it turns out.
TCP Portscanning in PHP
When a TCP socket is created using the fsockopen
function, you specify the IP address of the remote machine and the port number to which you want to connect. Using the underlying socket functionality, PHP will then attempt to create a virtual circuit to the remote machine on the specified port, in order to allow further communication to occur. If the destination port is unavailable, then the TCP provider on the remote machine will reject the connection request and the fsockopen
function will be return a boolean value of failed
.
So we now have an easy to understand and implement TCP portscanner. We set up a loop that specifies the minimum and maximum port numbers that we wish to scan, and within the loop, attempt to open a connection at the current value of the loop’s index. If the attempt to open the port fails, there’s no service at that port. If we’re able to successfully open a socket to that port, then there’s a service at that port which we log in our array of open ports as a key. We then insert the value for that key with a call to getservbyport
, which will return the unix service that’s normally registered at that port. Lastly, we close the open socket and move onto the next iteration of our loop.
UDP Portscanning in PHP
When an application wishes to send data over the network using the UDP protocol, it gives the data to UDP through the assigned port number, also telling UDP which port on the destination system the data should be sent to. UDP then creates a UDP message, marking the source and destination port numbers, which is then passed off to IP for delivery.
When a UDP packet is received by the destination machine, the UDP software looks at the packet’s header for the destination port number, and hands the payload off to whatever application has registered as using that port number. If no application has registered for the specified port number, then an “ICMP Destination Unreachable: Port Unreachable” Error Message is returned to the remote machine, and the payload is discarded.
Creating a UDP socket in PHP is similar to creating a TCP socket, with a couple of differences. You still call the fsockopen
function, but you must specify:
- that you want to use the UDP protocol,
- the IP address of the remote computer and
- the port number that you want to connect with.
In contrast to the TCP socket, at this point no connection exists to the remote computer.
function _scanPort ($portNumber) {
$handle = fsockopen($this-> targetIP, $portNumber, &$errno, &$errstr, 2);
if (!$handle) {
echo "$errno : $errstr <br/>";
}
socket_set_timeout ($handle, $this-> timeout);
$write = fwrite($handle,"x00");
if (!$write) {
echo "error writing to port: $index.<br/>";
next;
}
$startTime = time();
$header = fread($handle, 1);
$endTime = time();
$timeDiff = $endTime - $startTime;
if ($timeDiff >= $this-> timeout) {
fclose($handle);
return 1;
} else {
fclose($handle);
return 0;
}
}
Because a call to read from our UDP socket will block (wait until it receives data), we set a value to the set_socket_timeout
function. This tells the socket to only listen (block) on the socket for a response from the remote machine for a specific period of time. If that time is exceeded, then the socket will stop listening, and the code will continue to run. We’ll see how we use this to discern an open port shortly.
As UDP is connectionless, at this point a virtual circuit is not setup with the remote machine. Instead, you have to pass the data that you want to send to the remote machine through the socket using the fwrite
function. We then immediately log the time that we begin listening for a response from the remote machine, and use fread
to listen to the socket. At this point, one of two things can happen:
- the remote server returns an “ICMP Destination Unreachable: Port Unreachable” Error Message to us, and the
fread
ends, or - the socket times out waiting for a response.
In either case, we now log the time that the listening ended, and derive the total time spent listening by subtracting the end time from the start time. We compare this number with the socket’s timeout value that we set. If it’s less than the timeout value, then the remote server returned an “ICMP Destination Unreachable: Port Unreachable” Error Message to us, and we know that the port is closed. If the socket timed out, then we know one of two other things occurred: the application at that port was waiting to receive a valid command, or the packet was lost in transit (UDP doesn’t offer guaranteed delivery, and if a packet is lost en route, we’re not going to receive any information to that effect).
So, we’ve established a means to discern whether there’s anything at the port (ie. the socket returned before it timed out), and we now need a way to tell whether a socket timing out was the result of an application waiting for legitimate data, or the packet was lost in transit. The easiest way to do this would be to send multiple packets to that port, and if we get one response back that doesn’t timeout, then we know that there’s not an application there and the port is closed. But how can we do this in a manner that’s not too wasteful of bandwidth, and minimizes the application’s run time?
Prior to our full scan of the UDP ports that the class was instructed to scan, we’re going to conduct a smaller port scan very high in the port range, to minimize the finding of legitimate open ports, and test the network conditions between the machines. This initial scan is carried out in
our _networkProbe
method. Since most of the open UDP ports that are open are at or below port 1024, we’ll do a scan up in the port 55000 range. Any ports that time out this high in the port range we can assume did so because of lost datagrams, and not because we’ve detected an open port.
function _networkProbe ($noTrials=100, $startPortNumber=55000) {
$endPortNumber = $startPortNumber + $noTrials;
// temporarily set timeout to 2 seconds. we'll modify this with the
// data that we get from this method
$this-> timeout = 2;
// setup a for loop to scan the ports
for ($portNumber = $startPortNumber; $portNumber < $endPortNumber;
$portNumber++) {
$startTime = $this-> _getmicrotime();
$result = $this-> _scanPort($portNumber);
$endTime = $this-> _getmicrotime();
$timeDiff = $endTime - $startTime;
if (!$result) {
$responsesArray[] = $timeDiff;
$totalTime += $timeDiff;
}
}
$noResponses = count($responsesArray);
// if more than 40% of the datagrams timed out, abort the scan
if ($noResponses < (.6 * $noTrial)) {
echo "The connection is losing too many packets. Scan aborted. <br/>";
exit;
}
$averageResponseTime = $this-> _calcAvgResponseTime ($noResponses,
$totalTime);
$standardDeviation = $this-> _calcStdrDeviation ($responsesArray);
// calculate the timeout value
$timeoutValue = ceil($averageResponseTime + 4 * $standardDeviation);
// calculate number of cleanup iterations we'll need
// percentFalsePositive is the % of datagrams that we sent in
// the trial that timed out
$percentFalsePositives = ($noTrials - $noResponses)/$noTrials;
// percentResponses is the % of datagrams that we sent in the trial
// that returned (eg -- didn't timeout)
$percentResponses = $noResponses/$noTrials;
// calculate the total number of ports to be scanned in the
// real scan
$portRange = $this-> maxPort - $this-> minPort + 1;
// estFalsePositives is the estimated number of false positives we
// anticipate getting from the real scan
$estFalsePositives = $portRange * $percentFalsePositives;
$this-> cleanupIterations = $this->
_calcNoIterations ($estFalsePositives, $percentResponses, $portRange);
if ($this-> debug == 1) {
echo "<br/>";
echo "total time $totalTime<br/>";
echo "timeout value: " . $this-> timeout . "<br/>";
echo "cleanup iterations: " . $this-> cleanupIterations . "<br/>";
echo "<br/>";
flush();
}
}
So we now know:
- the number of packets that were lost from our sample,
- the total number of packets that were sent and
- the average time that it we had to listen for each packet that did respond without blocking.
With the number of packets lost from our sample, the total packets sent with our initial scan, and the range of how many ports we’re going to scan in the main scan, we can calculate the number of iterations that we’ll have to run, and retest the open ports detected, to eliminate false positives. The formula that we use to do this is an exponential decay logarithm and it’s functionallity can be found in the _calcNoIterations
method of the class.
We’re also going to use the average response time of the fread
calls that returned an “ICMP Destination Unreachable: Port Unreachable” Error Message and didn’t block, to calculate the standard deviation of these individual times. We multiply the standard deviation by a factor of four (four sigma) and add it to our average response time. This allows us to minimize the timeout value, and still be reasonably certain that we’re not eliminating too many scans that would’ve returned had it not timed out. At this point, this check is actually superfluous given that the set_socket_timeout
value can’t be set to a value of less than one second, which is where most of the derived timeout values are going to be. However, if the socket timeout value is ever modified to accept values of less than one second, we can anticipate a runtime decrease up to a factor of five in eliminating the ports that don’t have a service on them.
Using the UDP Portscanning Class
The UDP Port Scanning Class does an excellent job of abstracting the complexities of UDP port scanning. Use of the class is simplicity itself: include the class and new up an object based on the class that’s passing it the the target ip address, and optionally, the port to start scanning at, the port to end scanning at, and whether the object should output information as it conducts the scan. By default, the start port is set to 1, the end port is set to 1024, and output is on.
Then call the objects doScan
method and assign its output to a variable that will hold the results of the scan as an array. Here’s an example:
include ('classes/udpPortScanner.inc');
$udpScanner = new udpPortScanner("$REMOTE_ADDR");
$ports = $udpScanner-> doScan();
if (count($ports) == 0) {
echo "no open udp ports detected.<br/>";
} else {
echo "open udp ports:<br/>";
foreach ($ports as $portNumber => $service) {
echo "$portNumber ($service)<br/>";
}
}
Closing
There are two modifications that I have to make and another that I’d like to make. First, the calculated value of the number of iterations to eliminate false positives resulted in a number that is too low if the network conditions are good, and I was getting ports returned that were not, in fact, open. I modified the code to bump the minimum number of cleanup iterations up to a value of five in order to compensate for this.
A UDP port scanner is heavily reliant upon the network conditions that exist between the two machines. In testing, I had friends in both Australia and South Africa (I’m located in the U.S.) volunteer to be scanned, and the scanner was having a really difficult time detecting closed ports. This was because the network conditions were leading to a very large number of packets being lost in transit, and very long runtimes were encountered. In defense of the scanner, an nmap scan of the first 1024 UDP ports on the host in South Africa took nearly an hour, indicating that this is endemic to UDP port scanning, not to this scanner’s implementation. With this in mind, it became obvious that some of the network connections were just too bad to complete a scan in a reasonable amount of time. Because of this, I modified the code to abort if, during the initial network testing scan, more than 40% of the packets were lost. Feel free to eliminate or modify this value, but be warned that scanning over a bad network span can take a considerable amount of time.
Finally, I’d also like to collect information as to what ports Microsoft places its various services on, to complement the getservbyport
function, which will only return services that are mapped to the traditional ports on a *nix box. I could then specify which Microsoft service is running on a port, and in a later version, indicate how comprimising this is, and describe methods to disable the service and close the port if it’s not in use.
I’d like to thank David LaCroix of suddendecelaration.com for his assistance in setting up tcpdump for me to analyze the UDP port scans that I was conducting against one of his volunteered machines. His assistance was instrumental in completing the debugging of this software. Also, thanks to Daniel Bogan of waferbaby.com and Zak McGregor of carfolio.com, both of whom were helpful in determining that trans-oceanic UDP port scans are not that great an idea.
For additional information on Internet protocols, I highly recommend O’Reilly and Associates “Internet Core Protocols: The Definitive Guide,” by Erik A. Hall. You can find more information on O’Reilly’s Website.
The official home for the the tcpPortScan and udpPortScan classes is here. You can check there for the latest updates to the classes and to report any problems, issues or suggestions that you have.
Frequently Asked Questions about UDP Port Scanning in PHP
What is UDP port scanning and why is it important in PHP?
UDP, or User Datagram Protocol, is one of the core protocols in the Internet protocol suite. UDP port scanning is a process that sends client requests to server port addresses to identify online services. In PHP, UDP port scanning is important as it allows developers to identify open ports and services available on a host network. This can be useful in network security to identify potential vulnerabilities.
How does UDP port scanning differ from TCP port scanning?
While both UDP and TCP port scanning are used to identify open ports and services on a network, they operate differently. TCP, or Transmission Control Protocol, is a connection-oriented protocol, meaning it requires a connection to be established before data can be sent. UDP, on the other hand, is a connectionless protocol. It doesn’t require a connection to send data, making it faster but less reliable than TCP.
How can I implement UDP port scanning in PHP?
Implementing UDP port scanning in PHP involves creating a socket, setting socket options, and then using the socket to send and receive data. PHP has built-in functions for this, such as socket_create(), socket_set_option(), and socket_sendto(). You can find detailed examples and explanations in the article.
What are some common issues I might encounter when performing UDP port scanning in PHP?
Some common issues include firewall restrictions, which can block UDP packets, and PHP configuration settings, which may limit the execution time of scripts. Additionally, because UDP is a connectionless protocol, it can be difficult to determine whether a port is open or closed.
How can I troubleshoot issues with UDP port scanning in PHP?
Troubleshooting UDP port scanning in PHP often involves checking the error codes returned by the socket functions. PHP provides the socket_strerror() function, which returns a string describing the error. Additionally, you may need to adjust PHP configuration settings or firewall rules.
Can I use UDP port scanning to test the security of my network?
Yes, UDP port scanning can be a useful tool in network security. By identifying open ports and services, you can identify potential vulnerabilities and take steps to secure them.
Are there any libraries or tools that can simplify UDP port scanning in PHP?
While PHP has built-in functions for socket programming, there are also libraries and tools that can simplify the process. For example, the PHP Socket Class provides an object-oriented interface for socket programming.
How can I optimize the performance of my UDP port scanning script in PHP?
Performance optimization can involve a variety of strategies, such as using non-blocking sockets, adjusting the timeout value, or using multithreading to scan multiple ports simultaneously.
What are some best practices for UDP port scanning in PHP?
Best practices include handling errors properly, using non-blocking sockets to improve performance, and being mindful of the potential impact on network resources. Additionally, it’s important to respect the privacy and security of others when performing port scanning.
Can I use UDP port scanning in PHP to send data to an IP address?
Yes, you can use UDP port scanning in PHP to send data to an IP address. This involves creating a socket, setting socket options, and then using the socket_sendto() function to send data to the specified IP address and port.
Jason's been involved with Web development since 1996 and currently resides in Chicago, IL. Visit his personal site at www.somebodydial911.com.