In my recent article about python and how to build a network scanner we discussed two possibilities for finding available hosts on the local network. The next step is to find open ports for a single host on the network.
This can be achieved by finding the open ports. This is called port scanning. Every service on a machine offers connections to clients by registering a port number at the local operating system. This port is then bound to this service (server process). A host can run multiple services and by checking all possible ports, we can find out which services are running on that host.
The following code block takes an ip address and a port number as input, checks wether the specified port is open and in the case of success the port number is returend.
The python function which tries to establish a connection to this specfic port takes an ip address and port as input and returns 0 in case of success and in case of failure an error indicator is returend:
result = socket_obj.connect_ex((addr,port))
This function is defined in the
socket library and is basically all we need for a port scanner. We now just need a loop which we use to iterade over all ports we want to check, for every port we run
result = socket_obj.connect_ex((addr,port)) and check the result.
The code block shown above returns the open ports. Based on the IANA recommendation we then can assume which service is running behind the open port. This is useful for plannig the next steps of penetration testing: finding the possible exploits for this service.
Because this is a widely used technique, administrators trend to swap the standard ports for service: When running a server - assuming the administration is done using ssh - and checking the ssh service logs, we notice that a lot of ip addresses are listed which tried to connect to the ssh service, usernames are well chosen. The following is an excemption of an ssh auth log. It shows the source ip address and the username which tried to log into the server using ssh on port 22:
Sysadmins know about these "attacks" and a common method to prevent attackers to recognise the ssh service is to bind another port for ssh and close port 22.
Now banner grabbing comes into the game. Banner grabbing is used to recognise a service for a port. Also banner grabbing is quite simple: We try to connect to the open port, wait for a response from the service and display it. As you can see in the screenshot above, this method is quite effective. We recognised 5 open ports and banner grabbing reveals additional beneficial information, such as the name/kind of the server processes.
Banner grabbing with Python can be performed like this:
An additional benefit of banner grabbing can be the detection of the server operating system. The defacto standard port scanner Nmap performs OS detection based on specific patterns. This is a great help but depending on the security mechanisms it may not be very accurate (see picture below). As you can see in the image above, banner grabbing on port 22 reveales not only that ssh is running on that port it also reveales the server operating system.
The following image shows the port scan results for another host using Nmap as the scanning engine and with OS recognition attempt enabled. The OS was detected correctly but the distribution and kernel revision is still unknown. In the picture above the server OS was revealed by using banner grabbing.
When it comes to penetration testing or just a simple analysis the network scanner is one major tool for analyzing which hosts are available on the local network. Today there exist lots of tools, but depending on what you want to do it is a good idea to write your own analysis and penetration testing tools.
In this case we start with a simple network scanner, which lists the available hosts on your network. To get this done there are two (basically some more but we stick to the two simplest ones) possibilities:
- ICMP Echo Request
- TCP Scan
ICMP Echo request
This is well known by using the ping command. An ICMP packet is send to the specified (by IP address) host and if an ICMP echo is received, it is clear that the host is online. This can be used as a network scanner by just trying all available IP addresses. Enabling multi-threading delivers great performance improvements. I do not want to go into further detail here because this can be realized programmatically simple:
for host in available_ip_addresses:
print(ping -i 1 host)
That's basically it. Iterate through all available IP addresses ping them and check the reply.
This relies on the assumption that network devices have ICMP enabled. Personal firewalls or general firewalls are often set to so called "stealth mode" which means they do not react to ICMP echo requests. In this case no echo is received and it looks like the host is down.
To overcome this, the TCP scan can help. This relies on the assumption that hosts on the network have open ports where a client can connect to (see 3-Way-Handshake). Here we have to guess ports which may be open. This is mainly dependend on the OS but also on the settings. It is likely that some ports are open but not guaranteed. The following ports may be open dependent on the operating systems:
mac = [22, 445, 548, 631]
linux = [20, 21, 22, 23, 25, 80, 111, 443, 445, 631, 993, 995]
windows = [135, 137, 138, 139, 445]
aios = [49152, 62078] # Apple iOS (ios is also the name for Cisco's OS running on their products)
When now writing a network scanner we can iterate over all the possible IP addresses on the network and for every IP we try to connect to one of the ports. If the connection is successful (ACK received) we know that this host is available at the given IP address.
To check whether a connection succeeds or fails we can do the following in Python:
socket_obj.connect_ex((addr,port)) returns 0 if the connection succeeds, otherwise it delivers an error indicator.
This is basically all the "magic" behind a network scanner.
The provided network scanner can simply be used by the following command:
python avail_hosts_tcp_scan.py <networkaddress> <fromhost> <tohost> <hosttypes>
python avail_hosts_tcp_scan.py 192.168.0.1 1 10 windows
hosttypes are the above mentioned systems which have different default ports opened, use:
windows, linux, mac, aios, unspec
unspec uses the complete port range.