FLOW^:Pentest Workflow

Stage 01 — Recon and Host Discovery

Pentest attack-flow reference for stage 01 — recon and host discovery in an authorised engagement.

intermediateupdated 2026-09-17RustScan · Nmap · dig · Kerberos

[!dashboard] Attack-flow navigation Dashboard: HTB Pentest Attack Flow

Section: 02 of 17 · Focus: Stage 01 — Recon and Host Discovery

Previous: Stage 00 — Passive External Recon · Next: Stage 02 — Web Enumeration and Exploitation


🛰️ STAGE 1 — Recon & Host Discovery

First contact. Get the box into /etc/hosts + krb5.conf, kill clock skew before anything Kerberos, then rustscan → nmap the whole surface. rustscan finds the ports in seconds, nmap tells me what they are. Everything downstream branches off this scan, so I scan wide first and read carefully.

[!note] Env $IP $DOMAIN $DC $LHOST $U $P are already exported by the guide’s setup block. On a fresh box I usually only know $IP at first — $DOMAIN/$DC get filled in from the scan (SMB/LDAP ssl-cert, smb-os-discovery) and from /etc/hosts below.

[!abstract] MITRE ATT&CK — Recon / Discovery

TechniqueWhere it lands here
T1595 Active Scanningnmap/rustscan/masscan against the perimeter
T1595.001 Scanning IP Blockssubnet sweeps, -sn discovery
T1595.002 Vulnerability Scanning--script vuln, nuclei later
T1046 Network Service Discoverypost-foothold internal scanning (fscan section)
T1018 Remote System DiscoveryLAN host discovery (arp-scan/netdiscover)
T1070.001 Timestomp-adjacent hygieneclock sync avoids noisy Kerberos failures

0. SETUP — hosts + realm + clock (do this first, every AD box)

What to look for → the DC’s hostname/FQDN, the domain FQDN, and whether my clock matches the DC. Skip this and every Kerberos tool later throws KRB_AP_ERR_SKEW.

Enumerate / prep

# Auto-populate /etc/hosts with DC + domain straight from SMB
sudo nxc smb $IP --generate-hosts-file /etc/hosts

# Manual fallback / add extra vhosts as I find them
echo "$IP  $DC $DOMAIN dc01" | sudo tee -a /etc/hosts

# Generate a matching krb5.conf realm (needed for -k Kerberos auth later)
nxc smb $DC --generate-krb5-file krb5.conf
sudo cp krb5.conf /etc/krb5.conf

Fix the clock (measure the skew, then either sync or wrap)

# Measure skew straight off AD services (DC-minus-you; positive = DC ahead)
nmap -p445,88 --script smb2-time,clock-skew -Pn $DC
ntpdate -q $DC                      # prints offset in seconds
nxc smb $IP | grep -i time          # quick sanity check vs DC

# Option A — sync my whole host to the DC (simplest)
sudo ntpdate -u $DC
sudo apt install ntpdate -y         # if missing
sudo rdate -n $DC                            # modern alt
sudo chronyd -q "server $DC iburst"          # modern alt

# htpdate — sync over plain HTTP when NTP/UDP 123 is blocked by the VPN
sudo htpdate -s $DOMAIN              # uses the web server's Date: header

# Option B — surgical: leave my clock alone, wrap only the tool that talks Kerberos
faketime -f '+7h30m' <kerberos-tool>         # +offset if DC is ahead, - if behind

[!warning] Watch out

  • Clock skew is the #1 Kerberos killer. Any KRB_AP_ERR_SKEW = re-run ntpdate -u $DC. Re-check if a box’s time drifts mid-engagement.
  • faketime sign matters: nmap’s clock-skew is DC minus you — positive means DC is ahead, push forward (+). Wrong sign doubles the skew.
  • Use faketime -f (follow) for anything Python/impacket — child procs don’t inherit the fake clock without -f.
  • Writing --generate-hosts-file /etc/hosts needs sudo. evil-winrm -r $DOMAIN and all -k auth need the FQDN resolving here, so keep /etc/hosts current as vhosts surface. Deep dive: faketime-cheatsheet · tools: faketime, htpdate.

1. nmap flag reference (the whole arsenal)

[!example] Host discovery

