Skip to main content
  1. Posts/

Port Scanning on Linux and Windows - The Ultimate Guide

··1190 words·6 mins·
Table of Contents

Port scanning is the first thing you do on a network you’ve just landed on. The goal is simple: find out what services are listening, on which hosts, on which ports — that’s the map you’ll work from for the rest of the engagement. This post covers how to do it from Linux and Windows, both with the obvious tools (nmap, masscan) and with what’s already on the box when you can’t install anything.

Port scanning on Linux
#

Standard tools first, then techniques for when you can’t install anything.

Nmap: the gold standard
#

Nmap is the default port scanner. Free, open-source, and supports TCP, UDP, ICMP, and roughly two dozen other scanning techniques.

To scan a target system using Nmap, you can use the following command:

nmap <target IP>

Using the default TCP scan, this command will scan the target system for open ports. You can also specify the port range to be scanned using the -p option. For example, to scan for ports 80 and 443 on a target system, you can use the following command:

nmap -p 80,443 <target IP>

Netcat: the Swiss army knife
#

Netcat sends and receives bytes over network connections. Port scanning is one of its incidental uses; it’s not optimized for it, but it works when nmap isn’t on the box.

To perform a port scan using Netcat, you can use the following command:

nc -zv <target IP> <start port>-<end port>

This command will scan the target system for open ports between the specified range. The -z option specifies no data should be sent, and the -v option provides verbose output.

Masscan: for internet-scale speed
#

Masscan can scan the entire IPv4 internet in under an hour. It achieves that by bypassing the kernel TCP/IP stack entirely and sending packets asynchronously from userspace. The trade-off is reliability — it’s the right tool for “find anything responsive on this /16,” not for “carefully fingerprint this one host.”

To scan a network using Masscan, you can use the following command:

masscan -p1-65535 <target IP> --rate 1000

Note: Be careful with the --rate parameter, as high rates can crash network equipment or trigger security alerts.

Tool-less scanning on Linux
#

In environments where you cannot install or upload binaries, you must rely on what is already there.

Scanning with Bash sockets
#

Bash can provide TCP/UDP client connections via the special paths /dev/tcp/host/port and /dev/udp/host/port when that feature is enabled. This is a Bash feature (not POSIX), and it may not be available in other shells or on every build.

# Simple one-liner to scan ports 1-1024 (best-effort; can hang on filtered networks)
target="${1:-127.0.0.1}"
for i in {1..1024}; do
    # Prefer a timeout if available (Linux commonly has `timeout`; macOS often has `gtimeout` via coreutils).
    if command -v timeout >/dev/null 2>&1; then
        timeout 1 bash -c 'echo >/dev/tcp/"$1"/"$2"' _ "$target" "$i" >/dev/null 2>&1 && echo "Port $i is open"
    elif command -v gtimeout >/dev/null 2>&1; then
        gtimeout 1 bash -c 'echo >/dev/tcp/"$1"/"$2"' _ "$target" "$i" >/dev/null 2>&1 && echo "Port $i is open"
    else
        (echo >/dev/tcp/"$target"/"$i") >/dev/null 2>&1 && echo "Port $i is open"
    fi
done

Scanning with Python
#

Python is ubiquitous on Linux servers and provides a robust way to script a port scanner.

import socket

def scan_port(ip, port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.settimeout(1)
        # connect_ex returns 0 if successful
        if sock.connect_ex((ip, port)) == 0:
            print(f"Port {port} is open")

target = "127.0.0.1"
for p in range(1, 1025):
    scan_port(target, p)

Port scanning on Windows
#

Windows environments require a different set of tools, especially when you are trying to stay below the radar of EDR and AV.

PowerShell: living off the land on Windows
#

Every modern Windows host has PowerShell, which means you can scan from the box without dropping any tooling.

Test-NetConnection
#

The simplest way to check a port in PowerShell is the built-in Test-NetConnection cmdlet (available in PowerShell 4.0+).

Test-NetConnection -ComputerName 192.168.1.1 -Port 445

Writing a full scanner in PowerShell
#

For scanning multiple ports efficiently, we can use the System.Net.Sockets.TcpClient .NET class directly:

$target = "192.168.1.1"
$ports = 1..1024

foreach ($port in $ports) {
    $client = New-Object System.Net.Sockets.TcpClient
    try {
        $async = $client.BeginConnect($target, $port, $null, $null)
        $success = $async.AsyncWaitHandle.WaitOne(100, $false) # 100ms timeout
        if ($success -and $client.Connected) {
            $client.EndConnect($async) | Out-Null
            Write-Host "Port $port is open" -ForegroundColor Green
        }
    } catch {
        # Ignore connection failures/timeouts
    } finally {
        if ($null -ne $async) { $async.AsyncWaitHandle.Close() }
        $client.Close()
    }
}

Enumerating listening ports (local)
#

For local enumeration (not remote scanning), you can list listening TCP ports and correlate them to processes. This is not the same as scanning a remote host, but it is useful post-compromise or during host-based assessment.

Get-NetTCPConnection -State Listen | Select-Object LocalAddress, LocalPort, OwningProcess

Common port scanning techniques
#

A taxonomy of the common scan types and what gives them away to defenders:

  1. TCP Connect Scan (-sT): The most basic technique. It completes the three-way handshake. It is reliable but noisy because it establishes a full connection that will be logged by the application.
  2. SYN Scan (-sS): Also known as “Half-Open” scanning. It sends a SYN and waits for a SYN-ACK. If received, it typically sends a RST to avoid completing the handshake. This can reduce application-level logs, but network devices and host firewalls can still detect and log it.
  3. UDP Scan (-sU): UDP is connectionless. When you send a packet, an open port will usually send no response (or a service-specific response). A closed port will typically send back an ICMP Port Unreachable message. This type of scanning is slow and unreliable due to packet loss and ICMP rate-limiting.
  4. Null Scan (-sN): Sends a packet with no flags set. On many Unix-like stacks, closed ports respond with RST while open ports ignore it. This is less reliable on Windows targets and in the presence of modern firewalls/filters.
  5. Xmas Scan (-sX): Sends a packet with FIN, PUSH, and URG flags set. Similar caveats apply: behavior varies by OS and filtering, and Windows targets often do not respond in the RFC-expected way.

Stealth and evasion
#

When scanning as a red teamer, noise is your enemy. Here are some tips to stay stealthy:

  • Slow it down: Use Nmap’s timing templates. -T0 (Paranoid) or -T1 (Sneaky) are much harder to detect than the default -T3 or the aggressive -T4.
  • Randomize targets: Use --randomize-hosts to prevent sequential scanning patterns that trigger IDS alerts.
  • Use Decoys: The -D flag can add decoy source IPs to confuse some logging and attribution. It does not make you invisible to a well-instrumented target, and decoy effectiveness depends on the environment.
nmap -sS -p 80 -D 192.168.1.5,192.168.1.6,ME 192.168.1.100

Conclusion
#

Port scanning is the recon step that sets up everything that comes next on an engagement. Pick the right tool for the situation: nmap when you want fingerprinting, masscan when you want scale, bash or PowerShell when you can’t drop binaries.

And yes, get written authorization before you point any of this at a network you don’t own. In most jurisdictions, scanning networks you have no permission to touch is a crime regardless of whether you do anything with the results.

UncleSp1d3r
Author
UncleSp1d3r
As a computer security professional, I’m passionate about building secure systems and exploring new technologies to enhance threat detection and response capabilities. My experience with Rails development has enabled me to create efficient and scalable web applications. At the same time, my passion for learning Rust has allowed me to develop more secure and high-performance software. I’m also interested in Nim and love creating custom security tools.