In this blog post, we'll dive into the creation of an advanced port scanner using Python and Scapy, a powerful packet manipulation library. Scapy allows for fine-grained control over network packets, making it an ideal choice for developing custom network tools.

We'll implement a simple TCP port scanner first, and then add the capability for SYN port scanning, also known as half-open scanning, which sends TCP SYN packets to a target's ports. If a port responds with a SYN-ACK, it indicates the port is open. The scanner then sends an RST to avoid completing the TCP handshake.

Disclaimer: This tutorial is for educational purposes only. Unauthorized port scanning is illegal and unethical. Always obtain explicit permission before scanning any network or system. Use this knowledge responsibly and within legal boundaries.

Introduction

The TCP handshake (also known as three-way handshake) is the way socket connections are established in the TCP protocol, TCP is focused on reliability of data transfer, so this handshake is a way to ensure both machines are ready to exchange information.

Simple TCP scanner

Let's create a function in python to do the full TCP handshake on a desired host and port, establishing a connection.

# Function to perform a full TCP connect scan
def full_tcp_scan(target, port):
    try:
        # create and connect to a socket
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((target, port))

        # If the TCP handshake was successfull, return true
        return True
    except:
        # If the TCP handshake was unsuccessfull, return false
        return False

main.py

Here we create a socket, and try to connect to the host and port. If successfull we return True, and if not we return False.

SYN scanner

For this type of scan, we will need to install a library called scapy and import it into our script

pip install scapy

Scapy allows us to manually craft a SYN packet and detect the type of response we get from the host

# Function to perform the SYN scan
def syn_scan(target, port):
    syn_packet = IP(dst=target) / TCP(dport=port, flags='S')
    # Send the SYN packet and wait for a response
    response = sr1(syn_packet, timeout=3, verbose=False)
    
    if response is not None:
        if response.haslayer(TCP):
            if response.getlayer(TCP).flags == 0x12:  # SYN-ACK
                # Send RST packet to close the connection
                sr(IP(dst=target) / TCP(dport=port, flags='R'), timeout=1, verbose=False)
                return True
            elif response.getlayer(TCP).flags == 0x14:  # RST-ACK
                return False
    else:
        return False

main.py

Here, we create a packet with the 'S' flag, which means it's a SYN packet.

The flags can also be represented in hexadecimal format, and that's how we're checking the response, where 0x12 represents a SYN-ACK response, and 0x14 represents an RST-ACK response.

RST responses are used to terminate a connection, meaning that the communication attempt is denied, that's what we send when we find an open port to avoid finishing the three-way handshake.

Threading

For now, our functions can only scan a single port, but usually while pentesting we need to scan multiple ports, or a range of ports. Let's create a queue and multiple threads to execute our port scanning over multiple ports quickly.

First, we need to import some native python libraries and create the worker functions for our two types of scans

import threading
from queue import Queue

# Queue worker that runs the tcp scan for each port that was queued
def full_tcp_scan_worker(scan_queue, target):
    while not scan_queue.empty():
        port = scan_queue.get()
        if full_tcp_scan(target, port):
            print(f'Port {port}: Open')
        else:
            print(f'Port {port}: Closed')
        scan_queue.task_done()

# Queue worker that runs the tcp scan for each port that was queued
def syn_scan_worker(scan_queue, target):
    while not scan_queue.empty():
        port = scan_queue.get()
        if syn_scan(target, port):
            print(f'Port {port}: Open')
        else:
            print(f'Port {port}: Closed')
        scan_queue.task_done()

main.py

Then, we can create a method to create and start our threads. I'm making it receive the worker as a parameter so it can work with both scanners

def threaded_scan(target, ports, worker):
    # Number of threads
    threads = 500
    thread_list = []
    scan_queue = Queue()
    
    # Add all ports to be scanned in a queue for our worker to process
    for port in ports:
        scan_queue.put(port)
    
    # Create and start threads
    for i in range(threads):
        thread = threading.Thread(target=worker, args=(scan_queue, target))
        thread_list.append(thread)
    
    for thread in thread_list:
        thread.start()
    
    # Wait for all threads to finish before printing results
    for thread in thread_list:
        thread.join()

main.py

Then it's easy to execute our scans with multithreading by simply calling our threaded_scan function, like so

target = '127.0.0.1'
ports = [80, 3000, 8080, 6333, 6334, 11434]

print('Running full tcp scan:')
threaded_scan(target, ports, full_tcp_scan_worker)
print('\nRunning syn scan:')
threaded_scan(target, ports, syn_scan_worker)

main.py

And now we can run our port scanner script! Note that we need to run as root since the scapy lib uses some network functionality that is prohibited by the OS to regular users.

To run this without sudo, you need to give python access to these network capabilities using the following command:

setcap cap_net_raw=eip /usr/bin/pythonX.X

Where X.X is your python version.

Either way, running our script should give a result like this

$ sudo python main.py
Running full tcp scan:
Port 80: Closed
Port 3000: Open
Port 8080: Closed
Port 6333: Open
Port 6334: Open
Port 11434: Open

Running syn scan:
Port 80: Closed
Port 3000: Open
Port 8080: Closed
Port 6333: Open
Port 6334: Open
Port 11434: Open

And there's our python port scanner. I hope this tutorial was useful for you, if you enjoy my content, subscribe for free to receive my weekly newsletter with more tech-related content.

Full source code of all my projects is also available for paying members, if you want to support my work, consider subscribing as a PLUS or PRO member. Thanks!

Writing an advanced port scanner in Python with Scapy