FlagMeaningUse
-snPing sweep — no port scanmap a subnet without touching ports
-PnSkip host discovery (treat all as up)HTB default — ICMP usually blocked
-PS22,80,443TCP SYN ping on given portsdiscovery when ICMP dead but TCP open
-PA80,443TCP ACK pingslips past stateless ACLs that block SYN
-PU53,161UDP pingrare; catches UDP-only hosts
-PE/-PP/-PMICMP echo / timestamp / netmaskclassic ping types
-nNo reverse DNSspeed + silence; rDNS leaks your target in DNS logs anyway
--openOnly show open portscuts closed noise on -p- sweeps
--reasonShow why a port got its statedistinguishes RST vs timeout

[!example] Scan types

FlagMeaningNotes
-sSSYN “half-open” scandefault with root; fast, classic
-sTFull TCP connectno root / through proxies; loudest (full handshake logged)
-sUUDP scanslow; pair with --top-ports
-sVService/version detectionprobes banners; enables version-intensity scripts
-sCDefault NSE scripts (--script=default)safe-ish, high signal
-A-sV -sC -O + tracerouteconvenient, noisy
-OOS detectionneeds open+closed port; --osscan-guess forces
-sN/-sF/-sXNULL/FIN/XmasFW evasion on non-Windows targets (Windows answers RST regardless)
-sAACK scanmaps firewall rules (filtered vs unfiltered), not port state
--script <name>Run specific NSE script(s)comma-sep, globs OK: "smb-* and not smb-brute"

[!example] Timing, ports, output

FlagMeaning
-T0 … -T5paranoid / sneaky / polite / normal / aggressive / insane — -T4 is the lab default, -T0/-T1 for IDS dodging (very slow)
--min-rate 1000 / --max-rate 500packets/sec floor/ceiling — the real speed knob
--min-parallelism / --max-retries 2concurrency / retry cap for flaky links
-p-all 65535 TCP ports
-p80,443,8000-9000explicit list/range
--top-ports 100nmap’s statistically-most-common ports
-Ffast mode (~100 most common)
-oA basenameall three outputs: .nmap .gnmap .xml — always use it
-oX file.xmlXML (for ndiff / xsltproc / imports)
-oG file.gnmapgreppable (shell one-liners)
-v / -vvverbosity; --stats-every 15s progress lines
--resume file.nmapresume an interrupted scan

[!example] Evasion / spoofing

FlagMeaningPitfall
-fFragment IP packets (8-byte)modern IDS reassembles; breaks some services’ replies
--mtu 16Custom fragmentation (multiple of 8)controlled version of -f
-D RND:10,MEDecoy scan — target sees N fake sources + youdoesn’t hide you, just dilutes; never use on client reports without ROE
-S <IP>Spoof source addressreplies go to the spoofed IP — you won’t see results unless you can sniff them
--source-port 53Fixed source portsneaks past lazy “allow UDP/53 out” firewall rules (also -g 53)
--proxies http://...Route through proxiesforces -sT, slow
--data-length 24Append random payloaddefeats “packet size signature” detection
--spoof-mac 0Random MACLAN/VMnet only
-sI zombie:portIdle scan (fully blind)needs idle zombie with predictable IP-ID — rare in practice, exam-famous

2. NSE — categories and the scripts I actually run

Categories (use with --script <cat>): auth · broadcast · brute (lockout risk) · default (= -sC) · discovery · dos (never in prod) · exploit · external (queries whois/virustotal) · fuzzer · intrusive · malware · safe · version · vuln.

[!example] High-signal scripts by service

Port/ServiceScriptsGets you
21 FTPftp-anon,ftp-systanonymous login, OS hints
22 SSHssh2-enum-algos,ssh-hostkey,ssh-auth-methodsweak algos, password vs key auth
53 DNSdns-zone-transfer,dns-recursion,dns-nsidAXFR, open resolver
80/443 HTTPhttp-title,http-headers,http-server-header,http-methods,http-enumtitle, put/del methods, common paths
445 SMBsmb-os-discovery,smb2-security-mode,smb-shares,smb-protocolsOS/domain, signing, SMBv1
389/636 LDAPldap-rootdse,ldap-searchnaming contexts = domain FQDN
88 Kerberoskrb5-enum-userspre-auth user enum (see §9)
1433 MSSQLms-sql-info,ms-sql-ntlm-infoversion, NTLM leak
3389 RDPrdp-enum-encryption,rdp-ntlm-infoNLA state, NTLM info
5985 WinRMhttp-title + service bannerconfirms WinRM vs web app
any TLSssl-cert,ssl-enum-ciphersCN/SANs leak hostnames → vhosts
vuln sweep"vuln,vulners" --script-args mincvss=7.0high-sev hits without the noise
# target one host with a service menu
nmap -Pn -p445 --script "smb-* and not smb-brute and not smb-flood" $IP
# script args
nmap -Pn -p80 --script http-enum --script-args http-enum.basepath='/app/' $IP

