Description
Specialer offers a crippled shell with only a few commands that still obey tab completion. Explore its limited filesystem to find the hidden magician's note.
Setup
SSH to saturn.picoctf.net on port 57125 with the provided password.
Use tab completion inside each directory to discover files, then rely on echo "$(<file)" to print them since cat is unavailable.
ssh -p 57125 ctf-player@saturn.picoctf.net483e80d4cd abra && echo "$(<cadabra.txt)"cd ../ala && echo "$(<kazam.txt)"Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
List allowed commandsObservationI noticed the shell blocked both ls and cat immediately, which suggested the environment was a restricted jail and that I needed to rely on Bash built-ins and tab completion to enumerate what commands were actually available.Press Tab twice to see the approved verbs. Standard tools are missing, but cd and echo survive as shell built-ins; ls is gone and Tab completion fills that role instead.What didn't work first
Tried: Typing 'ls' or 'cat' directly to see what files exist.
The jail shell blocks both commands and returns a 'command not found' error. Beginners assume they need ls to enumerate the directory and give up, not realizing that Tab completion from the bash built-in surfaces the same filename list without ever calling an external binary.
Tried: Trying 'help' or 'man' to understand what commands are available.
Neither is present in the pruned PATH. The double-Tab trick works because bash resolves completions against its own internal built-in table before consulting PATH, so pressing Tab twice at an empty prompt reveals every allowed verb including shell built-ins that don't appear in PATH at all.
Learn more
Double-Tab in Bash triggers command completion against everything in
$PATH. In a jail shell$PATHis pruned to a tiny whitelist, so Tab-Tab is essentially a directory listing of the allowed binaries. Built-ins likecd,echo,pwd, andreadstill show up because they live inside the shell binary itself, not in$PATH.The discovery technique also extends to filenames. Type
echo $(<and press Tab in the current directory: Bash offers completions for files that match the partial substitution prefix, even thoughlsis blocked. Press Tab on a partial filename likeecho $(<cadand it will complete tocadabra.txtif it exists. That is enumeration without ever running an enumeration command.When standard tools vanish, knowing which features are built into the shell versus which need an external program is the entire game.
echo,printf,read, redirection, and globs cover most file-inspection tasks. The same skill helps in BusyBox and Alpine environments wherecatmay exist but most GNU coreutils do not.Step 2
Traverse directoriesObservationI noticed the challenge description mentioned a hidden magician's note across the filesystem and that cat was unavailable, which suggested I needed to use the echo "$(<file)" built-in redirection pattern to read files while navigating through the abra/ and ala/ subdirectories.Move through abra/ and ala/, reading each *.txt file with echo "$(<file.txt)". The flag resides inside ala/kazam.txt.What didn't work first
Tried: Using 'echo $(<file.txt)' in the wrong directory or with the wrong filename.
The flag is in ala/kazam.txt, not in abra/cadabra.txt. Beginners who find text in the first file they open may assume they are done, but cadabra.txt contains only a partial or decoy string. The correct file is reached by navigating to the sibling ala/ directory and reading kazam.txt.
Tried: Trying 'echo $(cat file.txt)' or 'less file.txt' as an alternative to cat.
Both cat and less are absent from the jail's PATH, so these commands fail with 'command not found'. The $(<file) redirection pattern is the correct approach because it uses only bash's built-in I/O redirection and produces the same output as cat without invoking any external program.
Learn more
The mechanism behind
$(<file)is worth understanding. Inside a command substitution,<fileopens the file as standard input to an empty command. Because the command produces no output of its own, the substitution captures the file's contents verbatim. It is exactly equivalent to$(cat file)minus thecatinvocation, which is why it works whencatis missing.Test the glob bypass too:
echo $(<*)expands the glob to the only matching file in the directory and reads it through the same redirection trick. If multiple files match, Bash will error out (you cannot read multiple files into one substitution that way), which itself is a useful enumeration signal.cdstill works with paths but rejects flags here, so navigate with bare directory names likecd ../ala.The pattern carries the same lesson as Special: filtering by command name is fragile. The robust way to lock down a shell is at the architectural layer (seccomp, mount namespaces, read-only filesystems) rather than by removing names from
$PATH. See Linux CLI for CTFs for more shell-restriction patterns.
Flag
Reveal flag
picoCTF{y0u_d0n7_4ppr3c1473_wh47_w3r3_d01...8b71}
The spell checker may be gone, but shell globbing still reveals the hidden text file.