Bandit 23 → 24: hand the privileged user your script
Third cron level in a row, and the pattern turns around. The last two had a job that already did the work — find or recompute where it left the password. This one runs a job that never touches the password at all. It runs whatever script I leave in a directory, as bandit24. So for the first time the job needs something from me, and the whole difficulty is two permission traps hiding in three short lines.
the goal
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.NOTE: This level requires you to create your own first shell-script. NOTE 2: Keep in mind that your shell script is removed once executed, so you may want to keep a copy around…
Both notes are the level telling on itself: I have to supply code, and whatever I supply gets deleted after one run.
the approach
Read the cron config first, same as the last two:
cat /etc/cron.d/cronjob_bandit24
* * * * * bandit24 /usr/bin/cronjob_bandit24.sh &> /dev/null
Runs as bandit24, every minute. Then the script it runs:
cat /usr/bin/cronjob_bandit24.sh
cd /var/spool/"$myname"/foo || exit
for i in * .*;
do
owner="$(stat --format "%U" "./$i")"
if [ "${owner}" = "bandit23" ] && [ -f "$i" ]; then
timeout -s 9 60 "./$i"
fi
rm -rf "./$i"
done
That’s the whole level. For every file in /var/spool/bandit24/foo it checks
who owns it, and if the owner is bandit23 and it’s a regular file, it runs
it — then rms it either way.
Notice what it never does: mention the password. Last level’s script did
cat /etc/bandit_pass/... > /tmp/..., reading the secret for me. This one is
just an engine that runs my code as bandit24. The read is the part I write.
That flips the problem. The password file is -r--------, owned by bandit24, so
there’s no reading it as bandit23. Every earlier level the instinct was to try to
become the owner — run-as, fake whoami, chown. All dead here, and unnecessary:
the job hands me the owner. Inside my script I already am bandit24. I don’t
become the actor, I write the actor’s instructions.
So: read the password, drop it somewhere reachable. The obvious version is wrong twice.
Trap one — ~ isn’t my home.
#!/bin/bash
cp /etc/bandit_pass/bandit24 ~/bandit24.txt
The cp succeeds and produces nothing I can reach. The script runs as
bandit24, so ~ expands to bandit24’s home. A relative or ~ path in a
dropped script resolves as the runner, not the author. Point it at a scratch
dir I control, spelled absolutely:
mkdir /tmp/aaa
chmod 777 /tmp/aaa # so bandit24 can write into it
Trap two — cp drags the source’s perms across.
#!/bin/bash
cp /etc/bandit_pass/bandit24 /tmp/aaa/bandit24.txt
A minute later the file appears, and:
cat /tmp/aaa/bandit24.txt
cat: bandit24.txt: Permission denied
That Permission denied is progress, not failure — the cron ran, bandit24
executed my script, the file exists. It’s unreadable because plain cp creates
the destination inheriting the source’s mode. The source is -r--------
(400), so the copy is 400 too, owned by bandit24.
The tempting move here is to read it by hand, which can never work: as bandit23 I can’t read the source at all. That impossibility is the level. Only the cron, running as bandit24, can.
Two fixes, both used. A redirect (>) makes a fresh file under my umask
instead of cloning the source’s 400, and an explicit chmod guarantees it:
#!/bin/bash
cat /etc/bandit_pass/bandit24 > /tmp/aaa/bandit24.txt
chmod 644 /tmp/aaa/bandit24.txt
One snag: the leftover 400 file from the previous attempt blocks the new write, and I don’t own it — but I own the directory, which is enough to delete it. Then deploy and wait one tick:
chmod +x copy.sh
cp copy.sh /var/spool/bandit24/foo/
cat /tmp/aaa/bandit24.txt
<redacted>
(/var/spool/bandit24/foo is drwxrwx-wx — others can write and traverse but not
list. Fine here: I only ever drop a file in.)
the takeaway
The brick is the reversal. The last two levels drilled don’t try to be the actor, read what the actor left behind. This is the same idea from the other side: when a secret is genuinely unreadable, don’t attack the permission — get the process that’s allowed to read it to do so on your behalf, by feeding it the code. A cron job that blindly executes a file I own is a privilege boundary I get to write both sides of.
Two permission facts worth keeping:
- A path in a script belongs to whoever runs the script.
~and any relative path resolve as the runner, not the author. Write absolute paths and give the runner a world-writable landing spot. cpcopies the source’s mode, not a fresh one. Copying a400file gives you a400file owned by the copier. When the point is to hand the result to someone else, use a redirect orchmodit — don’t let the secret’s own tight perms follow it out.