3. rustscan → nmap handoff (the fast path)

What to look for → every open TCP port, then default-script + version detail on exactly those ports. This is my default first scan.

Enumerate

# rustscan auto-feeds the open ports into nmap (-sCV = -sC -sV), saves all formats
rustscan -a $IP -u 50000 -r 1-65535 -- -sCV -oA ./recon/target

# Windows / no-ping / firewalled targets — force it through and give it more room
rustscan -a $IP -u 50000 -r 1-65535 -t 3000 -b 1000 -- -Pn -sCV --max-retries 3 -T4

# Quick port-only discovery when I just want the list (no nmap)
rustscan -a $IP -q

[!warning] Watch out The #1 rustscan mistake: forgetting -Pn on the nmap side. rustscan already proved the port open via raw TCP connect — but if nmap’s own ping then fails (ICMP blocked, normal on HTB), nmap reports “0 hosts up” and you lose every result. Always append -Pn after -- on Windows/firewalled boxes. -- -A piped through rustscan is still fast because nmap only re-touches the already-open ports, never the full 65535. Second rustscan gotcha: on a busy VPN, the default -b (batch size) floods and misses ports — if results look thin, re-run with -b 500.


4. masscan & naabu — subnet-scale discovery

masscan — asynchronous raw-socket scanner, internet-scale speeds. Different engine from nmap; results differ.

sudo masscan -p1-65535 $IP --rate=1000 -oG masscan-all.gnmap     # whole box, capped rate
sudo masscan -p80,443,445,3389 10.10.110.0/24 --rate=2000        # subnet sweep
# gotchas: --rate is pps (10000+ melts HTB VPN and DROPS results); raw sockets bypass the
# OS stack → responses can be eaten by your own firewall unless you: sudo iptables -A INPUT -p tcp --dport 60000 -j DROP  (and --source-port 60000)
# masscan finds ports only — hand the list to nmap for -sCV (same two-stage pattern as rustscan)

naabu — ProjectDiscovery’s fast SYN scanner; designed to pipe into httpx/nuclei.

sudo naabu -host $IP -p - -rate 1000 -o naabu.txt        # full range
naabu -host $IP -top-ports 1000                          # quick triage
# gotchas: needs root for SYN (else falls back to connect); -p - means all ports;
# pair with -verify to drop the false positives raw-SYN scans love

[!tip] Which scanner when Single box, I want detail now → rustscan → nmap. A whole /24 → masscan or naabu for ports, then nmap -sCV on the hits. Exam boxes → plain two-stage nmap is plenty. Whatever finds the ports, nmap owns the service/version truth.


5. Two-stage nmap (when I want full manual control)

What to look for → same result as the handoff, but I own the exact nmap command (custom scripts, output paths, timing). Use when rustscan’s auto-invocation gets in the way.

Enumerate

# Stage 1: find open ports fast, comma-join them
ports=$(nmap -p- --min-rate=1000 -T4 --open -oG - $IP | grep -oP '\d+(?=/open)' | paste -sd,)

# Stage 2: deep scan only those ports
nmap -p $ports -sCV -T4 -Pn $IP -oA ./recon/detailed

# Same idea straight from rustscan's greppable output
ports=$(rustscan -a $IP -g | grep -oP '\[\K[^\]]+')
nmap -sC -sV -Pn -p $ports $IP -oA ./recon/detailed

[!tip] Long -p- scan? Don’t sit and stare. Tap spacebar any time for an instant progress line (% complete + ETA), or run with --stats-every 15s. If it dies (SSH drop, Ctrl+C): nmap --resume ./recon/detailed.nmap picks up where it stopped — no restart from zero.


