Hey there, fellow threat hunters! 👋 Today we're going to build something fun and educational - a basic port scanner in Python. Because sometimes you need to think like the attackers to better defend your systems. (Just remember: only scan systems you own or have explicit permission to test!)
Our Scanner vs. Nmap
While building your own tools is educational, let's be honest - in production environments, you'll probably want to use Nmap. Here's why:
1. Feature Comparison
Our Scanner:
- Basic TCP connect scanning
- Simple service detection
- Multi-threading
- ~100 lines of code
Nmap:
- Multiple scan types (SYN, FIN, NULL, XMAS, etc.)
- OS detection
- NSE (Nmap Scripting Engine)
- Version detection
- Timing templates
- Proven reliability
2. Why Learn Both?
Understanding how to build a basic scanner helps you:
- Better understand what Nmap is doing under the hood
- Debug network issues more effectively
- Customize scanning behavior for specific needs
- Create specialized tools when Nmap isn't available
3. Equivalent Nmap Commands
Our scanner's functionality can be replicated in Nmap like this:
# Basic scan (like our default)
nmap -p1-1024 target.com
# Fast scan with threads (like our threaded version)
nmap -p1-1024 -T4 target.com
# With service detection (better than our service detection)
nmap -p1-1024 -sV target.com4. When to Use What
Use our scanner when:
- Learning about network programming
- Need a very specific, custom scanning behavior
- Want to integrate scanning into a larger Python application
- Can't install Nmap on the system
Use Nmap when:
- Need reliable, production-ready scanning
- Want advanced features like OS detection
- Need to run complex scanning scripts (NSE)
- Time is critical (Nmap is much faster!)
Remember: Nmap took years of development and community testing to become what it is. Our scanner is a learning tool - think of it as "Nmap 101" rather than a replacement! 😉
What We're Building
We'll create a simple but effective port scanner that can: - Scan a range of ports on a target host - Identify open ports and their potential services - Handle both TCP and UDP scanning - Provide clear, formatted output - Support multiple threads for faster scanningThe Basic Scanner
Let's start with a simple version and then build it up:import socket
import threading
from queue import Queue
import time
import argparse
from typing import List, Tuple
class PortScanner:
    def __init__(self, target: str, start_port: int = 1, end_port: int = 1024, threads: int = 50):
        self.target = target
        self.start_port = start_port
        self.end_port = end_port
        self.threads = threads
        self.queue = Queue()
        self.results = []
        
    def _is_port_open(self, port: int) -> Tuple[int, bool, str]:
        """Test if a port is open."""
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.settimeout(1)
                result = s.connect_ex((self.target, port))
                if result == 0:
                    try:
                        service = socket.getservbyport(port)
                    except:
                        service = "unknown"
                    return port, True, service
            return port, False, ""
        except:
            return port, False, ""
            
    def _worker(self):
        """Worker thread to process the port queue."""
        while True:
            port = self.queue.get()
            if port is None:
                break
            result = self._is_port_open(port)
            if result[1]:  # if port is open
                self.results.append(result)
            self.queue.task_done()
            
    def scan(self) -> List[Tuple[int, bool, str]]:
        """Start the port scanning process."""
        start_time = time.time()
        
        # Fill the queue with ports
        for port in range(self.start_port, self.end_port + 1):
            self.queue.put(port)
            
        # Start worker threads
        thread_list = []
        for _ in range(self.threads):
            t = threading.Thread(target=self._worker)
            t.start()
            thread_list.append(t)
            
        # Add sentinel values to stop threads
        for _ in range(self.threads):
            self.queue.put(None)
            
        # Wait for all threads to complete
        for t in threading.Thread:
            t.join()
            
        end_time = time.time()
        self.scan_time = end_time - start_time
        return sorted(self.results)Making It Usable
Now let's add a nice command-line interface:def main():
    parser = argparse.ArgumentParser(description="Simple Python Port Scanner")
    parser.add_argument("target", help="Target host to scan")
    parser.add_argument("-s", "--start", type=int, default=1, help="Start port (default: 1)")
    parser.add_argument("-e", "--end", type=int, default=1024, help="End port (default: 1024)")
    parser.add_argument("-t", "--threads", type=int, default=50, help="Number of threads (default: 50)")
    args = parser.parse_args()
    # Create and run scanner
    scanner = PortScanner(args.target, args.start, args.end, args.threads)
    print(f"\nStarting scan on host {args.target}")
    
    try:
        results = scanner.scan()
        
        # Print results
        print(f"\nScan completed in {scanner.scan_time:.2f} seconds")
        print("\nOpen ports:")
        print("PORT\tSTATE\tSERVICE")
        print("-" * 30)
        
        for port, is_open, service in results:
            if is_open:
                print(f"{port}\topen\t{service}")
                
        print(f"\nScanned {args.end - args.start + 1} ports")
        print(f"Found {len(results)} open ports")
        
    except KeyboardInterrupt:
        print("\nScan interrupted by user")
    except socket.gaierror:
        print("\nHostname could not be resolved")
    except socket.error:
        print("\nCouldn't connect to server")
if __name__ == "__main__":
    main()Using the Scanner
Save this code as `port_scanner.py` and run it like this:python port_scanner.py localhost  # Scan default ports
python port_scanner.py example.com -s 20 -e 100  # Scan specific port range
python port_scanner.py 192.168.1.1 -t 100  # Use more threads| Scanner usage example | 
How It Works
Let's break down the key components:
- Socket Connection: We use Python's socket library to attempt TCP connections to each port.
- Threading: Multiple threads process the port queue concurrently for faster scanning.
- Service Identification: We try to identify common services using `socket.getservbyport()`.
- Queue Management: A queue is used to safely distribute work among threads.
Making It Better
Here are some ways you could enhance this scanner: 1. Add UDP scanning:def _udp_scan(self, port: int) -> Tuple[int, bool, str]:
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            s.settimeout(1)
            s.sendto(bytes(0), (self.target, port))
            try:
                s.recvfrom(1024)
                return port, True, "udp"
            except socket.timeout:
                return port, False, ""
    except:
        return port, False, ""def _grab_banner(self, ip: str, port: int) -> str:
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(2)
            s.connect((ip, port))
            banner = s.recv(1024).decode().strip()
            return banner
    except:
        return ""COMMON_PORTS = {
    20: "FTP-DATA", 21: "FTP", 22: "SSH", 23: "TELNET",
    25: "SMTP", 53: "DNS", 80: "HTTP", 443: "HTTPS",
    3306: "MYSQL", 3389: "RDP"
}
No comments:
Post a Comment