Scheduled Tasks: cron & systemd Timers
A lot of what a Linux box does happens on a schedule with nobody watching: backups at 2 AM, log rotation nightly, updates every morning. Two systems handle this, the old classic cron and the modern systemd timers. You need to read both, because scheduled tasks are also one of the first places an attacker looks, a job that runs as root plus a script you can edit equals instant root.
cron: the classic scheduler
cron runs commands on a repeating schedule. Each line is a cron job, and the schedule is five time fields followed by the command:
┌───────── minute (0,59)
│ ┌─────── hour (0,23)
│ │ ┌───── day of month (1,31)
│ │ │ ┌─── month (1,12)
│ │ │ │ ┌─ day of week (0,7, 0 and 7 are Sunday)
│ │ │ │ │
* * * * * command-to-run
A * means "every". Read these examples until the pattern clicks:
0 2 * * * /opt/backup.sh # 02:00 every day
*/5 * * * * /usr/bin/collect # every 5 minutes
30 8 * * 1 /opt/report.sh # 08:30 every Monday (1 = Mon)
0 0 1 * * /opt/monthly.sh # midnight on the 1st of each month
There are handy shortcuts that replace all five fields:
@reboot /home/me/start.sh # once, each time the box boots
@daily /opt/nightly.sh # once a day (same as 0 0 * * *)
@hourly ... @weekly ... @monthly ...
Where cron jobs live
- Your own crontab , per-user, edited with
crontab -e, listed withcrontab -l. These lines have no user field (they run as you). - The system crontab ,
/etc/crontaband drop-in files in/etc/cron.d/. These add an extra user field ("run this as root"). - Run-parts directories , anything dropped in
/etc/cron.daily/,/etc/cron.hourly/,/etc/cron.weekly/,/etc/cron.monthly/runs on that cadence.
crontab -l # list YOUR cron jobs
crontab -e # edit them
cat /etc/crontab # the system-wide schedule (has a user column)
ls -la /etc/cron.d /etc/cron.daily # more places jobs hide
cron's output isn't shown on screen; jobs usually log to the system log (journalctl or /var/log/syslog), or email the user, which is why job lines often end in >> logfile 2>&1.
systemd timers: the modern way
Newer systems increasingly replace cron with systemd timers. A timer always comes in two pieces:
- a
.serviceunit , what to run, - a
.timerunit , when to run it.
backup.service (the what):
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/opt/backup.sh
backup.timer (the when):
[Unit]
Description=Run the backup nightly
[Timer]
OnCalendar=*-*-* 02:15:00 # every day at 02:15 (cron-like calendar)
Persistent=true # if the box was off, catch up on next boot
[Install]
WantedBy=timers.target
You turn it on like any unit, and inspect the schedule:
systemctl enable --now backup.timer # enable + start it
systemctl list-timers # every active timer, and when it next fires
systemctl status backup.timer
OnCalendar is the cron-style "at this wall-clock time" schedule; timers can also fire relative to events with OnBootSec= (after boot) or OnUnitActiveSec= (after the last run). The pay-off over cron: proper logging in journald, dependencies on other units, a randomised delay to avoid thundering herds (RandomizedDelaySec), and catch-up runs with Persistent=true.
cron vs systemd timers
| cron | systemd timer | |
|---|---|---|
| Define with | one crontab line | a .timer + a .service |
| Schedule syntax | * * * * * | OnCalendar=, OnBootSec= |
| List them | crontab -l, cat /etc/crontab | systemctl list-timers |
| Logging | syslog / email | journald (journalctl -u name) |
| Missed runs | skipped | caught up (Persistent=true) |
Both still exist everywhere, so learn to read both.
Enumerating scheduled tasks (the security bit)
When you land on a box, scheduled tasks are a top thing to enumerate, for two reasons: privilege escalation (a job that runs as root, calling a script you can write to, is a direct path to root) and persistence (attackers add a cron job or timer so their access survives a reboot). The sweep:
crontab -l # your jobs
cat /etc/crontab # system jobs (note the user column)
ls -la /etc/cron.d /etc/cron.* # drop-in and run-parts jobs
systemctl list-timers --all # every timer
# then: are any scripts those jobs run world-writable?
ls -la /opt/backup/run.sh
A root cron entry like * * * * * root /opt/backup/run.sh, where run.sh is writable by your user, means you can put any command in that script and it will run as root within a minute. You'll use exactly this in the Privilege Escalation lesson.
Try it (terminal)
crontab -l, read your own scheduled jobs.cat /etc/crontab, the system schedule, spot the extra user column.systemctl list-timers, the modern equivalent, and when each next runs.
Why this matters
Scheduled tasks are how a system takes care of itself, and how both defenders and attackers get things to run automatically. Reading a cron line at a glance and knowing where every job hides, crontabs, /etc/cron.d, and systemd timers, is core Linux fluency, and enumerating them is one of the highest-value moves the moment you have a foothold.