6. Deep TCP + version + default/vuln scripts

What to look for → confirmed service/product/version per port, OS guess, and any obvious high-severity CVE. Treat a version match as a lead, not proof.

Enumerate

# Privileged full-TCP SYN inventory with reasons (fast internal starting point)
sudo nmap -sS -Pn -n -p- --open --reason --min-rate 1000 --max-retries 2 $IP -oA ./recon/01-tcp-all

# Service + default scripts + OS on the ports that came back
sudo nmap -sS -Pn -n -sV -sC -O --reason -p $ports $IP -oA ./recon/02-services

# Exhaust probes when a banner stays unknown
nmap -Pn -n -sV --version-all -p <port> $IP

Attack (surface for weak points)

# High-severity vuln scripts only (mincvss filters the noise)
nmap -sV --script "vuln,vulners" --script-args mincvss=7.0 -p $ports $IP -oA ./recon/vuln

[!warning] Watch out -A is not -p- — it’s OS + version + default NSE + traceroute on the ports you gave it, nothing more. Keep intent explicit with -sV -sC -O. tcpwrapped in the output = connection accepted then instantly closed (ACL/wrapper), not a real banner — re-test from the expected source. filtered describes my vantage point, not the target — add --reason.


7. Top-UDP pass (DNS / SNMP / NFS / TFTP hide here)

What to look for → UDP services TCP scans never show. On AD/Linux boxes DNS(53), SNMP(161), NFS-adjacent(111), NTP(123) matter.

Enumerate

# Quick triage
sudo nmap -sU --top-ports 50 $IP

# Focused high-value UDP with version probes
sudo nmap -sU -Pn -n -sV --reason \
  -p53,67,68,69,111,123,137,161,162,500,514,623,1434,1900,4500,5353 $IP -oA ./recon/04-udp
UDP portServiceWhy I care
53DNSzone transfer, recursion, version
67/68DHCProgue-DHCP angle on the LAN
69TFTPanonymous config/firmware pulls
111rpcbindNFS export discovery (→ 2049)
123NTPclock sync + ntp-monlist amplification history
137NetBIOS-NShostname/domain without SMB
161/162SNMPcommunity public = full device census (→ Stage 03)
500/4500IKE/IPsecVPN endpoint, ike-scan
1434MSSQL browserinstance names + ports without a login
1900/5353SSDP/mDNSdevice inventory, Responder targets

[!warning] Watch out UDP silence reads as open|filtered — ambiguous by design. Add -sV so nmap sends protocol-aware payloads and can promote ambiguous ports to confirmed open. It’s slow; that’s why it’s a separate pass, not bolted onto the TCP scan. --top-ports 50 on UDP covers ~90% of what ever matters; a UDP -p- is an overnight job and almost never worth it.


8. IPv6 — the forgotten surface

What to look for → targets dual-stacked with an unhardened v6 service set (firewall rules often only cover v4).

# v6 scanning is explicit — nmap will NOT scan v6 unless asked
ping6 -c2 $TARGET_V6
sudo nmap -6 -sS -Pn -n -p- --open $TARGET_V6 -oA ./recon/tcp6
# link-local discovery on a LAN you have a foothold in:
ping6 -c2 ff02::1%eth0                      # all-nodes multicast → live v6 neighbors
ip -6 neigh                                  # kernel's discovered v6 neighbors

[!note] In AD, v6 is usually DHCP-managed but DNS is v4 — that’s the mitm6 angle; see Stage 10. Here in Stage 01 just note whether the target even answers on v6 — a :: in ip addr on a foothold means check it.


9. Host discovery on the LAN (post-foothold / internal range)

What to look for → live neighbors before port-scanning them. ARP can’t be filtered on the local segment — it’s the ground truth.

# ARP-based (local subnet only, undetectable-by-firewall, fast)
sudo arp-scan -l -I eth0                      # auto subnet
sudo arp-scan 10.10.110.0/24 | tee arp.txt
sudo netdiscover -i eth0 -r 10.10.110.0/24 -P # passive-capable, live table

# ICMP/TCP fallback across routed segments
fping -asgq 10.10.110.0/24                    # alive hosts, one line each
nmap -sn -PS22,80,135,445,3389 10.10.110.0/24 -oA ./recon/lan-discover

