Article

UDP Portscanning in PHP

Page: 1 2 3 Next

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:

  1. the remote server returns an "ICMP Destination Unreachable: Port Unreachable" Error Message to us, and the fread ends, or

  2. 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.

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links