Ronnie Atuhaire
Ronnie Atuhaire's Blog

Ronnie Atuhaire's Blog

Python Port Scanning With Threads

Python Port Scanning With Threads

Ronnie Atuhaire's photo
Ronnie Atuhaire
Jul 30, 2022

5 min read

Subscribe to my newsletter and never miss my upcoming articles

Table of contents

  • Just a recap, what are ports?
  • Most Exploited Ports
  • Code
  • Conclusion

Port scanning is part of the first phase of a penetration test and allows you to find all network entry points available on a target system. I have a full introductory article about this subject and you may want to check it out before you proceed.

Just a recap, what are ports?

A port is a virtual array used by computers to communicate with other computers over a network. A port is also referred to as the number assigned to a specific network protocol. A network protocol is a set of rules that determine how devices transmit data to and fro on a network.

The two most common types of network protocols are the Transmission Control Protocol (TCP) and the User Datagram Protocol (UDP).

image.png

As I mentioned in the blog I first wrote, scanning ports is an important part of penetration testing. It allows you to identify and exploit vulnerabilities in websites, mobile applications, or systems.

Most Exploited Ports

22

Port 22 is subject to countless, unauthorized login attempts by hackers who are attempting to access unsecured servers. A highly effective deterrent is to simply turn off Port 22 and run the service on a seemingly random port above 1024 (and up to 65535).

20 & 21

Ports 20 and 21 are solely TCP ports used to allow users to send and receive files from a server to their personal computers. FTP stands for File Transfer Protocol. The FTP port is insecure and outdated and can be exploited

23

The Telnet protocol is a TCP protocol that enables a user to connect to remote computers over the internet. The Telnet port has long been replaced by SSH, but it is still used by some websites today. It is outdated, insecure, and vulnerable to malware. Telnet is vulnerable to spoofing, credential sniffing, and credential brute-forcing.

Obviously there are more ports than this lie Port 69(TFTP), HTTP / HTTPS (443, 80, 8080, 8443), SMB (139, 137, 445).

So let's code a faster solution for port scanning because you may want to test your home router or network and patch yourself as early as possible.

I will be using the following libraries;

argparse
The argparse module makes it easy to write user-friendly command-line interfaces.

socket
Python's standard library consists of various built-in modules that support interprocess communication and networking.

colorama
This has the upshot of providing a simple cross-platform API for printing coloured terminal text

threading
To create a multi-threaded application since it will involve waiting.

queue
The queue module implements multi-producer and multi-consumer queues. It is especially useful in threaded programming when information must be exchanged safely

Code

import argparse
import socket # for connecting
from colorama import init, Fore

from threading import Thread, Lock
from queue import Queue

Now let's add some colours


Let's create a number of threads we shall have in our application to have a faster search/scan. You can edit this.

Also, let's initialise a queue!

N_THREADS = 200
# thread queue
q = Queue()
print_lock = Lock()

In multithreading when multiple threads are working simultaneously on a shared resource like a file(reading and writing data into a file), then to avoid concurrent modification error(multiple threads accessing the same resource leading to inconsistent data) some sort of locking mechanism is used.

Now, let's create our scanning function using basic socket methods.

def port_scan(port):
    """
    Scan a port on the global variable `host`
    """
    try:
        s = socket.socket()
        s.connect((host, port))
    except:
        with print_lock:
            print(f"{GRAY}{host:15}:{port:5} is closed  {RESET}", end='\r')
    else:
        with print_lock:
            print(f"{GREEN}{host:15}:{port:5} is open    {RESET}")
    finally:
        s.close()

This is going to be our scanning thread method that will assist us on the particular threads at hand and it will call the above function. Refer to comments for more explanation.

def scan_thread():
    global q
    while True:
        # get the port number from the queue
        worker = q.get()
        # scan that port number
        port_scan(worker)
        # tells the queue that the scanning for that port
        # is done
        q.task_done()

Finally, we create our main method; Please consider my comments for code explanation.

def main(host, ports):
    global q
    for t in range(N_THREADS):
        # for each thread, start it
        t = Thread(target=scan_thread)
        # when we set daemon to true, that thread will end when the main thread ends
        t.daemon = True
        # start the daemon thread
        t.start()

    for worker in ports:
        # for each port, put that port into the queue
        # to start scanning
        q.put(worker)

    # wait the threads ( port scanners ) to finish
    q.join()

Now, let's test our script;

if __name__ == "__main__":
    # parse some parameters passed
    parser = argparse.ArgumentParser(description="Simple port scanner")
    parser.add_argument("host", help="Host to scan.")
    parser.add_argument("--ports", "-p", dest="port_range", default="1-65535", help="Port range to scan, default is 1-65535 (all ports)")
    args = parser.parse_args()
    host, port_range = args.host, args.port_range

    start_port, end_port = port_range.split("-")
    start_port, end_port = int(start_port), int(end_port)

    ports = [ p for p in range(start_port, end_port)]

    main(host, ports)

To run the above script, you will have to add an argument in the command line for the target IP. This could be your default gateway IP, home router etc.

Eg;

py  port_scanner.py 192.168.4.1

The above will search for the 65535 default ports. But if you wish to specify, you can do something like this;

py  port_scanner.py 192.168.4.1 --ports 1-1024

For example, on my side; I have an output like this;

image.png

For the full code, visit repo.

Conclusion

Once again, hope you learned something today from my little closet.

Please consider subscribing or following me for related content, especially about Tech, Python & General Programming.

image.png

You can show extra love by buying me a coffee to support this free content and I am also open to partnerships, technical writing roles, collaborations and Python-related training or roles.

Buy Ronnie A Coffee

You can also follow me on Twitter : Waiting for you!

Share this