[!tools] Internal all-in-one: fscan fscan_windows_x64.exe (SHA-256 · GPG signature)

fscan sweeps a segment for hosts, ports, and weak services in one shot from a compromised Windows box — MS17-010 check, SSH/SMB/RDP/MSSQL/Redis brute (careful), web title grab.

# upload to the foothold, then:
.\fscan_windows_x64.exe -h 10.10.110.0/24 -np -no -nopoc -o fscan-result.txt
# -np skip ping (if ICMP is filtered), -no don't save default log, -nopoc skip web poc checks (quieter)
.\fscan_windows_x64.exe -h 10.10.110.5 -p 1-65535      # deep on one juicy host

OPSEC: fscan is loud and well-signatured (AV eats it on sight — expect to need exclusion or obfuscation). It’s a lab/internal convenience, not a stealth tool. For monitored networks prefer native nmap through a Stage 10 socks proxy.


10. Vhosts & DNS → feed /etc/hosts

What to look for → hostnames the IP scan hints at but doesn’t route: cert CN/SANs, HTTP redirects/titles, DNS zone data, and reverse-PTR records. Every new name goes into /etc/hosts so web + Kerberos work.

Enumerate

# Reverse PTR lookup — ask the box's own DNS server to resolve its own IP.
# On plenty of HTB/AD boxes the reverse zone is auto-populated for the DC,
# so this hands you $DOMAIN from nothing but $IP, before nmap even finishes.
dig -x $IP @$IP +noall +answer
# 166.11.10.10.in-addr.arpa. 604800 IN    PTR    trick.htb.

# Names leak from TLS certs and HTTP on web ports
nmap -p443,8443 --script=ssl-cert $IP
nmap -sV -p80,443,8080,8443 --script=http-title,http-headers,http-server-header $IP

# DNS zone transfer (AXFR) → instant subdomain dump when it works
nmap -p53 --script=dns-zone-transfer --script-args dns-zone-transfer.domain=$DOMAIN $IP
dig axfr $DOMAIN @$IP

# Add whatever surfaces
echo "$IP  $DOMAIN www.$DOMAIN mail.$DOMAIN" | sudo tee -a /etc/hosts

[!tip] Reading the PTR answer dig -x is shorthand for a PTR query against <reversed-octets>.in-addr.arpa — @$IP points it at the target’s own resolver since that’s usually the only DNS server that has the zone. The FQDN in the ANSWER section (trick.htb. above) is your $DOMAIN — export it and go straight to §0’s /etc/hosts/krb5.conf setup. No PTR record / NXDOMAIN is common and means try TLS-cert CNs or AXFR instead, not that something’s broken.

[!note] If a web port serves a different site per Host: header, that’s vhost routing — fuzz it in STAGE 2 (ffuf ... -H "Host: FUZZ.$DOMAIN"). Zone transfer denied is the expected result on a sane DNS server, not an error. Port→service reference: Common Ports and Services Cheatsheet 2026.

SRV records — AD publishes its own service map, unauthenticated

What to look for → every DC, the PDC emulator, and Global Catalog servers, straight from DNS — no LDAP bind or Kerberos ticket needed.

# _msdcs is the AD-specific subzone; these four cover 95% of what you need
dig srv _ldap._tcp.dc._msdcs.$DOMAIN @$DC +short          # every domain controller
dig srv _kerberos._tcp.dc._msdcs.$DOMAIN @$DC +short      # every KDC (usually == DC list)
dig srv _ldap._tcp.pdc._msdcs.$DOMAIN @$DC +short         # the PDC emulator specifically
dig srv _gc._tcp.$DOMAIN @$DC +short                      # Global Catalog (multi-DC forest only)

# nslookup form — same data, useful from a Windows foothold where dig isn't installed
nslookup -type=srv _ldap._tcp.dc._msdcs.$DOMAIN $DC

[!tools] dnsrecon / adidnsdump dnsrecon automates the whole DNS pass above in one command:

dnsrecon -d $DOMAIN -n $DC -t std,srv,axfr      # std = A/AAAA/MX/NS/TXT/SOA, srv = the AD SRV set, axfr = zone transfer attempt

adidnsdump is a different job — it needs any authenticated domain account (any tier) and dumps the entire AD-integrated DNS zone via LDAP, not just the public SRV/PTR subset. Revisit once creds land in Stage 04:

