Test TCP ports with Python and Scapy

Test Tcp Ports with Python and Scapy

Test TCP ports with Python and Scapy

Home » News » Test TCP ports with Python and Scapy
Table of Contents
Image

Photo by way of Gavin Allanwood on Unsplash

In Stop the usage of Telnet to check ports, I explored a number of selection instructions and scripts to check TCP connectivity. These instructions and scripts vary from fundamental assessments to extra subtle exams, however they’re restricted to the options supplied by way of supporting gear like Netcat.

There is another choice when you need outstanding keep an eye on and versatility to your TCP port exams: Do it your self. Programming languages like Python be offering socket programming APIs and get admission to to classy frameworks like Scapy to perform simply that.

[ Cheat sheet: Get a list of Linux utilities and commands for managing servers and networks. ]

Get began with a TCP port take a look at

Start with a easy TCP port take a look at in Python:

#!/usr/bin/env python3
"""
VERY easy port TCP port take a look at

Author: Jose Vicente Nunez <@[email protected]>
"""
import socket
from pathlib import Path
from typing import Dict, List
from argparse import ArgumentParser


def load_machines_port(the_data_file: Path) -> Dict[str, List[int]]:
    port_data = {}
    with open(the_data_file, 'r') as d_scan:
        for line in d_scan:
            host, ports = line.break up()
            port_data[host] = [int(p) for p in ports.split(',')]
    go back port_data


def test_port(deal with: str, dest_port: int) -> bool:
    check out:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            if sock.connect_ex((deal with, dest_port)) == 0:
                go back True
        go back False
    aside from (OSError, ValueError):
        go back False


if __name__ == "__main__":
    PARSER = ArgumentParser(description=__doc__)
    PARSER.add_argument("scan_file", kind=Path, assist="Scan file with list of hosts and ports")
    ARGS = PARSER.parse_args()
    knowledge = load_machines_port(ARGS.scan_file)
    for gadget in knowledge:
        for port in knowledge[machine]:
            check out:
                effects = test_port(gadget, port)
            aside from (OSError, ValueError):
                effects = False
            if effects:
                print(f"{machine}:{port}: OK")
            else:
                print(f"{machine}:{port}: ERROR")

This software opens the socket and assumes that any error way the port is closed.

Give it a check out:

$ ./tcp_port_scan.py port_scan.csv
google.com:80: OK
amazon.com:80: OK
raspberrypi:22: OK
raspberrypi:9090: OK
raspberrypi:8086: OK
raspberrypi:21: ERROR
dmaf5:22: OK
dmaf5:80: ERROR

It works as anticipated. But what if it’s essential use a framework that lets you skip all of the boilerplate whilst doing extra advanced issues?

Meet Scapy

Scapy describes itself as “a Python program that enables the user to send, sniff and dissect, and forge network packets.” Using this capacity, you’ll construct gear that may probe, scan, take a look at, or uncover networks.

Most Linux distributions have a package deal for Scapy. On Fedora, set up it like this:

$ sudo dnf set up -y python3-scapy.noarch

Scapy calls for increased privileges to run. If you make a decision to make use of pip, chances are you’ll do the next:

sudo -i
python3 -m venv /usr/native/scapy
. /usr/native/scapy/bin/turn on
pip set up --upgrade pip
pip set up wheel
pip set up scapy

Just take into account to turn on your digital setting prior to calling Scapy when you set up it that manner. You can use Scapy as a library or as an interactive shell. Next, I’ll display you a couple of packages.

Try a easy interactive TCP port scanner

In the interactive mode, you name the Scapy terminal as root, because it calls for increased privileges.

For that, you’re going to upload layers. First, upload an IP community layer:

IP(dst="raspberrypi.home")

Then upload TCP ports:

