Description
We have a binary that can list directories as root. SSH into the provided server, run the binary named bin, and find a way to read the root flag through it.
Setup
SSH into the provided server as ctf-player using the supplied password and port.
Locate the SUID binary named bin in your home directory and run it once to see how it behaves.
ssh ctf-player@saturn.picoctf.net -p <PORT_FROM_INSTANCE>Solution
Walk me through it- Step 1Observe default behaviorRunning ./bin with nothing set prints: Error: SECRET_DIR environment variable is not set. The binary uses the SECRET_DIR value as the directory it lists, and it does the listing as root because it is SUID.bash
./binLearn more
A SUID binary runs with the privileges of its owner (here, root) regardless of who launches it. This binary lists the contents of whatever directory the
SECRET_DIRenvironment variable points to, but performs that listing with root privileges. The intended use is harmless directory browsing; the bug is in how it constructs the listing command.Before crafting anything, run the binary normally to learn which environment variable it reads. The error message names it directly:
SECRET_DIR. You can also confirm the SUID bit withls -l bin(look for ansin the owner permission field). - Step 2List directories as root with SECRET_DIRSet SECRET_DIR on the command line. Pointing it at /root shows that the flag file exists there, but a normal cat on it is denied because the file itself is only readable by root.bash
SECRET_DIR=. ./binbashSECRET_DIR=/root ./binbashcat /root/flag.txtThe listing of/rootrevealsflag.txt, but a directcat /root/flag.txtreturnsPermission deniedbecause only the binary runs as root, not your shell.Learn more
Supplying
SECRET_DIR=/rootconfirms the flag location, but you still cannot read the file directly. The binary holds the root privilege, your interactive shell does not. The path forward is to make the privileged binary itself read the file rather than just listing the directory.This is the classic split that makes these challenges solvable: the privileged process accepts attacker-controlled input (the environment variable) and feeds it into a shell command without sanitizing it.
- Step 3Inject a command through SECRET_DIRBecause the binary passes SECRET_DIR into a shell, you can append your own command with a shell separator. Using & (or ;) chains a cat of the flag that runs as root, printing the flag.bash
SECRET_DIR="/root&cat /root/flag.txt" ./binLearn more
This is a command injection vulnerability. The binary takes the
SECRET_DIRvalue and interpolates it into a shell command line (something likels $SECRET_DIR) that runs with root privileges. Because the value is never escaped, a shell metacharacter such as&,;, or|breaks out of the intended command and lets you run a second one.Setting
SECRET_DIR="/root&cat /root/flag.txt"makes the binary first list/root, then runcat /root/flag.txtas root, which prints the flag inline with the listing output. The fix in real software is to never build shell commands from untrusted input: pass arguments toexecvedirectly, or strictly validate and quote any value before it reaches a shell. - Step 4Read the flagThe injected cat prints the flag value alongside the directory listing. Copy it and submit.
Learn more
Command injection through an unsanitized environment variable is a real and recurring class of privilege-escalation bug. Whenever a privileged program builds a shell command from data an unprivileged user controls (environment variables, file contents, network input), an attacker can chain arbitrary commands at the elevated privilege level.
Flag
picoCTF{...}
The SECRET_DIR command-injection payload (SECRET_DIR="/root&cat /root/flag.txt" ./bin) prints the flag inline with the directory listing.