adidnsdump -u $DOMAIN\$U -p $P $DC              # writes records.csv — every internal name, including ones with no PTR

[!tip] Why bother when nmap already found the DC A single-DC lab box makes SRV records feel redundant — but on a multi-DC forest they’re how you find the PDC emulator (the one DC that matters for password/lockout state and time sync) and the Global Catalog (needed for forest-wide LDAP searches in Stage 04) without guessing which of five DCs to point tools at. _msdcs.$DOMAIN is also queryable straight off an external resolver on internet-facing AD — same passive-recon value as the PTR trick in §8 of Stage 00.


11. Kerberos on 88 = it’s AD (tell + first users)

What to look for → port 88 open means Domain Controller. I can enumerate valid domain users pre-auth without creds, straight off the scan — hands STAGE 2 a userlist.

Enumerate

# Valid-user enum via Kerberos pre-auth (realm MUST be uppercase)
nmap -p88 --script=krb5-enum-users \
  --script-args krb5-enum-users.realm=${DOMAIN^^},userdb=/usr/share/seclists/Usernames/top-usernames-shortlist.txt $DC

# Re-confirm skew here too — 88 is exactly where KRB_AP_ERR_SKEW bites
nmap -p445,88 --script smb2-time,clock-skew -Pn $DC

[!warning] Watch out The krb5 realm is case-sensitive and uppercase (${DOMAIN^^}) — lowercase silently returns nothing. Enum doesn’t trigger account lockout (pre-auth only) but pre-auth failures do log (Event 4768/4771). For the heavier userlist sweep hand off to kerbrute userenum in STAGE 2 — see Kerbrute (binaries staged in Stage 00 §12). NSE per-port script menus live in NSE Guide; the full nmap flag arsenal (timing knobs, evasion, idle scan) in Nmap Cheatsheet 2026.


12. SMB / LDAP / DNS quick wins (bridge to Stages 03–04)

First-auth probes that answer “is this worth an hour?” in 60 seconds:

# SMB — domain, hostname, OS, signing, null session in one line each
nxc smb $IP
nxc smb $IP -u '' -p '' --shares            # null session → shares?
smbclient -N -L //$IP/ 2>/dev/null

# LDAP — anonymous bind dumps the domain naming context (sometimes more)
ldapsearch -x -H ldap://$DC -s base namingcontexts
nxc ldap $DC -u '' -p '' --users            # occasionally wins a full user list

# DNS — adidnsdump-style record pull if we have ANY creds later; for now just AXFR + version
dig axfr $DOMAIN @$DC; dig version.bind chaos txt @$DC

[!tip] Handoff logic SMB signing disabled → relay potential (Stage 10). Null shares → Stage 03. Any creds → full AD enum in Stage 04. 88/389/445 on one box → it is the DC: fix /etc/hosts, krb5.conf, and clock before anything else.


13. Web screenshot triage — see every web port at once

When the scan comes back with a dozen HTTP(S) ports, eyeball them all in one pass instead of curling each.

[!tools] Stage this gowitness_linux_amd64 (SHA-256 · GPG signature) gowitness_windows_amd64.exe (SHA-256 · GPG signature)

gowitness — headless-Chrome screenshotter. Alternative: EyeWitness (Python, classifies login panels/default pages automatically).

# scan-straight-from-nmap: gowitness reads the -oX XML and shoots every http service
gowitness scan nmap -f ./recon/detailed.xml --open --service-contains http --screenshot-path ./shots
# or from a URL list (v3 syntax — old `gowitness single/file` is deprecated)
gowitness scan file -f urls.txt --screenshot-path ./shots -t 8
# review everything in the built-in gallery (defaults to 127.0.0.1:7171)
gowitness report server

[!tip] CPTS exam tip Screenshot before deep enumeration. The gallery instantly surfaces login portals, default pages (IIS welcome = unpatched vibe), directory listings, and camera/printer panels you’d waste 20 minutes identifying via curl -I. Log every interesting panel in the notes with its port — Stage 02 fuzzing targets come from this view.


14. Results hygiene — parse, convert, keep receipts

What to look for → durable, greppable, diffable scan output. Future-you (and the report) needs it.

NMAP_FILE=./recon/detailed.gnmap

