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.ย ]
No Comment! Be the first one.