Introduction
Network security and reconnaissance are essential skills for cybersecurity professionals. In this blog post, we will build a Python-based network scanner that performs ARP scanning, port scanning, and DNS resolution using the scapy, socket, dns.resolver, and threading libraries. We will also use rich for better console output.
Overview of the Scanner
- Perform an ARP scan to discover active hosts on the network.
- Scan common ports on discovered hosts.
- Resolve DNS records for a given domain.
Flowchart of the Scanner:
Script Breakdown
Parsing Command-Line Arguments
The script accepts command-line arguments using argparse to select between port scanning and DNS scanning.
ARP Scanning
The Address Resolution Protocol (ARP) scan sends requests to devices in the network and collects responses to determine active hosts.
Port Scanning
For each discovered active host, the script attempts to connect to common ports (e.g., 22, 80, 443) to check if they are open.
DNS Resolution
If DNS mode is enabled, the script retrieves the A records (IPv4 addresses) of a given domain.
Code
import argparse # Importing argparse for handling command-line arguments
import socket # Importing socket for network communication
import scapy.all as scapy # Importing scapy for ARP scanning
import dns.resolver # Importing dns.resolver for DNS scanning
import threading # Importing threading for concurrent port scanning
from rich.console import Console # Importing rich.console for formatted console output
from rich.table import Table # Importing rich.table for displaying results in table format
from rich.text import Text # Importing rich.text for text formatting
from rich import print # Importing print from rich to enhance terminal output
console = Console() # Initializing a rich console instance
def parser():
global target, port_enabled, dns_enabled # Defining global variables to store user inputs
arg_parser = argparse.ArgumentParser(description="Options for scanning") # Creating an argument parser
arg_parser.add_argument('-t', "--target", help="Specify target explicitly") # Adding argument for target IP/domain
req_args = arg_parser.add_mutually_exclusive_group(required=True) # Ensuring only one option (port or DNS) is selected
req_args.add_argument("--port", action="store_true", help="Enable port scanning") # Port scanning option
req_args.add_argument("--dns", action="store_true", help="Enable DNS scanning") # DNS scanning option
args = arg_parser.parse_args() # Parsing command-line arguments
target = args.target # Storing target input
port_enabled = args.port # Storing port scanning option
dns_enabled = args.dns # Storing DNS scanning option
def arp_scan():
global active_hosts # Defining a global list to store active hosts
active_hosts = [] # Initializing an empty list
# Creating an ARP request to discover active devices in the network
arp_request = scapy.ARP(pdst=target if target else "192.168.1.1/24")
broadcast = scapy.Ether(dst='ff:ff:ff:ff:ff:ff') # Creating an Ethernet frame for broadcast
req_broadcast = broadcast / arp_request # Combining Ethernet frame with ARP request
clients = scapy.srp(req_broadcast, timeout=1, verbose=False)[0] # Sending request and capturing responses
# Creating a table for displaying active hosts
table = Table(title="Active Hosts", show_header=True, header_style="bold cyan")
table.add_column("IP Address", style="green")
table.add_column("MAC Address", style="magenta")
table.add_column("Device Name", style="yellow")
for sent, received in clients: # Iterating through received ARP responses
ip = received.psrc # Extracting IP address
hostname = get_hostname(ip) # Fetching device name
active_hosts.append(ip) # Adding IP to active hosts list
table.add_row(ip, received.hwsrc, hostname) # Adding row to table
console.print(table) # Displaying the table
def get_hostname(ip):
try:
hostname = socket.gethostbyaddr(ip)[0] # Resolving hostname from IP
return hostname # Returning the hostname
except socket.herror:
return "Unknown" # Returning 'Unknown' if resolution fails
def scan_port(host, port):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: # Creating a socket object
s.settimeout(1) # Setting a timeout for connection attempts
result = s.connect_ex((host, port)) # Checking if port is open
if result == 0:
console.print(f"[bold green]Open port {port} on {host}[/bold green]") # Printing open ports
except Exception as e:
console.print(f"[bold red]Error scanning {host}:{port} - {e}[/bold red]") # Handling errors
def multi_thread_scan_ports(host, ports):
threads = [] # Creating a list to store threads
for port in ports: # Iterating over ports to scan
thread = threading.Thread(target=scan_port, args=(host, port)) # Creating a new thread
thread.start() # Starting the thread
threads.append(thread) # Adding thread to the list
for thread in threads:
thread.join() # Waiting for all threads to complete
def dns_scan():
try:
result = dns.resolver.resolve(target, 'A') # Resolving A records of the target domain
table = Table(title="DNS MX Records", show_header=True, header_style="bold cyan") # Creating a table for results
table.add_column("Mail Exchange (MX) Records", style="yellow")
for val in result:
table.add_row(str(val)) # Adding resolved DNS records to the table
console.print(table) # Printing the table
except Exception as e:
console.print(f"[bold red]DNS scan failed: {e}[/bold red]") # Handling errors
if __name__ == "__main__":
ports_to_scan = [21, 22, 23, 25, 53, 80, 443, 8080] # Defining common ports to scan
parser() # Parsing command-line arguments
if port_enabled:
console.print("[bold cyan]Starting ARP scan...[/bold cyan]") # Indicating ARP scan start
arp_scan() # Running ARP scan
for host in active_hosts:
console.print(f"[bold yellow]Scanning ports on {host}...[/bold yellow]") # Indicating port scan start
multi_thread_scan_ports(host, ports_to_scan) # Scanning ports using multithreading
else:
console.print("[bold cyan]Starting DNS scan...[/bold cyan]") # Indicating DNS scan start
dns_scan() # Running DNS scan
Running the Scanner
To run the scanner, use one of the following commands:
- Port scanning:
python scanner.py -t 192.168.1.1 --port
- DNS scanning:
python scanner.py -t example.com --dns
Hey, I'm CJ, a curious mind who loves breaking down tech stuff into simple, fun stories. If you enjoyed this read, follow me for more posts like this, and check out my other socials to see what I'm building next!
X (Formerly Twitter) | GitHub | Linkedin
Top comments (0)