# Host + count of open ports
egrep -v "^#|Status: Up" $NMAP_FILE | cut -d' ' -f2,4- | \
sed -n -e 's/Ignored.*//p' | \
awk -F, '{split($0,a," "); printf "Host: %-20s Ports Open: %d\n" , a[1], NF}' | sort -k 5 -g

# Clean table: state / proto/port / service
egrep -v "^#|Status: Up" $NMAP_FILE | cut -d' ' -f2,4- | sed -n -e 's/Ignored.*//p' | \
awk '{print "Host: " $1 " Ports: " NF-1; $1=""; for(i=2; i<=NF; i++) { a=a" "$i; }; split(a,s,","); for(e in s) { split(s[e],v,"/"); printf "%-8s %s/%-7s %s\n" , v[2], v[3], v[1], v[5]}; a="" }'

# Top service versions across the scan
egrep -v "^#|Status: Up" $NMAP_FILE | cut -d ' ' -f4- | tr ',' '\n' | \
sed -e 's/^[ \t]*//' | awk -F '/' '{print $7}' | grep -v "^$" | sort | uniq -c | sort -k 1 -nr

# one-liner: just the open port numbers (feed to -p or other tools)
grep -oP '\d+/open' $NMAP_FILE | cut -d/ -f1 | sort -n | uniq | paste -sd,

# XML → readable HTML report for the evidence folder
xsltproc ./recon/detailed.xml -o ./recon/detailed.html

# diff two scans (long engagements — new ports = new surface)
ndiff ./recon/baseline.xml ./recon/current.xml

[!tip] XML over greppable for anything durable — -oX retains full host/port/service/NSE structure and diffs across time with ndiff baseline.xml current.xml (catches new attack surface on long boxes). Greppable is for quick shell work in the moment. Full recipes: Awesome NMAP grep.

[!warning] /etc/hosts discipline

  • One line per IP, FQDN first, then short names: $IP dc01.$DOMAIN dc01 $DOMAIN — Kerberos picks the first match.
  • Never duplicate an IP with conflicting names from two boxes; comment out old entries, don’t delete (audit trail).
  • After every vhost discovery (§10), append immediately — “I’ll remember it” is how you lose 30 minutes to curl returning the wrong vhost.

15. AutoRecon — the “do all of this” wrapper

AutoRecon chains everything above: full-port discovery → per-service deep scans → web screenshots → feroxbuster kicks, all into results/<IP>/ folders.

autorecon $IP --dirbuster.threads 50 -o ./autorecon-out
# --single-target for one box at a time; -p/-t/-ct to shape port/timing

[!note] Use it to check coverage, not to think for you. On CPTS boxes AutoRecon’s feroxbuster/vhost stages are exactly the Stage 02 work — great safety net, but know what every spawned command does; “AutoRecon didn’t find it” is not a strategy.


[!failure] Detection & OPSEC — assume the scan is seen

What I runWhat the blue team sees
-sS SYN scanhalf-open connections; IDS signature on rate/sequencing — fast scans at -T4/high --min-rate trip thresholds instantly
-sT connect scanfull handshakes + SYN + app-layer logs on every service — double visibility, use only when rootless
rustscan/masscan/naabuextreme pps → NetFlow/IDS alarm regardless of flags
-sC/vuln scriptsreal probes hitting app logs (404 storms, malformed requests) — far noisier than the port scan itself
-D decoysdilutes, doesn’t anonymize — source list still includes you
-f/--mtu, -T0, --source-port 53classic evasions; help vs. naïve IDS, reassembled/caught by anything modern

On HTB/CPTS: noise is free — optimize for speed and completeness. On a monitored engagement: agree scan windows, throttle (-T2/--max-rate), scan from the expected source, and log your source IP + timestamps for SOC deconfliction (T1595 attribution).


[!tip] Where this feeds Open 445/139/389/88 → STAGE 2 AD enum (netexec, bloodhound). Open 80/443/8080 → web (ffuf/feroxbuster). Open 53/111/2049/6379 → Linux service detours. The full phased map: Attack-Flow-Guide · Most-Used-Commands.


[!navigation] Continue the attack flow Previous: Stage 00 — Passive External Recon

Dashboard: HTB Pentest Attack Flow

Next: Stage 02 — Web Enumeration and Exploitation

On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.