TCP(dport=[22,3000,8086]

Next, ship the packets and seize spoke back and unanswered effects:

(ans, notanws) = sr(*)

Then analyze the spoke back effects, filtering best open ports:

ans.abstract(lfilter = lambda s,r: r.sprintf("%TCP.flags%") == "SA",prn=lambda s,r: r.sprintf("%TCP.sport% is open"))

Here’s what you’ll be able to get:

$ sudo scapy3 -H
>>> (ans, notanws) = sr(IP(dst="raspberrypi.home")/TCP(dport=[22,3000,8086]))
Begin emission:
Finished sending 3 packets.

Received 5 packets, were given 3 solutions, closing 0 packets

>>> ans.abstract(lfilter = lambda s,r: r.sprintf("%TCP.flags%") == "SA",prn=lambda s,r: r.sprintf("%TCP.sport% is open"))
ssh is open
hbci is open
d_s_n is open

Not unhealthy for simply two strains of code, in comparison to 46 from the primary Python script.

Next. you’ll be able to create an automatic port scanner, the usage of what you realized prior to.

[ Download now: A system administrator’s guide to IT automation. ]

Create a Scapy-flavored customized port take a look at

The interactive shell is good if you end up exploring and experimenting to search out one of the best ways to take on an issue. But whenever you get a hold of an answer, you’ll make it a script:

#!/usr/bin/env -S sudo python3
"""
VERY easy port TCP port take a look at, the usage of Scapy
* 
* 
* 
* Please take a look at the unique script: 
Author: Jose Vicente Nunez <@[email protected]>
"""
import os
import sys
import traceback
from enum import IntEnum
from pathlib import Path
from random import randint
from typing import Dict, List
from argparse import ArgumentParser
from scapy.layers.inet import IP, TCP, ICMP
from scapy.packet import Packet
from scapy.sendrecv import sr1, sr

NON_PRIVILEGED_LOW_PORT = 1025
NON_PRIVILEGED_HIGH_PORT = 65534
ICMP_DESTINATION_UNREACHABLE = 3


elegance TcpFlags(IntEnum):
    """
    
    """
    SYNC_ACK = 0x12
    RST_PSH = 0x14


elegance IcmpCodes(IntEnum):
    """
    ICMP codes, to make a decision
    
    """
    Host_is_unreachable = 1
    Protocol_is_unreachable = 2
    Port_is_unreachable = 3
    Communication_with_destination_network_is_administratively_prohibited = 9
    Communication_with_destination_host_is_administratively_prohibited = 10
    Communication_is_administratively_prohibited = 13


FILTERED_CODES = [x.value for x in IcmpCodes]


elegance RESPONSES(IntEnum):
    """
    Customized responses for our port take a look at
    """
    FILTERED = 0
    CLOSED = 1
    OPEN = 2
    ERROR = 3


def load_machines_port(the_data_file: Path) -> Dict[str, List[int]]:
    port_data = {}
    with open(the_data_file, 'r') as d_scan:
        for line in d_scan:
            host, ports = line.break up()
            port_data[host] = [int(p) for p in ports.split(',')]
    go back port_data


def test_port(
        deal with: str,
        dest_ports: int,
        verbose: bool = False
) -> RESPONSES:
    """
    Test the deal with + port mixture
    :param deal with:  Host to test
    :param dest_ports: Ports to test
    :go back: Answer and Unanswered packets (filtered)
    """
    src_port = randint(NON_PRIVILEGED_LOW_PORT, NON_PRIVILEGED_HIGH_PORT)
    ip = IP(dst=deal with)
    ports = TCP(game=src_port, dport=dest_ports, flags="S")
    reset_tcp = TCP(game=src_port, dport=dest_ports, flags="S")
    packet: Packet = ip / ports
    verb_level = 0
    if verbose:
        verb_level = 99
        packet.display()
    check out:
        spoke back = sr1(
            packet,
            verbose=verb_level,
            retry=1,
            timeout=1,
            threaded=True
        )
        if now not spoke back:
            go back RESPONSES.FILTERED
        elif spoke back.haslayer(TCP):
            if spoke back.getlayer(TCP).flags == TcpFlags.SYNC_ACK:
                rst_packet = ip / reset_tcp
                sr(rst_packet, timeout=1, verbose=verb_level)
                go back RESPONSES.OPEN
            elif spoke back.getlayer(TCP).flags == TcpFlags.RST_PSH:
                go back RESPONSES.CLOSED
        elif spoke back.haslayer(ICMP):
            icmp_type = spoke back.getlayer(ICMP).kind
            icmp_code = int(spoke back.getlayer(ICMP).code)
            if icmp_type == ICMP_DESTINATION_UNREACHABLE and icmp_code in FILTERED_CODES:
                go back RESPONSES.FILTERED
    aside from TypeError:
        traceback.print_exc(record=sys.stdout)
        go back RESPONSES.ERROR


if __name__ == "__main__":
    if os.getuid() != 0:
        elevate EnvironmentError(f"Sorry, you need to be root to run this program!")
    PARSER = ArgumentParser(description=__doc__)
    PARSER.add_argument("--verbose", motion="store_true", assist="Toggle verbose mode on/ off")
    PARSER.add_argument("scan_file", kind=Path, assist="Scan file with list of hosts and ports")
    ARGS = PARSER.parse_args()
    knowledge = load_machines_port(ARGS.scan_file)
    for gadget in knowledge:
        m_ports = knowledge[machine]
        for dest_port in m_ports:
            ans = test_port(deal with=gadget, dest_ports=dest_port, verbose=ARGS.verbose)
            print(f"{ans.name} -> {machine}:{dest_port}")

This script is extra advanced than the primary, which makes use of Python by myself, nevertheless it gives a extra detailed clarification of the analyzed ports. You can run it like this: ./tcp_port_scan_scapy.py port_scan.csv:

$ ./tcp_port_scan_scapy.py port_scan.csv
OPEN -> google.com:80
OPEN -> amazon.com:80
OPEN -> raspberrypi:22
OPEN -> raspberrypi:9090
OPEN -> raspberrypi:8086
CLOSED -> raspberrypi:21
FILTERED -> dmaf5:22
FILTERED -> dmaf5:80

The effects for my gadget display one connection closed and two of them perhaps filtered.

The actual energy of Scapy is the extent of customization you currently have from a well-known language like Python. The shell mode is especially vital as you’ll troubleshoot community issues simply whilst performing some exploration paintings.

What to be informed subsequent

Developing a TCP port scanner the usage of a programming language like Python supplies a degree of flexibleness and customization this is onerous to reach with scripting by myself. By including a specialised library like Scapy, you’ll carry out much more advanced community packet manipulation. Read this educational for Scapy, and you’ll be able to be amazed at what you’ll do.

[ Network getting out of control? Check out Network automation for everyone, a complimentary book from Red Hat. ]

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 
share this article.

Enjoying my articles?

Sign up to get new content delivered straight to your inbox.

Please enable JavaScript in your browser to complete this form.
Name