# Python Port Scanning With Threads

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](https://blog.octachart.com/port-scanning-with-python) about this subject and you may want to [check it out](https://blog.octachart.com/port-scanning-with-python) 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](https://cdn.hashnode.com/res/hashnode/image/upload/v1659196163334/2eivADUbl.png align="left")

As I mentioned in [the blog I first wrote](https://blog.octachart.com/port-scanning-with-python), 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``` <br>
The ```argparse``` module makes it easy to write user-friendly command-line interfaces. 

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

 📌 ```colorama ```<br>
This has the upshot of providing a simple cross-platform API for printing coloured terminal text

  📌 ```threading``` <br>
To create a multi-threaded application since it will involve waiting.

  📌 ```queue``` <br>
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;

<center>

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1659194755958/-5n3ZLm04.png align="left")

</center>

For the full code, visit [repo](https://github.com/Ronlin1/PortScanning__faster).

### 🌟 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](https://cdn.hashnode.com/res/hashnode/image/upload/v1657097866062/lOsaHYsYe.png align="left")

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.

<center>

<a href="https://www.buymeacoffee.com/RonnieAtuhaire" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Ronnie A Coffee" height="41" width="174"></a>

📢 You can also follow me on [Twitter](https://twitter.com/intent/follow?screen_name=AfroBoyUg) : ♥ ♥  Waiting for you! 🙂
