RobReid Posted December 20, 2009 Share Posted December 20, 2009 I am pretty new to PHP and I am trying to create a port scanner (not for hacking!) on similar lines to the ones you find on security sites and online DNS tool sites. For example if you go to this site http://networking.ringofsaturn.com/Tools/probe.php and run a port scan for a site that I own it returns the following ports open which are correct. Port Response 22-SSH SSH-2.0-OpenSSH_5.1p1 Debian-5 25-SMTP 220 domain.com ESMTP Postfix (Debian/GNU) 53-DNS 80-HTTP 143-LDAP OK Dovecot ready. 443-HTTPS However when I run a basic loop through all ports with fsockopen call e.g $fp = fsockopen($url, $port, $errno, $errstr, 2); if($fp){ echo("open"); }else{ echo("closed"); } which is the code that is shown on numerous websites I have come across it returns the following ports as open 22-SSH 25-SMTP 80-HTTP 110-POP3 143-IMAP 443-HTTPS 465-SMTPS 1080-SOCKS 3128-SQUID 8000-HTTP 8080-HTTP 10000-Amanda As you can see I show ports open that are not and also some closed that are not e.g 53 DNS. Now the problem I see with just checking for a port being open with fsockopen is that a port maybe open but a firewall maybe protecting it and dropping the packet. Therefore if that happens I want to report it as closed which is what I am assuming the ringofsaturn tool does. I don't know how to do this and have searched high and low and would like to write something myself rather than use a component as thats the best way to learn a new language I feel jump in at the deep end. So I tried something a bit different which now gives me the responses that http://networking.ringofsaturn.com/Tools/probe.php gives but still shows the incorrect ports as being open e.g test port: 21 Open mydomain.com port 21 - ftp errorno = 0 error = is resource! fp = Resource id #2 response = socket status stream_type: tcp_socket mode: r+ unread_bytes: 0 seekable: timed_out: 1 blocked: 1 eof: test port: 22 Open mydomain.com port 22 - N/A errorno = 0 error = is resource! fp = Resource id #3 response = SSH-2.0-OpenSSH_5.1p1 Debian-5 socket status stream_type: tcp_socket mode: r+ unread_bytes: 0 seekable: timed_out: blocked: 1 eof: test port: 25 Open mydomain.com port 25 - smtp errorno = 0 error = is resource! fp = Resource id #2 response = 220 mydomain.com ESMTP Postfix (Debian/GNU) socket status stream_type: tcp_socket mode: r+ unread_bytes: 0 seekable: timed_out: blocked: 1 eof: test port: 53 Open mydomain.com port 53 - domain errorno = 10060 error = A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. no connection to port: 53 The code I have so far is below. Can someone tell me what I should be doing to check whether a port is actually open or whether its actually being blocked by a firewall so not really open as the packet is being dropped. Any pointers would be great! function testPort($url,$port){ $result = ""; if (!getservbyport($port,"tcp")) {$pname = "N/A";} else {$pname = getservbyport($port,"tcp");} echo "Open $url port $port - $pname<br />"; $fp = @fsockopen($url, $port, $errno, $errstr, 2); $response=""; echo "errorno = $errno error = $errstr <br />"; if (is_resource($fp)){ echo "is resource! fp = $fp<br/>"; $response = fread($fp,4096); echo "response = $response<br/>"; $stat=socket_get_status($fp); echo "socket status<br/>"; foreach($stat as $key=>$value){ echo "$key: $value<br />"; } echo 'Open - $response<br />'; fclose($fp); }else{ echo "no connection to port: $port<br />"; if($errno==10060){ echo 'Closed - $pname - Timeout<br />'; }else{ echo 'Closed - $pname - $errstr<br />'} } } flush(); return; } Link to comment https://forums.phpfreaks.com/topic/185808-writing-a-port-scanner-that-correctly-identifies-open-closed-and-blocked/ Share on other sites More sharing options...
Deoctor Posted December 21, 2009 Share Posted December 21, 2009 hi i wrote a code long back about this port scanning. just go through it. timer.php <html> <title>Check the Open ports</title> <pre> <font size=3><b>Hai Using this application u can check the ports that are opened in the ip address. If no port is mentioned it will check for the default ports.. <a href="http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers" target="_blank">For list of TCP/UDP ports</a> Common Ports Used are FTP 21 SSH 22 Telnet 23 SMTP 25 Web 80 Pop 3 110 IMAP 143 Other Applications Remote Desktop 3389 PC Anywhere 5631</b></font> </pre> <body background="2.png" bgproperties="fixed"> <form action="timer.php" method="POST"> <br><br><br> <table align="center" border="1" bordercolor="black"> <tr> <td align="left" bgcolor="transparent" valign="center">Ip address:<input type=text style="color: #FF0000;font-family: Verdana;font-weight: bold;font-size: 14px;background-color: transparent;" maxlength='30' name=address></td></tr><tr></tr> <tr><td align="left" bgcolor="transparent" valign="center">Port Number:<input type=text style="color: #FF0000;font-family: Verdana;font-weight: bold;font-size: 14px;background-color: transparent;" maxlength='25' name=service></td></tr> <tr><td align="center" valign="center"><input type=submit name=submit value=Submit></td> </tr> </table> </form> </body> </html> <?php error_reporting(0); $address = $_POST['address']; $service = $_POST['service']; if($address && $service) { if(fsockopen($address,$service,$errno,$errstr,10)) { //echo "$address:$service is up!"; echo ("<table><td bgcolor=green width=100% align='center'><font face=Verdana, Arial, Helvetica, sans-serif size=2><b>$address:$service is up!</b></font></td></table>"); } else { //echo "$address:$service is NOT up!"; echo ("<table><td bgcolor=red width=100% align='center'><font face=Verdana, Arial, Helvetica, sans-serif size=2><b>$address:$service is Down!</b></font></td></table>"); } } elseif($address) { include("scanner.class.php"); /* get the target ip address */ $ip_address = gethostbyname("$address"); error_reporting(off); /* set all the require atributes */ $my_scanner = new PortScanner($ip_address,$ip_address); $my_scanner->set_ports("15-25,80,110,143,3306,3389,5631,1337,666"); $my_scanner->set_delay(1); $my_scanner->set_wait(2); /* do the scan and capture the results */ $results = $my_scanner->do_scan(); /* display the results - this simply loops through the ip addresses and indents the results */ foreach($results as $ip=>$ip_results) { echo gethostbyaddr($ip)."\n<blockquote>\n"; foreach($ip_results as $port=>$port_results) { echo "\t".$port." : ".$port_results['pname']." : "; if ($port_results['status']==1) { //echo "open"; echo ("<table><td bgcolor=Green width=100% align='center'><font face=Verdana, Arial, Helvetica, sans-serif size=2><b>Open</b></font></td></table>"); } else { //echo "closed"; echo ("<table><td bgcolor=Red width=100% align='center'><font face=Verdana, Arial, Helvetica, sans-serif size=2><b>Closed</b></font></td></table>"); } echo "<br>\n"; } echo "</blockquote>\n\n"; } } else { } ?> scanner.class.php <? /******************************************************************************* * Copyright 2002 - Adrian Ritchie :: GrinGod Productions :: [email protected] * * This software is being release by Adrian Ritchie for educational purposes. * Whilst it may be re-used by other parties, the copy-right remains with * Adrian Ritchie. Should this code be re-used either whole or in part, this * agreement should be attached. * * Dislaimer: * This software is for educational / private network use only. Do with it * what you may, but on your own head be it! *******************************************************************************/ class PortScanner { var $start_ip; /* start of the ip range to scan */ var $stop_ip; /* end of the ip range to scan */ var $current_ip; /* current ip address being scanned (this is for future features) */ var $ports; /* array of ports to be scanned */ var $wait; /* how long to wait for a response from the port */ var $delay; /* how long to pause between each port */ /*** * Function PortScanner() * Class constructor. * Sets the start and end address of the scan range * must be passed... but may be the same. * The end address must be greater than the start address. ***/ function PortScanner($start, $stop) { $this->start_ip = ip2long($start); /* store the start ip address as a long number */ $this->stop_ip = ip2long($stop); /* store the end ip address as a long number */ } /*** * Function set_ports * Adds the passed ports to the array of ports to be scanned. * This can either be a single port, a range of ports e.g. 1-90 * a a combination of both separated by commas (no spaces). ***/ function set_ports($ports) { /* Explode ports into an array based on comma seperation. Will create an array even if there is no comma so no need to check for an array in following code */ $ports_array = explode(",",$ports); /* loop through array of ports */ foreach($ports_array as $key=>$val) { /* try to explode port range into an array */ if(ereg("([0-9]+)\-([0-9]+)",$val, $buff)) { /* loop through range to add each port */ for($ii=$buff[1]; $ii<=$buff[2]; $ii++) { $this->ports[] = $ii; } } else { /* only one port was sent so add that */ $this->ports[] = $val; } } } /*** * Function set_wait * Sets the time to wait for response from socket. * Good object-oriented design doesn't allow access to class * attributes, so accessor functions are provided. ***/ function set_wait($wait) { $this->wait = $wait; } /*** * Function set_delay * Sets the delay between each port check. Delay is in micro-seconds. * Good object-oriented design doesn't allow access to class * attributes, so accessor functions are provided. ***/ function set_delay($seconds=0, $microseconds=0) { $this->delay = (1000000*$seconds) + $microseconds; } /*** * Function do_scan * Does the actual scan, based on the given details (see above functions). ***/ function do_scan() { /* Loop through ip addresses. This is why ip addresses are stored as long numbers as apposed to Internet Protocol dotted addresses */ for($this->current_ip=$this->start_ip; $this->current_ip<=$this->stop_ip; $this->current_ip++) { /* convert the long number back to a dotted address */ $ip = long2ip($this->current_ip); /* loop through the ports and check each */ foreach($this->ports as $key=>$port) { /* for unix systems, this will obtain the name of the service running on that port. Win32 systems will just return N/A. */ if (!getservbyport($port,"tcp")) {$pname = "N/A"; } else {$pname = getservbyport($port,"tcp"); } /* attempt to open a socket to the port at the current ip address */ $ptcp = fsockopen($ip, $port, &$errno, &$errstr, $this->wait); if($ptcp) {$status=1; } /* return 1 for open port (so users can display their own message */ else {$status=0; } /* return 0 for closed port (so users can display their own message */ /* return the results in a structured multi-dimensioned array so the user can choose how to display the results */ $results["$ip"]["$port"]["pname"] = "$pname"; $results["$ip"]["$port"]["status"] = "$status"; /* start the delay before moving on to the next port */ $this->do_delay($this->delay); } } /* returnt the results to the user */ Return $results; } /*** * Function do_delay * This pauses execution for the passed length of time (micro-seconds) * This allows a delay of less than 1 second for system which do not * support usleep (Win32). * This code was post on the PHP manual by "dsc at c2i dot net" * I'm not sure if this works or not, so if your running *nix you * may want to change this to use usleep(); ***/ function do_delay($delay) { $start = gettimeofday(); do { $stop = gettimeofday(); $timePassed = 1000000 * ($stop['sec'] - $start['sec']) + $stop['usec'] - $start['usec']; } while ($timePassed < $delay); } } ?> i think these might help you out. Link to comment https://forums.phpfreaks.com/topic/185808-writing-a-port-scanner-that-correctly-identifies-open-closed-and-blocked/#findComment-981340 Share on other sites More sharing options...
RobReid Posted December 24, 2009 Author Share Posted December 24, 2009 Unfortunatley your code doesn't work correctly either and reports exactly the same ports open as mine as you are also just checking fsockopen to determine whether the port is open or not. The port maybe open but if the Firewall drops the packet then its not actually open in a probe sense is it. Correct results from http://networking.ringofsaturn.com/Tools/probe.php 22,25,53,80,143,443 My results 22,25,53,80,110,143,443,465,1080,3128,8000,8080,10000 Your results 22,25,53,80,110,143,443,465,1080,3128,8080,8000,10000 You should try your own code out and test it against the results of the link I sent you so that you can see the difference. I am not going to give out my IP address but you can test it against www.google.com. The port scanner at http://networking.ringofsaturn.com/Tools/probe.php returns two open ports 80 http and 443 https. Your port scanner reports those two as well as many others. Therefore there must be something ontop of just checking for the port being open with fsockopen. The only thing I can think of it some sort of simulated Telnet request to the port passing the appropriate request for that type of port and then checking the response to see if its valid. The only problem with this approach is that you would have to craft an exact request/response pair for each possible port you want to test. If you manage to find anyone with the correct code please let me know. Thanks for replying. Link to comment https://forums.phpfreaks.com/topic/185808-writing-a-port-scanner-that-correctly-identifies-open-closed-and-blocked/#findComment-983382 Share on other sites More sharing options...
Deoctor Posted December 24, 2009 Share Posted December 24, 2009 Unfortunatley your code doesn't work correctly either and reports exactly the same ports open as mine as you are also just checking fsockopen to determine whether the port is open or not. The port maybe open but if the Firewall drops the packet then its not actually open in a probe sense is it. Correct results from http://networking.ringofsaturn.com/Tools/probe.php 22,25,53,80,143,443 My results 22,25,53,80,110,143,443,465,1080,3128,8000,8080,10000 Your results 22,25,53,80,110,143,443,465,1080,3128,8080,8000,10000 You should try your own code out and test it against the results of the link I sent you so that you can see the difference. I am not going to give out my IP address but you can test it against www.google.com. The port scanner at http://networking.ringofsaturn.com/Tools/probe.php returns two open ports 80 http and 443 https. Your port scanner reports those two as well as many others. Therefore there must be something ontop of just checking for the port being open with fsockopen. The only thing I can think of it some sort of simulated Telnet request to the port passing the appropriate request for that type of port and then checking the response to see if its valid. The only problem with this approach is that you would have to craft an exact request/response pair for each possible port you want to test. If you manage to find anyone with the correct code please let me know. Thanks for replying. dear rob i cannot tell u weather u understood the code or not.. coz i am checking some predefined ports in the scanner.class.php, so it will write these ports correctly..just now i have checked up google.com it shows that the port 22 is closed using my code.. and coming for the testing part i have tested it as well. and ur so called mentioned port scanner from the site also uses the same fsockopen() function to check the ports. there is nothing else that it is gone do. some ports will not get scanned when u try using from the server but when u try using the scanner from ur localhost machine it works perfectly.. so try the same code in the localhost which will give u different results.. and how are u so sure that the site is giving u correct results.. Link to comment https://forums.phpfreaks.com/topic/185808-writing-a-port-scanner-that-correctly-identifies-open-closed-and-blocked/#findComment-983499 Share on other sites More sharing options...
RobReid Posted December 24, 2009 Author Share Posted December 24, 2009 Hi there I changed your code so that it scanned all the ports that I was checking as well as the ports the other scanner checked e.g from your code I changed $my_scanner = new PortScanner($ip_address,$ip_address); //$my_scanner->set_ports("15-25,80,110,143,3306,3389,5631,1337,666"); $my_scanner->set_ports("20,21,22,23,25,37,53,69,79,80,106,110,111,123,135,137,138,139,143,389,443,465,513,554,587,631,902,1080,1214,1352,1433,1494,1720,1723,3128,3306,3389,5050,5060,5631,5900,7070,8080,9090,2705,2745,3410,3689,3724,4020,4899,5000,5001,5100,5190,5500,5501,5502,5503,6881,6969,8000,8001,8009,8038,9898,10000,10080,26000,28800,29100"); so as you can see it scanned all the ports I was scanning. My little snippet was just showing you the output from 3 particular ports from that list. The reason I know that the port scanner on http://networking.ringofsaturn.com/Tools/probe.php is correct is its my own site that I am scanning and I know which ports are open and closed as I set up the firewall. The site I am scanning is NOT the same server as the localhost I am running this test from. I have full outbound access on my localhost so I should see the correct ports on this server like he does on his site. I know which ports are blocked on the live server I am scanning and his scanner is correctly seeing them as blocked (by the firewall) whereas your code and my code when run from my PC is seeing these ports as open. I have also tried putting the code on a live server and I get exactly the same wrong results. He maybe using fsockopen as you can see from the error messages but he is obviously doing a lot more than just checking the resource result like you are doing as you can see in the right hand side of the results he gets a response such as SSH-2.0-OpenSSH_5.1p1 Debian-5 for port 22 and 220 mydomain.com ESMTP Postfix (Debian/GNU) for port 25 This is for my site not www.google.com by the way. So you can see that he is doing a lot more than just checking for the result of fsockopen. As that does not give you those details only from reading a response from the socket do you get that e.g $response = fread($fp,4096); As you can see from the debug results of my own scanner function I am also getting those results back so I am halfway there I just don't know what else I need to do to ensure I can check for a valid response from each port. I was hoping that this bit of code might help $stat=socket_get_status($fp); echo "socket status<br/>"; foreach($stat as $key=>$value){ echo "$key: $value<br />"; } where $fp is the result from fsockopen and returns socket status stream_type: tcp_socket mode: r+ unread_bytes: 0 seekable: timed_out: blocked: 1 eof: But I cannot see any pattern that would be useful. Thanks Link to comment https://forums.phpfreaks.com/topic/185808-writing-a-port-scanner-that-correctly-identifies-open-closed-and-blocked/#findComment-983522 Share on other sites More sharing options...
Deoctor Posted December 24, 2009 Share Posted December 24, 2009 now i got it out i think he is using the condition like this if fsockopen() fails then telnet the port number.. here is the telnet function but i didnt tested it ofcourse.. so Happy testing :P :P <?php class Net_Telnet { /** * @var string The remote server address. * @access private */ private $_address; /** * @var integer The remote server port. * @access private */ private $_port; /** * @var resource The remote server socket. * @access private */ private $_sock; /** * @var * @access static */ static public $CRLF = "\r\n"; /** * @var * @access public */ public $prompt; /** * Constructs a Net_Telnet object. * * @param string The remote server address, with option port * @param optional string The prompt to expect from the server. * @param optional integer The timeout for the connection attempt. * @access public */ public function __construct($address, $prompt=false, $timeout = 5) { if(preg_match("/([\w.-]+)(?:\d+))?/", $address, $matches)) { $this->_address = $matches[1]; $this->_port = $matches[2]?$matches[2]:23; } $this->prompt = $prompt; $this->_timeout = $timeout; $this->connect(); } /** * connect is called automatically from the constructor, so * we protect it. * * @access protected */ protected function connect() { $this->sock = fsockopen($this->_address, $this->_port); stream_set_timeout($this->_sock, $this->_timeout); } /** * Read a socket until we hit EOF or match a string. * * @param string The pattern to match (static currently) * @access public */ public function read_response_until($str) { $response = ''; while(1) { $char = fgetc($this->_sock); if($char === false) { return $response; } $response .= $char; if(strstr($response, $str)) { return substr($response, 0, strpos($response, $str)); } } } /** * Write to the socket, remembering to append the local CRLF. * * @param string The command to write to the socket. * @access public */ public function write_cmd($cmd) { $cmd .= self::$CRLF; fwrite($this->_sock, $cmd); } /** * A convenience method. Writes a command to the socket, and * reads until the next line prompt is returned from the server. * Returns the resulting buffer. * * @param string The command to write to the socket. * @access public */ public function command($buffer) { $this->write_cmd($buffer); $response = $this->read_response_until("$this->prompt>"); return $response; } } /* vim: set sts=2 ts=2 expandtab ai bs=2 : */ ?> Link to comment https://forums.phpfreaks.com/topic/185808-writing-a-port-scanner-that-correctly-identifies-open-closed-and-blocked/#findComment-983531 Share on other sites More sharing options...
RobReid Posted December 26, 2009 Author Share Posted December 26, 2009 Hi There and Merry Xmas I haven't tested your code yet but surely he is using a telnet if the fsockopen returns open and NOT closed. If a firewall is preventing access on the port then the port would be open but the packet is getting dropped so therefore if there is no connection with fsockopen then we know its closed. However if we get a resource object back from fsockopen we know that it either a) its open and accessible b) it open but being blocked by a firewall or something else So we need a way of distinguishing the difference Therefore for this "open" case this is where a telnet request comes in and if we get a response we know its actually open. However the issues are a) Knowing what requests to send for each port b) knowing what responses to expect for each port if its truely open c) The port maybe open port but doesn't return a response for example if you go to the http://networking.ringofsaturn.com/Tools/probe.php site and port scan for www.google.com you will see there is no actual response back for 80 or 443. The same is true for my own site I get no response back for 80,443, or 53 (DNS) but I do for 22 (SSH-2.0-OpenSSH_5.1p1 Debian-5 ), 25 (220 mydomain.com ESMTP Postfix (Debian/GNU) ), 143 * OK Dovecot ready. Therefore there must be something else to check or look for don't you think? Link to comment https://forums.phpfreaks.com/topic/185808-writing-a-port-scanner-that-correctly-identifies-open-closed-and-blocked/#findComment-984275 Share on other sites More sharing options...
RobReid Posted December 26, 2009 Author Share Posted December 26, 2009 Right I have got a little further and getting correct responses apart from HTTP (8000,8080) Just looking at the debug code responses for each port when the following code is run I noticed a pattern for those ports where fsockopen returned a resource object (which most scanners treat as open) // where $fp = response from fsockopen $stat=socket_get_status($fp); foreach($stat as $key=>$value){ echo "$key: $value<br />"; } In that for those ports that don't return a response when reading with fread such as "* OK Dovecot ready" for port 143 but are actually still open. Then the array item "EOF" was blank (false). And for those that returned no response and were actually blocked returned EOF=1 True. Therefore for items with a resource I check the EOF value as well as read the response. For those resources that have NO response AND EOF=True then I treat as blocked. This seems to work (in that it returned the same results for my site as http://networking.ringofsaturn.com/Tools/probe.php does) apart from the two HTTP ports 8000 and 8080 that are returning OPEN instead of CLOSED. I am checking this against my own website so what happens when you run the following test against your own website and compare the results with http://networking.ringofsaturn.com/Tools/probe.php. Do you get the same results? Maybe...its his code that is wrong for those ports as I can actually Telnet into my site on those two ports which suggests my code is correct. Also his site when scanning www.google.com only returns ports 80 and 443 as open and I can telnet into 8080 and 8000 which suggests that maybe his code is incorrect?? What do you think? I have put my function here. Its not written in the best manner ( e.g a class) as I am just testing at the moment and have removed lots of debug etc. Just wrap a loop with the host and the ports going into it e.g $ports = "20,21,22,23,25,53,80,143,443,8080,8000,10000"; $portlist = explode(",",$ports); // test ports foreach($portlist as $portval){ testPort($host,$portval); } function testPort($url,$port){ if (!getservbyport($port,"tcp")) {$pname = "N/A";} else {$pname = getservbyport($port,"tcp");} echo "Open $url port $port - $pname<br />"; $fp = @fsockopen($url, $port, $errno, $errstr, 5); $response=""; echo "errorno = $errno error = $errstr <br />"; if (is_resource($fp)){ echo "is resource! fp = $fp<br/>"; // check the status as if its not EOF then its actually open and responsive to TELNET requests $stat=socket_get_status($fp); $eof = $stat["eof"]; $blockedport = -1; // if response is at eof if(!$eof){ $blockedport = 0; }else{ // looks like its blocked but we will try for a response anyway $blockedport = 1; } // read original respone $response = trim(fread($fp,4096)); if(empty($response) && $blockedport==1){ $closed = true; echo "Port is not really open as we cannot pass commands to it and we had no response back<br />"; }else{ $closed = false; echo "Port is open as we got a response $response OR we were not blocked<br />"; } if($closed){ echo "no connection to port: $port - blocked<br />"; }else{ echo "connection is open - $response<br />'};"; } fclose($fp); }else{ echo "no connection to port: $port <br />"; // timeout error if($errno==10060){ echo "no connection to port: $port - timeout error<br />"; }else{ echo "no connection to port: $port - $errstr<br />"; } } flush(); return; } Let me know what you reckon. Link to comment https://forums.phpfreaks.com/topic/185808-writing-a-port-scanner-that-correctly-identifies-open-closed-and-blocked/#findComment-984319 Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.