daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

recon-and-host-discovery.md (31209B)


      1 ---
      2 title: "Stage 01 — Recon and Host Discovery"
      3 description: "CPTS attack-flow reference for stage 01 — recon and host discovery in an authorised engagement."
      4 category: pentest-workflow
      5 subcategory: "CPTS Attack Flow"
      6 order: 2
      7 tags: ["htb", "cpts", "htb-attack-flow", "htb-attack-flow-stage-01", "pentest-workflow"]
      8 tools: ["RustScan", "Nmap", "dig", "Kerberos"]
      9 difficulty: intermediate
     10 updated: "2026-09-17"
     11 source: "vault:Pentest Attack Flow/02 - Stage 01 - Recon and Host Discovery.md"
     12 ---
     13 > [!dashboard] Attack-flow navigation
     14 > **Dashboard:** [HTB Pentest Attack Flow](/sheets/pentest-workflow/attack-flow-dashboard)
     15 >
     16 > **Section:** 02 of 17 · **Focus:** Stage 01 — Recon and Host Discovery
     17 >
     18 > **Previous:** [Stage 00 — Passive External Recon](/sheets/pentest-workflow/passive-external-recon) · **Next:** [Stage 02 — Web Enumeration and Exploitation](/sheets/pentest-workflow/web-enumeration-and-exploitation)
     19 
     20 ---
     21 # 🛰️ STAGE 1 — Recon & Host Discovery
     22 
     23 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.
     24 
     25 > [!note] Env
     26 > `$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.
     27 
     28 > [!abstract] MITRE ATT&CK — Recon / Discovery
     29 > | Technique | Where it lands here |
     30 > |---|---|
     31 > | T1595 Active Scanning | nmap/rustscan/masscan against the perimeter |
     32 > | T1595.001 Scanning IP Blocks | subnet sweeps, `-sn` discovery |
     33 > | T1595.002 Vulnerability Scanning | `--script vuln`, nuclei later |
     34 > | T1046 Network Service Discovery | *post-foothold* internal scanning (fscan section) |
     35 > | T1018 Remote System Discovery | LAN host discovery (arp-scan/netdiscover) |
     36 > | T1070.001 Timestomp-adjacent hygiene | clock sync avoids noisy Kerberos failures |
     37 
     38 ---
     39 
     40 ### 0. SETUP — hosts + realm + clock (do this first, every AD box)
     41 
     42 **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`.
     43 
     44 **Enumerate / prep**
     45 ```bash
     46 # Auto-populate /etc/hosts with DC + domain straight from SMB
     47 sudo nxc smb $IP --generate-hosts-file /etc/hosts
     48 
     49 # Manual fallback / add extra vhosts as I find them
     50 echo "$IP  $DC $DOMAIN dc01" | sudo tee -a /etc/hosts
     51 
     52 # Generate a matching krb5.conf realm (needed for -k Kerberos auth later)
     53 nxc smb $DC --generate-krb5-file krb5.conf
     54 sudo cp krb5.conf /etc/krb5.conf
     55 ```
     56 
     57 **Fix the clock** (measure the skew, then either sync or wrap)
     58 ```bash
     59 # Measure skew straight off AD services (DC-minus-you; positive = DC ahead)
     60 nmap -p445,88 --script smb2-time,clock-skew -Pn $DC
     61 ntpdate -q $DC                      # prints offset in seconds
     62 nxc smb $IP | grep -i time          # quick sanity check vs DC
     63 
     64 # Option A — sync my whole host to the DC (simplest)
     65 sudo ntpdate -u $DC
     66 sudo apt install ntpdate -y         # if missing
     67 sudo rdate -n $DC                            # modern alt
     68 sudo chronyd -q "server $DC iburst"          # modern alt
     69 
     70 # htpdate — sync over plain HTTP when NTP/UDP 123 is blocked by the VPN
     71 sudo htpdate -s $DOMAIN              # uses the web server's Date: header
     72 
     73 # Option B — surgical: leave my clock alone, wrap only the tool that talks Kerberos
     74 faketime -f '+7h30m' <kerberos-tool>         # +offset if DC is ahead, - if behind
     75 ```
     76 
     77 > [!warning] Watch out
     78 > - **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.
     79 > - `faketime` sign matters: nmap's `clock-skew` is **DC minus you** — positive means DC is *ahead*, push forward (`+`). Wrong sign *doubles* the skew.
     80 > - Use `faketime -f` (follow) for anything Python/impacket — child procs don't inherit the fake clock without `-f`.
     81 > - 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.
     82 > Deep dive: faketime-cheatsheet · tools: [faketime](https://github.com/wolfcw/libfaketime), [htpdate](https://github.com/angea/htpdate).
     83 
     84 ---
     85 
     86 ### 1. nmap flag reference (the whole arsenal)
     87 
     88 > [!example] Host discovery
     89 > | Flag | Meaning | Use |
     90 > |---|---|---|
     91 > | `-sn` | Ping sweep — no port scan | map a subnet without touching ports |
     92 > | `-Pn` | Skip host discovery (treat all as up) | **HTB default** — ICMP usually blocked |
     93 > | `-PS22,80,443` | TCP SYN ping on given ports | discovery when ICMP dead but TCP open |
     94 > | `-PA80,443` | TCP ACK ping | slips past stateless ACLs that block SYN |
     95 > | `-PU53,161` | UDP ping | rare; catches UDP-only hosts |
     96 > | `-PE/-PP/-PM` | ICMP echo / timestamp / netmask | classic ping types |
     97 > | `-n` | No reverse DNS | speed + silence; rDNS leaks your target in DNS logs anyway |
     98 > | `--open` | Only show open ports | cuts `closed` noise on `-p-` sweeps |
     99 > | `--reason` | Show *why* a port got its state | distinguishes RST vs timeout |
    100 
    101 > [!example] Scan types
    102 > | Flag | Meaning | Notes |
    103 > |---|---|---|
    104 > | `-sS` | SYN "half-open" scan | default with root; fast, classic |
    105 > | `-sT` | Full TCP connect | no root / through proxies; **loudest** (full handshake logged) |
    106 > | `-sU` | UDP scan | slow; pair with `--top-ports` |
    107 > | `-sV` | Service/version detection | probes banners; enables version-intensity scripts |
    108 > | `-sC` | Default NSE scripts (`--script=default`) | safe-ish, high signal |
    109 > | `-A` | `-sV -sC -O` + traceroute | convenient, **noisy** |
    110 > | `-O` | OS detection | needs open+closed port; `--osscan-guess` forces |
    111 > | `-sN/-sF/-sX` | NULL/FIN/Xmas | FW evasion on *non-Windows* targets (Windows answers RST regardless) |
    112 > | `-sA` | ACK scan | maps firewall *rules* (filtered vs unfiltered), not port state |
    113 > | `--script <name>` | Run specific NSE script(s) | comma-sep, globs OK: `"smb-* and not smb-brute"` |
    114 
    115 > [!example] Timing, ports, output
    116 > | Flag | Meaning |
    117 > |---|---|
    118 > | `-T0` … `-T5` | paranoid / sneaky / polite / normal / aggressive / insane — `-T4` is the lab default, `-T0/-T1` for IDS dodging (very slow) |
    119 > | `--min-rate 1000` / `--max-rate 500` | packets/sec floor/ceiling — the real speed knob |
    120 > | `--min-parallelism` / `--max-retries 2` | concurrency / retry cap for flaky links |
    121 > | `-p-` | all 65535 TCP ports |
    122 > | `-p80,443,8000-9000` | explicit list/range |
    123 > | `--top-ports 100` | nmap's statistically-most-common ports |
    124 > | `-F` | fast mode (~100 most common) |
    125 > | `-oA basename` | **all three** outputs: `.nmap` `.gnmap` `.xml` — always use it |
    126 > | `-oX file.xml` | XML (for ndiff / xsltproc / imports) |
    127 > | `-oG file.gnmap` | greppable (shell one-liners) |
    128 > | `-v` / `-vv` | verbosity; `--stats-every 15s` progress lines |
    129 > | `--resume file.nmap` | resume an interrupted scan |
    130 
    131 > [!example] Evasion / spoofing
    132 > | Flag | Meaning | Pitfall |
    133 > |---|---|---|
    134 > | `-f` | Fragment IP packets (8-byte) | modern IDS reassembles; breaks some services' replies |
    135 > | `--mtu 16` | Custom fragmentation (multiple of 8) | controlled version of `-f` |
    136 > | `-D RND:10,ME` | Decoy scan — target sees N fake sources + you | doesn't hide you, just dilutes; never use on client reports without ROE |
    137 > | `-S <IP>` | Spoof source address | replies go to the spoofed IP — you won't see results unless you can sniff them |
    138 > | `--source-port 53` | Fixed source port | sneaks past lazy "allow UDP/53 out" firewall rules (also `-g 53`) |
    139 > | `--proxies http://...` | Route through proxies | forces `-sT`, slow |
    140 > | `--data-length 24` | Append random payload | defeats "packet size signature" detection |
    141 > | `--spoof-mac 0` | Random MAC | LAN/VMnet only |
    142 > | `-sI zombie:port` | Idle scan (fully blind) | needs idle zombie with predictable IP-ID — rare in practice, exam-famous |
    143 
    144 ---
    145 
    146 ### 2. NSE — categories and the scripts I actually run
    147 
    148 **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`.
    149 
    150 > [!example] High-signal scripts by service
    151 > | Port/Service | Scripts | Gets you |
    152 > |---|---|---|
    153 > | 21 FTP | `ftp-anon,ftp-syst` | anonymous login, OS hints |
    154 > | 22 SSH | `ssh2-enum-algos,ssh-hostkey,ssh-auth-methods` | weak algos, password vs key auth |
    155 > | 53 DNS | `dns-zone-transfer,dns-recursion,dns-nsid` | AXFR, open resolver |
    156 > | 80/443 HTTP | `http-title,http-headers,http-server-header,http-methods,http-enum` | title, put/del methods, common paths |
    157 > | 445 SMB | `smb-os-discovery,smb2-security-mode,smb-shares,smb-protocols` | OS/domain, signing, SMBv1 |
    158 > | 389/636 LDAP | `ldap-rootdse,ldap-search` | naming contexts = domain FQDN |
    159 > | 88 Kerberos | `krb5-enum-users` | pre-auth user enum (see §9) |
    160 > | 1433 MSSQL | `ms-sql-info,ms-sql-ntlm-info` | version, NTLM leak |
    161 > | 3389 RDP | `rdp-enum-encryption,rdp-ntlm-info` | NLA state, NTLM info |
    162 > | 5985 WinRM | `http-title` + service banner | confirms WinRM vs web app |
    163 > | any TLS | `ssl-cert,ssl-enum-ciphers` | **CN/SANs leak hostnames** → vhosts |
    164 > | vuln sweep | `"vuln,vulners" --script-args mincvss=7.0` | high-sev hits without the noise |
    165 
    166 ```bash
    167 # target one host with a service menu
    168 nmap -Pn -p445 --script "smb-* and not smb-brute and not smb-flood" $IP
    169 # script args
    170 nmap -Pn -p80 --script http-enum --script-args http-enum.basepath='/app/' $IP
    171 ```
    172 
    173 ---
    174 
    175 ### 3. rustscan → nmap handoff (the fast path)
    176 
    177 **What to look for** → every open TCP port, then default-script + version detail on exactly those ports. This is my default first scan.
    178 
    179 **Enumerate**
    180 ```bash
    181 # rustscan auto-feeds the open ports into nmap (-sCV = -sC -sV), saves all formats
    182 rustscan -a $IP -u 50000 -r 1-65535 -- -sCV -oA ./recon/target
    183 
    184 # Windows / no-ping / firewalled targets — force it through and give it more room
    185 rustscan -a $IP -u 50000 -r 1-65535 -t 3000 -b 1000 -- -Pn -sCV --max-retries 3 -T4
    186 
    187 # Quick port-only discovery when I just want the list (no nmap)
    188 rustscan -a $IP -q
    189 ```
    190 
    191 > [!warning] Watch out
    192 > **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`.
    193 
    194 ---
    195 
    196 ### 4. masscan & naabu — subnet-scale discovery
    197 
    198 **masscan** — asynchronous raw-socket scanner, internet-scale speeds. Different engine from nmap; results differ.
    199 ```bash
    200 sudo masscan -p1-65535 $IP --rate=1000 -oG masscan-all.gnmap     # whole box, capped rate
    201 sudo masscan -p80,443,445,3389 10.10.110.0/24 --rate=2000        # subnet sweep
    202 # gotchas: --rate is pps (10000+ melts HTB VPN and DROPS results); raw sockets bypass the
    203 # 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)
    204 # masscan finds ports only — hand the list to nmap for -sCV (same two-stage pattern as rustscan)
    205 ```
    206 
    207 **naabu** — ProjectDiscovery's fast SYN scanner; designed to pipe into httpx/nuclei.
    208 ```bash
    209 sudo naabu -host $IP -p - -rate 1000 -o naabu.txt        # full range
    210 naabu -host $IP -top-ports 1000                          # quick triage
    211 # gotchas: needs root for SYN (else falls back to connect); -p - means all ports;
    212 # pair with -verify to drop the false positives raw-SYN scans love
    213 ```
    214 
    215 > [!tip] Which scanner when
    216 > 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**.
    217 
    218 ---
    219 
    220 ### 5. Two-stage nmap (when I want full manual control)
    221 
    222 **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.
    223 
    224 **Enumerate**
    225 ```bash
    226 # Stage 1: find open ports fast, comma-join them
    227 ports=$(nmap -p- --min-rate=1000 -T4 --open -oG - $IP | grep -oP '\d+(?=/open)' | paste -sd,)
    228 
    229 # Stage 2: deep scan only those ports
    230 nmap -p $ports -sCV -T4 -Pn $IP -oA ./recon/detailed
    231 
    232 # Same idea straight from rustscan's greppable output
    233 ports=$(rustscan -a $IP -g | grep -oP '\[\K[^\]]+')
    234 nmap -sC -sV -Pn -p $ports $IP -oA ./recon/detailed
    235 ```
    236 
    237 > [!tip] Long `-p-` scan? Don't sit and stare.
    238 > 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.
    239 
    240 ---
    241 
    242 ### 6. Deep TCP + version + default/vuln scripts
    243 
    244 **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.
    245 
    246 **Enumerate**
    247 ```bash
    248 # Privileged full-TCP SYN inventory with reasons (fast internal starting point)
    249 sudo nmap -sS -Pn -n -p- --open --reason --min-rate 1000 --max-retries 2 $IP -oA ./recon/01-tcp-all
    250 
    251 # Service + default scripts + OS on the ports that came back
    252 sudo nmap -sS -Pn -n -sV -sC -O --reason -p $ports $IP -oA ./recon/02-services
    253 
    254 # Exhaust probes when a banner stays unknown
    255 nmap -Pn -n -sV --version-all -p <port> $IP
    256 ```
    257 
    258 **Attack (surface for weak points)**
    259 ```bash
    260 # High-severity vuln scripts only (mincvss filters the noise)
    261 nmap -sV --script "vuln,vulners" --script-args mincvss=7.0 -p $ports $IP -oA ./recon/vuln
    262 ```
    263 
    264 > [!warning] Watch out
    265 > `-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`.
    266 
    267 ---
    268 
    269 ### 7. Top-UDP pass (DNS / SNMP / NFS / TFTP hide here)
    270 
    271 **What to look for** → UDP services TCP scans never show. On AD/Linux boxes DNS(53), SNMP(161), NFS-adjacent(111), NTP(123) matter.
    272 
    273 **Enumerate**
    274 ```bash
    275 # Quick triage
    276 sudo nmap -sU --top-ports 50 $IP
    277 
    278 # Focused high-value UDP with version probes
    279 sudo nmap -sU -Pn -n -sV --reason \
    280   -p53,67,68,69,111,123,137,161,162,500,514,623,1434,1900,4500,5353 $IP -oA ./recon/04-udp
    281 ```
    282 
    283 | UDP port | Service | Why I care |
    284 |---|---|---|
    285 | 53 | DNS | zone transfer, recursion, version |
    286 | 67/68 | DHCP | rogue-DHCP angle on the LAN |
    287 | 69 | TFTP | anonymous config/firmware pulls |
    288 | 111 | rpcbind | NFS export discovery (→ 2049) |
    289 | 123 | NTP | clock sync + `ntp-monlist` amplification history |
    290 | 137 | NetBIOS-NS | hostname/domain without SMB |
    291 | 161/162 | SNMP | **community `public` = full device census** (→ [Stage 03](/sheets/pentest-workflow/service-enumeration)) |
    292 | 500/4500 | IKE/IPsec | VPN endpoint, `ike-scan` |
    293 | 1434 | MSSQL browser | instance names + ports without a login |
    294 | 1900/5353 | SSDP/mDNS | device inventory, [Responder](https://github.com/lgandx/Responder) targets |
    295 
    296 > [!warning] Watch out
    297 > 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.
    298 
    299 ---
    300 
    301 ### 8. IPv6 — the forgotten surface
    302 
    303 **What to look for** → targets dual-stacked with an *unhardened* v6 service set (firewall rules often only cover v4).
    304 
    305 ```bash
    306 # v6 scanning is explicit — nmap will NOT scan v6 unless asked
    307 ping6 -c2 $TARGET_V6
    308 sudo nmap -6 -sS -Pn -n -p- --open $TARGET_V6 -oA ./recon/tcp6
    309 # link-local discovery on a LAN you have a foothold in:
    310 ping6 -c2 ff02::1%eth0                      # all-nodes multicast → live v6 neighbors
    311 ip -6 neigh                                  # kernel's discovered v6 neighbors
    312 ```
    313 
    314 > [!note] In AD, v6 is usually DHCP-managed but **DNS is v4** — that's the [mitm6](https://github.com/dirkjanm/mitm6) angle; see [Stage 10](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot). Here in Stage 01 just note whether the target even answers on v6 — a `::` in `ip addr` on a foothold means check it.
    315 
    316 ---
    317 
    318 ### 9. Host discovery on the LAN (post-foothold / internal range)
    319 
    320 **What to look for** → live neighbors before port-scanning them. ARP can't be filtered on the local segment — it's the ground truth.
    321 
    322 ```bash
    323 # ARP-based (local subnet only, undetectable-by-firewall, fast)
    324 sudo arp-scan -l -I eth0                      # auto subnet
    325 sudo arp-scan 10.10.110.0/24 | tee arp.txt
    326 sudo netdiscover -i eth0 -r 10.10.110.0/24 -P # passive-capable, live table
    327 
    328 # ICMP/TCP fallback across routed segments
    329 fping -asgq 10.10.110.0/24                    # alive hosts, one line each
    330 nmap -sn -PS22,80,135,445,3389 10.10.110.0/24 -oA ./recon/lan-discover
    331 ```
    332 
    333 > [!tools] Internal all-in-one: fscan
    334 > [fscan_windows_x64.exe](/downloads/pentest-workflow/fscan_windows_x64.exe) ([SHA-256](/downloads/pentest-workflow/fscan_windows_x64.exe.sha256) · [GPG signature](/downloads/pentest-workflow/fscan_windows_x64.exe.sha256.asc))
    335 >
    336 > [fscan](https://github.com/shadow1ng/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.
    337 > ```powershell
    338 > # upload to the foothold, then:
    339 > .\fscan_windows_x64.exe -h 10.10.110.0/24 -np -no -nopoc -o fscan-result.txt
    340 > # -np skip ping (if ICMP is filtered), -no don't save default log, -nopoc skip web poc checks (quieter)
    341 > .\fscan_windows_x64.exe -h 10.10.110.5 -p 1-65535      # deep on one juicy host
    342 > ```
    343 > **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](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot) socks proxy.
    344 
    345 ---
    346 
    347 ### 10. Vhosts & DNS → feed `/etc/hosts`
    348 
    349 **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.
    350 
    351 **Enumerate**
    352 ```bash
    353 # Reverse PTR lookup — ask the box's own DNS server to resolve its own IP.
    354 # On plenty of HTB/AD boxes the reverse zone is auto-populated for the DC,
    355 # so this hands you $DOMAIN from nothing but $IP, before nmap even finishes.
    356 dig -x $IP @$IP +noall +answer
    357 # 166.11.10.10.in-addr.arpa. 604800 IN    PTR    trick.htb.
    358 
    359 # Names leak from TLS certs and HTTP on web ports
    360 nmap -p443,8443 --script=ssl-cert $IP
    361 nmap -sV -p80,443,8080,8443 --script=http-title,http-headers,http-server-header $IP
    362 
    363 # DNS zone transfer (AXFR) → instant subdomain dump when it works
    364 nmap -p53 --script=dns-zone-transfer --script-args dns-zone-transfer.domain=$DOMAIN $IP
    365 dig axfr $DOMAIN @$IP
    366 
    367 # Add whatever surfaces
    368 echo "$IP  $DOMAIN www.$DOMAIN mail.$DOMAIN" | sudo tee -a /etc/hosts
    369 ```
    370 
    371 > [!tip] Reading the PTR answer
    372 > `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.
    373 
    374 > [!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.
    375 
    376 **SRV records — AD publishes its own service map, unauthenticated**
    377 
    378 **What to look for** → every DC, the PDC emulator, and Global Catalog servers, straight from DNS — no LDAP bind or Kerberos ticket needed.
    379 
    380 ```bash
    381 # _msdcs is the AD-specific subzone; these four cover 95% of what you need
    382 dig srv _ldap._tcp.dc._msdcs.$DOMAIN @$DC +short          # every domain controller
    383 dig srv _kerberos._tcp.dc._msdcs.$DOMAIN @$DC +short      # every KDC (usually == DC list)
    384 dig srv _ldap._tcp.pdc._msdcs.$DOMAIN @$DC +short         # the PDC emulator specifically
    385 dig srv _gc._tcp.$DOMAIN @$DC +short                      # Global Catalog (multi-DC forest only)
    386 
    387 # nslookup form — same data, useful from a Windows foothold where dig isn't installed
    388 nslookup -type=srv _ldap._tcp.dc._msdcs.$DOMAIN $DC
    389 ```
    390 
    391 > [!tools] dnsrecon / adidnsdump
    392 > [dnsrecon](https://github.com/darkoperator/dnsrecon) automates the whole DNS pass above in one command:
    393 > ```bash
    394 > 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
    395 > ```
    396 > [adidnsdump](https://github.com/dirkjanm/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](/sheets/pentest-workflow/active-directory-enumeration):
    397 > ```bash
    398 > adidnsdump -u $DOMAIN\$U -p $P $DC              # writes records.csv — every internal name, including ones with no PTR
    399 > ```
    400 
    401 > [!tip] Why bother when nmap already found the DC
    402 > 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](/sheets/pentest-workflow/passive-external-recon).
    403 
    404 ---
    405 
    406 ### 11. Kerberos on 88 = it's AD (tell + first users)
    407 
    408 **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.
    409 
    410 **Enumerate**
    411 ```bash
    412 # Valid-user enum via Kerberos pre-auth (realm MUST be uppercase)
    413 nmap -p88 --script=krb5-enum-users \
    414   --script-args krb5-enum-users.realm=${DOMAIN^^},userdb=/usr/share/seclists/Usernames/top-usernames-shortlist.txt $DC
    415 
    416 # Re-confirm skew here too — 88 is exactly where KRB_AP_ERR_SKEW bites
    417 nmap -p445,88 --script smb2-time,clock-skew -Pn $DC
    418 ```
    419 
    420 > [!warning] Watch out
    421 > 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](/sheets/pentest-workflow/passive-external-recon)). NSE per-port script menus live in NSE Guide; the full nmap flag arsenal (timing knobs, evasion, idle scan) in Nmap Cheatsheet 2026.
    422 
    423 ---
    424 
    425 ### 12. SMB / LDAP / DNS quick wins (bridge to Stages 03–04)
    426 
    427 First-auth probes that answer *"is this worth an hour?"* in 60 seconds:
    428 
    429 ```bash
    430 # SMB — domain, hostname, OS, signing, null session in one line each
    431 nxc smb $IP
    432 nxc smb $IP -u '' -p '' --shares            # null session → shares?
    433 smbclient -N -L //$IP/ 2>/dev/null
    434 
    435 # LDAP — anonymous bind dumps the domain naming context (sometimes more)
    436 ldapsearch -x -H ldap://$DC -s base namingcontexts
    437 nxc ldap $DC -u '' -p '' --users            # occasionally wins a full user list
    438 
    439 # DNS — adidnsdump-style record pull if we have ANY creds later; for now just AXFR + version
    440 dig axfr $DOMAIN @$DC; dig version.bind chaos txt @$DC
    441 ```
    442 
    443 > [!tip] Handoff logic
    444 > SMB signing **disabled** → relay potential ([Stage 10](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot)). Null shares → [Stage 03](/sheets/pentest-workflow/service-enumeration). Any creds → full AD enum in [Stage 04](/sheets/pentest-workflow/active-directory-enumeration). 88/389/445 on one box → it *is* the DC: fix `/etc/hosts`, krb5.conf, and clock before anything else.
    445 
    446 ---
    447 
    448 ### 13. Web screenshot triage — see every web port at once
    449 
    450 When the scan comes back with a dozen HTTP(S) ports, eyeball them all in one pass instead of curling each.
    451 
    452 > [!tools] Stage this
    453 > [gowitness_linux_amd64](/downloads/pentest-workflow/gowitness_linux_amd64) ([SHA-256](/downloads/pentest-workflow/gowitness_linux_amd64.sha256) · [GPG signature](/downloads/pentest-workflow/gowitness_linux_amd64.sha256.asc))
    454 > [gowitness_windows_amd64.exe](/downloads/pentest-workflow/gowitness_windows_amd64.exe) ([SHA-256](/downloads/pentest-workflow/gowitness_windows_amd64.exe.sha256) · [GPG signature](/downloads/pentest-workflow/gowitness_windows_amd64.exe.sha256.asc))
    455 >
    456 > [gowitness](https://github.com/sensepost/gowitness) — headless-Chrome screenshotter. Alternative: [EyeWitness](https://github.com/RedSiege/EyeWitness) (Python, classifies login panels/default pages automatically).
    457 >
    458 > ```bash
    459 > # scan-straight-from-nmap: gowitness reads the -oX XML and shoots every http service
    460 > gowitness scan nmap -f ./recon/detailed.xml --open --service-contains http --screenshot-path ./shots
    461 > # or from a URL list (v3 syntax — old `gowitness single/file` is deprecated)
    462 > gowitness scan file -f urls.txt --screenshot-path ./shots -t 8
    463 > # review everything in the built-in gallery (defaults to 127.0.0.1:7171)
    464 > gowitness report server
    465 > ```
    466 
    467 > [!tip] CPTS exam tip
    468 > 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.
    469 
    470 ---
    471 
    472 ### 14. Results hygiene — parse, convert, keep receipts
    473 
    474 **What to look for** → durable, greppable, diffable scan output. Future-you (and the report) needs it.
    475 
    476 ```bash
    477 NMAP_FILE=./recon/detailed.gnmap
    478 
    479 # Host + count of open ports
    480 egrep -v "^#|Status: Up" $NMAP_FILE | cut -d' ' -f2,4- | \
    481 sed -n -e 's/Ignored.*//p' | \
    482 awk -F, '{split($0,a," "); printf "Host: %-20s Ports Open: %d\n" , a[1], NF}' | sort -k 5 -g
    483 
    484 # Clean table: state / proto/port / service
    485 egrep -v "^#|Status: Up" $NMAP_FILE | cut -d' ' -f2,4- | sed -n -e 's/Ignored.*//p' | \
    486 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="" }'
    487 
    488 # Top service versions across the scan
    489 egrep -v "^#|Status: Up" $NMAP_FILE | cut -d ' ' -f4- | tr ',' '\n' | \
    490 sed -e 's/^[ \t]*//' | awk -F '/' '{print $7}' | grep -v "^$" | sort | uniq -c | sort -k 1 -nr
    491 
    492 # one-liner: just the open port numbers (feed to -p or other tools)
    493 grep -oP '\d+/open' $NMAP_FILE | cut -d/ -f1 | sort -n | uniq | paste -sd,
    494 
    495 # XML → readable HTML report for the evidence folder
    496 xsltproc ./recon/detailed.xml -o ./recon/detailed.html
    497 
    498 # diff two scans (long engagements — new ports = new surface)
    499 ndiff ./recon/baseline.xml ./recon/current.xml
    500 ```
    501 
    502 > [!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.
    503 
    504 > [!warning] /etc/hosts discipline
    505 > - One line per IP, FQDN **first**, then short names: `$IP dc01.$DOMAIN dc01 $DOMAIN` — Kerberos picks the first match.
    506 > - Never duplicate an IP with conflicting names from two boxes; comment out old entries, don't delete (audit trail).
    507 > - After every vhost discovery (§10), append immediately — "I'll remember it" is how you lose 30 minutes to `curl` returning the wrong vhost.
    508 
    509 ---
    510 
    511 ### 15. AutoRecon — the "do all of this" wrapper
    512 
    513 [AutoRecon](https://github.com/Tib3rius/AutoRecon) chains everything above: full-port discovery → per-service deep scans → web screenshots → feroxbuster kicks, all into `results/<IP>/` folders.
    514 ```bash
    515 autorecon $IP --dirbuster.threads 50 -o ./autorecon-out
    516 # --single-target for one box at a time; -p/-t/-ct to shape port/timing
    517 ```
    518 > [!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.
    519 
    520 ---
    521 
    522 > [!failure] Detection & OPSEC — assume the scan is seen
    523 > | What I run | What the blue team sees |
    524 > |---|---|
    525 > | `-sS` SYN scan | half-open connections; IDS signature on rate/sequencing — fast scans at `-T4`/high `--min-rate` trip thresholds instantly |
    526 > | `-sT` connect scan | full handshakes + `SYN` + app-layer logs on every service — **double visibility**, use only when rootless |
    527 > | rustscan/masscan/naabu | extreme pps → NetFlow/IDS alarm regardless of flags |
    528 > | `-sC`/`vuln` scripts | real probes hitting app logs (404 storms, malformed requests) — far noisier than the port scan itself |
    529 > | `-D` decoys | dilutes, doesn't anonymize — source list still includes you |
    530 > | `-f`/`--mtu`, `-T0`, `--source-port 53` | classic evasions; help vs. naïve IDS, reassembled/caught by anything modern |
    531 > 
    532 > 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).
    533 
    534 ---
    535 
    536 > [!tip] Where this feeds
    537 > 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](/sheets/pentest-workflow/attack-flow-guide) · [Most-Used-Commands](/sheets/pentest-workflow/most-used-commands).
    538 ---
    539 
    540 > [!navigation] Continue the attack flow
    541 > **Previous:** [Stage 00 — Passive External Recon](/sheets/pentest-workflow/passive-external-recon)
    542 >
    543 > **Dashboard:** [HTB Pentest Attack Flow](/sheets/pentest-workflow/attack-flow-dashboard)
    544 >
    545 > **Next:** [Stage 02 — Web Enumeration and Exploitation](/sheets/pentest-workflow/web-enumeration-and-exploitation)