Escaping an uppercase-only shell with the $0 variable and landing as a higher-privileged user through a SUID wrapper.
Introduction
Day 32. Bandit Level 32 to Level 33. This is the final level of Bandit and it ends the way the series began, with a shell escape. After a long run through git, the challenge drops me into a shell that mangles everything I type into uppercase, rendering every normal command useless.
The core skill here is understanding shell variables, specifically $0, and combining that with the SUID concept from earlier levels. The escape is a single character, but why it works ties together two ideas the series has been building toward the whole way through.
By the end of this article you will understand what the uppercase shell does, why $0 breaks out of it and how the wrapper binary's SUID bit quietly hands over the next user's privileges. It is a fitting capstone for everything the series has covered.
Level Objective
The official OverTheWire page is brief. After all the git work, it is time for another escape. The task is to find a way out of the restricted shell and get the password for the next level. Login is as bandit32 over SSH on port 2220 with the password carried over from the last level.
Approach
I connected to bandit32 over SSH. The login banner rendered and instead of a normal prompt I was greeted with WELCOME TO THE UPPERCASE SHELL and a double angle bracket prompt. Any command I typed would be converted to uppercase before running, so lowercase tools like ls and cat would never be found:
ssh [email protected] -p 2220

The one thing on a Linux system that is normally uppercase is variables, and the shell variable $0 holds the name of the currently running shell program. Typing $0 at the prompt tells the shell to execute itself by name, which spawns a fresh interactive shell that does not apply the uppercase filter. That single command broke me out:
$0
whoami
cat /etc/bandit_pass/bandit33
Once I had a normal dollar prompt, I ran whoami and found I was no longer bandit32. I was bandit33. The uppercase shell binary, uppershell, is SUID and owned by bandit33, so the shell it spawned inherited bandit33's privileges. From there I read the password file for bandit33 directly.

Commands Used
# Connect to the Bandit server as bandit32, landing in the uppercase shell
ssh [email protected] -p 2220
# Execute the shell by its own name to spawn a normal unrestricted shell
$0
# Confirm the spawned shell runs as bandit33 via the SUID binary
whoami
# Read the next level's password now that the shell runs as bandit33
cat /etc/bandit_pass/bandit33
Command Breakdown
$0 This is the whole escape. $0 is a special shell variable that holds the name of the program currently running, which here is the shell binary itself. Typing it alone tells the shell to launch itself and the newly spawned shell does not wrap input in the uppercase filter. Because $0 is already effectively uppercase, it survives the shell's mangling and executes.
whoami Prints the current user. The result, bandit33 rather than bandit32, is the tell. The shell that was just spawned is running with elevated privileges rather than the account that logged in.
cat /etc/bandit_pass/bandit33 Reads the password file for bandit33. Normally bandit32 could never open this file, but because the spawned shell runs as bandit33 through the SUID wrapper, the read succeeds and prints the password.
uppershell and the SUID bit The uppercase shell is provided by a binary called uppershell that carries the SUID bit and is owned by bandit33. Any shell it spawns inherits bandit33's effective privileges. This is the same mechanism from the earlier setuid levels, applied here to a restricted shell wrapper.
Lesson Learned
The main technical takeaway is that a restricted shell built on top of a normal one is only as strong as its input filtering. The uppercase shell blocked lowercase commands but let a variable expansion through, and that one gap was enough to spawn an unrestricted shell.
What clicked during this level was seeing two earlier lessons combine into one. The $0 shell variable is the kind of thing that appeared around the cron and variable levels, and the SUID privilege inheritance is straight out of the Day 26 setuid level. The escape is not one trick. It is two ideas meeting at exactly the right point.
Going forward, whenever I face a restricted shell I would probe what it does and does not filter, then reach for shell built-ins and variables that slip past the filter. $0, sh and similar are the first things to try. Any wrapper that interprets input at all can usually be turned against itself.
- echo $0 — see the name of the current shell program
- $0 — spawn a fresh shell from within a restricted one
- whoami and id — confirm the privilege context immediately after an escape
- ls -la — check a binary's SUID bit and its owner
- sh and bash — alternative shells to invoke when the current one is restricted
- printenv — inspect the full environment from inside a restricted shell
🔴 SOC Analyst Insight
Restricted shells are a real and widely used control. Organisations drop users into rbash, lshell or custom wrapper shells to limit what they can run on a host. This level demonstrates the uncomfortable truth that these restrictions are frequently bypassable, especially when the wrapper interprets any user input at all.
When investigating a host, restricted shell escapes are a live threat worth taking seriously. If a low-privilege user or a compromised service account can break out and, worse, inherit privileges through a SUID wrapper, a small foothold becomes a serious one very quickly.
# Inspect the account database for restricted or non-interactive shells
grep -rE "rbash|lshell|/bin/false|nologin" /etc/passwd
The command above inspects the account database for restricted or non-interactive shells, showing which users are meant to be confined. With that knowledge, the next step is checking whether any SUID binaries or interpretable wrappers give those confined users an escape path. This level is exactly that scenario, a restricted user escaping into a SUID-owned context.
The connection back to this level is precise. The uppercase shell was the restriction, $0 was the escape and the SUID bit on uppershell was the privilege escalation stacked on top. In production, defenders harden restricted shells by stripping interpreters, disabling variable expansion and auditing SUID binaries relentlessly. A shell escape combined with SUID is a textbook local privilege escalation, and catching it early is the difference between a contained incident and a fully compromised host.
Key Takeaway
A restricted shell is only as strong as its input filtering, and a single unfiltered variable like $0 can spawn a full shell right through it. Stack a SUID wrapper on top and the escape also becomes a privilege escalation. Restricted shells slow attackers down, they do not stop determined ones. Real containment comes from stripping interpreters, auditing SUID binaries and never trusting a wrapper to hold on its own.
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
✅ Day 24. — Bandit Level 24 → 25 | Brute forcing and loops
✅ Day 25. — Bandit Level 25 → 26 | Restricted shells
✅ Day 26. — Bandit Level 26 → 27 | SUID privilege escalation
✅ Day 27. — Bandit Level 27 → 28 | git clone over SSH
✅ Day 28. — Bandit Level 28 → 29 | git log and git show
✅ Day 29. — Bandit Level 29 → 30 | git branches
✅ Day 30. — Bandit Level 30 → 31 | git tags
✅ Day 31. — Bandit Level 31 → 32 | git push and .gitignore
✅ Day 32. — Bandit Level 32 → 33 | Uppercase shell escape ← today
🏁 Series Complete — Bandit 0 through 33 | 30-Day Journey finished
Thank you for following along through the entire series. Every level, every command and every lesson is now documented.
Every restricted shell is a promise someone hopes you will not test, and $0 is the quietest way to call the bluff.
OverTheWire Bandit Walkthrough — Level 32 → 33 | 30-Day Cybersecurity Learning Journey (Day 32) was originally published in System Weakness on Medium, where people are continuing the conversation by highlighting and responding to this story.