Port Scanning with PowerShell

When a target has no nmap and uploading a scanner is noisy or blocked, PowerShell plus raw .NET sockets is the living-off-the-land fallback for network service discovery (MITRE T1046). The technique needs nothing beyond the CLR: System.Net.Sockets.TcpClient attempts a TCP connect to each host:port pair; a completed connection means the port is open.

The basic pattern

$ports = @(21, 22, 23, 53, 80, 88, 135, 139, 389, 443, 445, 636,
           1433, 1521, 3306, 3389, 5985, 5986, 8080, 8443)
 
foreach ($port in $ports) {
    $client = New-Object System.Net.Sockets.TcpClient
    $connect = $client.BeginConnect($target, $port, $null, $null)
    # Async connect + short wait: without a timeout, each closed port
    # stalls the loop on the OS TCP connect timeout (~21 s on Windows)
    $wait = $connect.AsyncWaitHandle.WaitOne(200, $false)
    if ($client.Connected) { Write-Output "[+] $port open" }
    $client.Close()
}

Two implementation details matter more than the loop itself:

  1. Timeout discipline. A synchronous Connect() on a filtered port blocks for the full OS retransmit budget (~21 seconds on Windows), which makes scanning more than a handful of ports impractical. Use BeginConnect + AsyncWaitHandle.WaitOne(ms) — or Test-NetConnection -Port per target — so filtered ports cost milliseconds, not seconds.
  2. Where you scan from. The scan inherits the network position and identity of the host it runs on. That is the point: post-compromise, scanning from an internal host reaches segments perimeter tools can’t, and blends into admin traffic.

Test-NetConnection -ComputerName $target -Port $port is the built-in single-probe alternative — cleaner output, but serial and slower per call, so roll your own socket loop for anything beyond spot checks.

Trade-offs vs. a real scanner

PowerShell socketsnmap
PrerequisitesNone (CLR ships with Windows)Binary upload or install
SpeedSerial unless you add runspaces/jobsEngineered for parallelism
Detection signatureLong-lived process fanning out TCP connectsClassic scan patterns IDS know well
StealthLooks like an admin script — until it touches 500 hostsInstantly recognizable

The detectability cuts both ways: a PowerShell process opening TCP connections to hundreds of internal hosts/ports in seconds is one of the highest-signal Sysmon EID 3 (network connection) analytics a SOC can write. Script Block Logging (EID 4104) will also capture the scan source verbatim if it runs as a script.

  • bash-port-scanning — the UNIX-side equivalent using Bash’s /dev/tcp pseudo-device
  • netstat — the complementary view: which ports the local host itself is listening on, and which processes own them
  • netsh-windows-firewall — the firewall context that decides which discovered ports are actually reachable
  • invoke-webrequest-download-cradles — the other common pure-PowerShell network pattern (HTTP egress instead of TCP probing)

Sources