If you want to know how to create a network scanner tool in python in a few lines, without effort, you are in the right place:
We are going to build a tool for network analysis, in particular for host and service scanning.
This a very important phase of reconnaissance, and as usual, we will not use any external tool.
The whole procedure in our case requires a machine with Linux Mint installed but works well in other OS with small changes.
The final result of the project will therefore be a command-line tool to which we will pass in input an IP in the case of port scanning or a range in the case of host scanning.
In order to solve the problem we will use different approaches:
During the first part, i.e. the host scanning the Python library of reference will be Scapy, for the service scanning instead we will use directly a TCP socket.
Preparation
Before starting the code session we need to verify if we have all that we need.
If we do not have scapy installed, we type in the terminal:
pip install scapy
After this brief introduction, let’s get straight down to writing the code and see the imports:
from scapy.all import *
from optparse import OptionParser
from socket import *
Python host scanner method in a few lines
As far as host scanning is concerned, we define a method in which we will use an ARP packet sent in broadcasting (ARP is the protocol devised for IP-MAC ADDRESS mapping).
The method may be a little imprecise, but it is the fastest and is enough for our scope, a possible extension is the introduction of other scanning modes.
But let’s see the code before commenting on it:
def get_hosts(dst):
ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=options.dst),timeout=2)
alive = [(a.answer[Ether].src, a.answer[ARP].psrc ) for a in ans]
return alive
Scapy allows us to construct an ARP packet to which we pass the broadcasting address as the destination address, to which we ask for the MAC address of the IP passed as a parameter.
The SRP (send receive packet) method returns a tuple containing respectively the requests that have not received an answer and those that have.
The next step is to create an “alive” list containing tuples with MAC and IP addresses.
To obtain these values simply iterate over the “ans” list containing the answered calls and
read the values this way (a.answer[Ether].src, a.answer[ARP].psrc ) where “a” is the element in the list.
Switch to the Python port scanner method
After seeing how host scanning works with Scapy, let’s see how we can do service scanning with a TCP socket:
def scan_port(dst, port):
sock = socket(AF_INET, SOCK_STREAM)
setdefaulttimeout(2)
if sock.connect_ex((dst, port)) == 0:
return True
return False
In the code, we create a TCP socket called “sock”. AF_INET and SOCK_STREAM are the constants to pass to the constructor to create a TCP socket.
Then we set a timer that indicates the timeout in seconds.
And the final step is to attempt a connection with the ip and port passed as parameters.
If the connection is successful the final result is 0 and so we return True, and False otherwise.
The main method, parsing options
The building of our network scanner tool in python is complete, anyway, we need another step.
Finally, let’s look at the main, where we’ll take the input arguments to decide which method to call and then pass the correct values to it.
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-d", '--dst', action="store", dest="dst", help="The host/host-range to scan")
parser.add_option("-p", '--port', action="store_true", dest="port", help="Indicates the wish to scan the ports of the selected host")
options, args = parser.parse_args()
if not options.port:
alive = get_hosts(options.dst)
print("="*45)
for c, (a, i) in zip(range(1, len(alive)+1),alive):
print(f"[{c}] MAC: {a}\tIP: {i}")
else:
for p in range(1024):
scan_result = scan_port(options.dst, p)
if scan_result:
print(f"[+] Port {p} is open")
The input values are taken from the optparse library and stored in an object called “options”.
If we don’t input the argument “-p” the script will call the “get_hosts” method and display the results on screen,
otherwise it will call the scan_port method for all standard ports from 0 to 1023, showing the ports that are open.
Put all together
Now let’s see the complete code:
from scapy.all import *
from optparse import OptionParser
from socket import *
def get_hosts(dst):
ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=options.dst),timeout=2)
alive = [(a.answer[Ether].src, a.answer[ARP].psrc ) for a in ans]
return alive
def scan_port(dst, port):
sock = socket(AF_INET, SOCK_STREAM)
setdefaulttimeout(2)
if sock.connect_ex((dst, port)) == 0:
return True
return False
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-d", '--dst', action="store", dest="dst", help="The host/host-range to scan")
parser.add_option("-p", '--port', action="store_true", dest="port", help="Indicates the wish to scan the ports of the selected host")
options, args = parser.parse_args()
if not options.port:
alive = get_hosts(options.dst)
print("="*45)
for c, (a, i) in zip(range(1, len(alive)+1),alive):
print(f"[{c}] MAC: {a}\tIP: {i}")
else:
for p in range(1024):
scan_result = scan_port(options.dst, p)
if scan_result:
print(f"[+] Port {p} is open")
And now some examples of use, remembering that it must be launched with root permissions:
In the case of a local network with network address 192.168.1.0 and netmask 255.255.255.0
if we want to display the active hosts we will call the script with:
sudo python3 scanner.py --dst 192.168.1.0/24
If, on the other hand, after the first scan we have identified our target in a host (for example, 192.168.1.106) we will run the script as follows:
sudo python3 scanner.py --dst 192.168.1.106 -p
And more in general:
# Host scanner
sudo python3 scanner.py --dst <IP_RANGE>
# Port scanner
sudo python3 scanner.py --dst <TARGET_IP> -p
Further readings
Now we have seen how to create our network scanner tool and you can keep learning.
if you found this interesting you can take a look at those articles: