Description
Special autocorrects every shell command, so you must abuse parameter expansion and braces to execute raw commands and leak the flag.
Setup
SSH to saturn.picoctf.net on port 56058 with the supplied password.
Experiment with bash parameter expansion to bypass the forced capitalization/rewriting and run arbitrary commands under the hood.
ssh -p 56058 ctf-player@saturn.picoctf.netd8819d45${parameter=ls blargh}${parameter=cat < blargh/flag.txt}Solution
Walk me through it- Step 1Probe the sanitizerBare ls or cat are rewritten, but expressions starting with ${ slip through. Test ${parameter=ls} as a smoke check before chaining anything else.
Learn more
The Special shell wraps every line in a regex-style rewriter that hunts for known command names at the start of input. The regex sees a literal token; it does not understand the rest of Bash's grammar. The form
${parameter=word}is the assign-default parameter expansion: ifparameteris unset, Bash sets it towordand substitutes that value, which then runs as a command. From the sanitizer's point of view the line begins with a brace, not a verb, so it leaves it alone.A worked trace makes the asymmetry concrete. Input
${parameter=ls}arrives. The sanitizer scans forls,cat,cdas the first identifier; finding${instead, it passes the line through. Bash then parses parameter expansion, assignsparameter=ls, substitutesls, and only at that point does the shell try to run the command. The rewriter never gets a second look.Empirical edge cases worth confirming on the box:
${ ls}with a leading space is a syntax error (Bash expects an identifier immediately after the brace), and${ls}with no operator simply expands an unset variable to the empty string. The sanitizer rejects neither, but only the assign-default form (=) actually runs anything. JSX renders the literal braces with{and}escapes; the shell line itself is plain.The technique generalises across restricted shells:
rbash,git-shell, and CTF jails all leak when the filter is built on string matching rather than a full Bash parser. The wider toolbox covers aliases, functions, command substitution$(cmd), process substitution<(cmd), brace expansion, and SUID detours throughvim :!orawk system(). See Linux CLI for CTFs for a broader survey of these patterns. - Step 2Chain the exploitUse ${parameter=ls blargh} to enumerate the directory and ${parameter=cat < blargh/flag.txt} to read the flag through stdin redirection.
Learn more
The redirection trick is the second half of the bypass. The sanitizer matches arguments to commands; it does not parse redirection operators.
cat < blargh/flag.txtopens the file ascat's stdin instead of passing it asargv[1], so a filter that blockscat foooften missescat < foo. Combine that with the parameter-expansion wrapper and the whole expression sails past the rewriter.The same lesson applies to real restricted-shell deployments: kiosk terminals,
git-shell, container init shells. Whenever the gate is "match these tokens" rather than "parse the language and enforce a whitelist on the AST," the gate leaks. The only durable fix is to swap the shell for a non-shell interface or to enforce the boundary at the syscall layer with seccomp or namespaces.
Flag
picoCTF{5p311ch3ck_15_7h3_w0...35}
Any creative use of ${parameter=...} (or similar expansion) that runs cat on blargh/flag.txt yields the answer.