Port scanning with Python

Port scanning is commonly known for its use by hackers and network security testers to determine which services are running on a target host, and to identify whether potentially dangerous ports are open for communication.  However, it is also a useful tool used by network engineers to troubleshoot network connections. In this post I will be covering how to perform port scanning with Python using a simple IP address scanning class.

Disclaimer: Only scan target hosts that you own, or have explicit permission to scan.  Unauthorised port scanning may be considered illegal in your jurisdiction. By using this Python code, you are taking responsibility for your own actions.

For the legalities of hacking see this resource HACKING – What’s LEGAL & What’s NOT [commission paid link].

What is port scanning?

Before we explain port scanning, let’s first understand what is a port:

In computer networking, port numbers are used to connect to processes or specific services on a network host. Port numbers are standardized by the Internet Assigned Numbers Authority (IANA), with well know destination ports for major applications, such as internet and email, occupying reserved ports from 1 to 1023.

Remaining ports (1024 – 65535), also known as ephemeral ports, are less restricted and are normally used by applications (on the server side) or for a short duration, typically the session duration on the client side of a client-server connection.

Port numbers are used by transport protocols such as Transmission Control Protocol (TCP) and User Datagram Protocol (UDP). The port scanner in the code below is specifically targeted at TCP ports.

So then, what is port scanning?

Simply put, port scanning is an activity that tests if the target ports on a given host are open. To determine whether the port is available for communication (data can be sent or received through the port), the IP address and target ports of the host are systematically tested to see if a connection can be established.

Set up a ScanIP class

To get started port scanning with Python, we will use a class called ScanIP to hold the IP address and a list of default ports for the host to be scanned. The list of default ports can be overridden when we run the scan against the target host.

We use Python’s built-in ipaddress (IPv4/IPv6 manipulation library) module to hold the IP address in the __init__ constructor for the class. This has the benefit of checking that a valid IPv4  address is entered when the ScanIP class is instantiated.

The socket library will be used to attempt to create a TCP connection to the in-scope ports on the target host in order to determine whether the port is open.

import ipaddress
import socket

class ScanIP:

    default_ports = [21, 22, 23, 25, 53, 80, 110, 135, 139, 143, 443, 
        445, 587, 993, 1433, 3306, 3389, 5900, 8080]

    def __init__(self, ip):
        self.ip = format(ipaddress.ip_address(ip))

Define a method to scan a single port

Next we need to define a method (scan_port) that can attempt to open a socket connection to a specific port of the target host. The method will return True if the port is open and False if the port is likely closed.

The method requires a port number and an optional timeout parameter (if not supplied, timeout argument defaults to 2 seconds). The socket session for the TCP connection is stored in the variable s, and the timeout is set.

The connection attempt is managed by try, except and finally code blocks, so that any refused connections and connection timeouts are treated as False and the connection is adequately closed.

The connect method is called on s, with IP address and port number as arguments, in order to attempt to open a connection to the target port. If a connection to the port is successful, this is printed to the terminal and method returns True:

    def scan_port(self, port, timeout=2):
        """scan a  single TCP port"""
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        try:
            s.connect((self.ip, port))
            print ("Port %d: OPEN" % (port,))
            return True
        except (ConnectionRefusedError, socket.timeout) as e:
            return False
        finally:
            s.close()

Define a method to scan a list of ports

To scan multiple ports, we use a list of ports and iterate over the port numbers, calling the scan_port method on each port number.
This is defined in the scan method, which takes a list of ports as input. If no list of ports is supplied, the default list is used.
A dictionary of results is returned by scan:

    def scan(self, ports=default_ports):
        """scan a list of TCP ports"""
        open_ports = {}
        for port in ports:
            portscan = self.scan_port(port)
            open_ports[port] = portscan
        return open_ports

Test the scan_port function

That should complete the class to scan the ports on a target host. Now let’s implement some test code to illustrate how to use the class.
First, create a ScanIP object, supplying an IP address as input. In the example, this is stored in a variable, target.
We can then call the scan method on target. (In the example, no list of ports is supplied as argument, so the default ports are used.)
The results (dictionary object) are stored in a variable result, which we then print to the terminal.

if __name__ == "__main__":

    target = ScanIP("192.168.0.1")

    # using default ports
    result = target.scan()
    
    print(result)

Example output from the above code is displayed below:

Port 53: OPEN
Port 80: OPEN
Port 443: OPEN
{21: False, 22: False, 23: False, 25: False, 53: True, 80: True, 110: False, 
135: False, 139: False, 143: False, 443: True, 445: False, 587: False, 
993: False, 1433: False, 3306: False, 3389: False, 5900: False, 8080: False}
[Finished in 132ms]

I hope you found this code useful.

If you liked this post, you should definitely go and check out my previous post on Scanning HTTP headers for vulnerabilities with Python.

As usual, all the code in this post can be found on my Github page.

Leave a Reply

Your email address will not be published. Required fields are marked *