Udpscan.php
From ChekMate Security Group
udpscan.php
<?
/*
* a udp port scanner
*
* this class implements a udp port scanner in php. a sample to
* use the class would be the following:
*
*
* $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/>";
* }
* }
*
*
* copyright jason n. perkins (jason@somebodydial911.com) 2001-10-15
* version 1.0
* released on 2001-10-15
*
*/
class udpPortScanner {
var $targetIP;
var $minPort;
var $maxPort;
var $timeout;
var $socketTimeout;
var $ports = array();
var $cleanupIterations;
var $output;
/*
* udpPortScanner
*
* class constructor. we initialize a couple of object variables here.
*/
function udpPortScanner ($targetIP, $minPort=1, $maxPort=1024, $output=0) {
// intitalize variables
$this-> targetIP = "udp://$targetIP";
$this-> minPort = $minPort;
$this-> maxPort = $maxPort;
$this-> output = $output;
set_time_limit(0);
}
/*
* doScan
*
* the only other public method in the class. this method is similar to
* a c's main function; everything is run from here. we do our
* networkProbe, then conduct the scan itself, then run the method to
* test the results returned from our scan to eliminate false postives
* and finally return an array indexed by the ports that we found open.
*
*/
function doScan () {
$this-> _networkProbe();
// conduct the scan
for ($portNumber = $this-> minPort; $portNumber <= $this-> maxPort; $portNumber++) {
if ($this-> output == 1) {
echo "scanning port: $portNumber<br/>";
flush();
}
if ($this-> _scanPort($portNumber)) {
$service = getservbyport($portNumber, udp);
$this-> ports [$portNumber] = $service;
}
}
// now call the method that will test for and remove detected fasle
// positives
$this-> _removeFalsePositives ();
// return the detected open ports array
return $this-> ports;
}
/*
* _scanPort (as private as you can get in php)
*
* the actual scanning of a specified port occurs here. first a socket
* is opened to the specified port and then we set the timeout of the
* function to the value that was determined in the _networkProbe
* function. we then send a single udp packet to the target machine.
* after the packet is sent, we enter a loop waiting for a response.
* a host running nothing on that port will immediately return an
* error. however, if the datagram is lost or that port is open,
* nothing will be returned and the socket time's out. if the socket
* timed out, we return a 1 value (true), else we return a 0
* value (false).
*/
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;
}
}
/*
* _removeFalsePositives (as private as you can get in php)
*
* we ititerate over the contents of the $ports array testing each
* port for false positives returned from our main scan of the target
* ip. if a false positive is found, we unset() that value from the
* array and continue the processing of the shortened array. the
* number of iterations that's conducted was determined in the
* _networkProbe method. the array of tested ports is returned.
*/
function _removeFalsePositives () {
if (count($this-> ports) > 0) {
$noInitiallyOpenPorts = count($this-> ports);
if ($this-> output == 1) {
echo "<br/>";
echo "$noInitiallyOpenPorts ports initially detected open.<br/>";
echo "cleanup iterations: " . $this-> cleanupIterations . "<br/>";
echo "<br/>";
}
for ($index = 1; $index <= $this-> cleanupIterations; $index++) {
if ($this-> output == 1) {echo "current cleanup iteration: $index<br/>";}
flush();
foreach ($this-> ports as $portNumber => $status) {
if (!$this-> _scanPort($portNumber)) {
unset ($this-> ports [$portNumber]);
}
}
}
}
return $this-> ports;
}
/*
* _networkProbe (as private as you can get in php)
*
* we conduct an intial udp port scan high in the port range. we're
* doing this to minimize the detection of legitimate open ports so
* that we can get an estimate of the number of udp packets that are
* being lost due to the network connection between the server and
* client. we'll use this estimate of lost packets to setup the
* number of cleanupIterations we have to run. the formula to determine
* this is a logarithim similar to one used to calculate exponential
* rate of decay.
*
* from the udp packets that don't timeout, we also establish
* the standard deviation of how long it took them to completete the
* round trip which is used to setup a timeout value of 4 sigma from
* the average round trip time for the socket_set_timeout function.
* because the socket_set_timeout currently won't except a value of
* less than a second this is mainly an exercise in futility in terms
* of minimizing program run time. If the socket_set_timeout function
* is ever changed to allow a value of less than one second, then
* under good network conditions we could expect a decrease in runtime
* of up to a factor of five.
*
*/
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;
// echo "$timeDiff<br/>";
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 scans we anticipate
// getting from the real scan
$estFalsePositives = $portRange * $percentFalsePositives;
$this-> cleanupIterations = $this-> _calcNoIterations ($estFalsePositives, $percentResponses, $portRange);
if ($this-> output == 1) {
echo "<br/>";
echo "total time $totalTime<br/>";
echo "timeout value: " . $this-> timeout . "<br/>";
echo "cleanup iterations: " . $this-> cleanupIterations . "<br/>";
echo "<br/>";
flush();
}
}
/*
* _getMicroTime (as private as you can get in php)
*
* this function return the current time as seconds.microseconds format.
* found on php.net and used here without modification.
*/
function _getMicroTime () {
$t = microtime();
$t = ((double)strstr($t, ' ') + (double)substr($t,0,strpos($t,' ')));
return $t;
}
/*
* _calcAvgResponseTime (as private as you can get in php)
*
* returns the avg response time of the initial scan.
*/
function _calcAvgResponseTime ($noResponses, $totalTime) {
if ($noResponses == 0) {
$averageResponseTime = 0;
} else {
$averageResponseTime = $totalTime / $noResponses;
}
return $averageResponseTime;
}
/*
* _calcStdrDeviation (as private as you can get in php)
*
* calculates and returns the standard deviation of the array of
* response times from the intital scan.
*/
function _calcStdrDeviation ($responsesArray) {
foreach ($responsesArray as $currentResponse) {
$temp = pow(($averageResponseTime - $currentResponse), 2);
$variance += $temp;
}
$standardDeviation = sqrt ($variance);
return $standardDeviation;
}
/*
* _calcNoIterations (as private as you can get in php)
*
* this function return the current time as seconds.microseconds format.
* found on php.net and used here without modification.
*/
function _calcNoIterations ($estFalsePositives, $percentResponses, $portRange) {
// we're always going to have some cleanupIterations, so
if ($noFalsePosititives == 0) {
$cleanupIterations = 3;
} else {
$this-> cleanupIterations = ceil (log($estFalsePositives) / $percentResponses);
}
if ($cleanupIterations < 5) {
$cleanupIterations = 5;
}
return $cleanupIterations;
}
}
?>




