Skip to main content

Specialer picoCTF 2023 Solution

Navigate a restricted SSH environment and use creative shell techniques to read a hidden flag file.

Published: April 26, 2023Updated: August 25, 2026

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.

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.

bash
ssh -p 57125 ctf-player@saturn.picoctf.net
bash
483e80d4
bash
cd abra && echo "$(<cadabra.txt)"
bash
cd ../ala && echo "$(<kazam.txt)"

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1List allowed commands
    Observation
    The shell blocks both ls and cat right away, so this is a restricted jail. Fall back on Bash built-ins and tab completion to find out what is 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 blocks both and answers 'command not found'. It is easy to assume you need ls to see the directory and stop there, but Tab completion lists the same filenames without calling any external binary.

    Tried: Trying 'help' or 'man' to understand what commands are available.

    Neither survives in the pruned PATH. Double-Tab works because bash completes against its own built-in table before it consults PATH, so an empty prompt reveals every allowed verb, built-ins included.

    Learn more

    Double-Tab in Bash triggers command completion against everything in $PATH. In a jail shell $PATH is pruned to a tiny whitelist, so Tab-Tab is essentially a directory listing of the allowed binaries. Built-ins like cd, echo, pwd, and read still 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 though ls is blocked. Press Tab on a partial filename like echo $(<cad and it will complete to cadabra.txt if 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 where cat may exist but most GNU coreutils do not.

  2. Step 2Traverse directories
    Observation
    The description mentions a magician's note hidden somewhere on the filesystem, and cat is gone. Read files with the built-in redirection pattern instead while walking 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 lives in ala/kazam.txt, not abra/cadabra.txt. Finding text in the first file you open feels like the end, but cadabra.txt holds only a decoy. Move to the sibling ala/ directory and read kazam.txt.

    Tried: Trying 'echo $(cat file.txt)' or 'less file.txt' as an alternative to cat.

    Neither cat nor less is in the jail's PATH, so both fail with 'command not found'. The $(<file) pattern uses only bash's own I/O redirection and prints the same thing without invoking an external program.

    Learn more

    The mechanism behind $(<file) is worth understanding. Inside a command substitution, <file opens 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 the cat invocation, which is why it works when cat is 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. cd still works with paths but rejects flags here, so navigate with bare directory names like cd ../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.

Interactive tools
  • Regex TesterTest regular expressions against a string with live match highlighting, flag toggles, and common CTF pattern shortcuts.
  • Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
  • Reverse Shell GeneratorGenerate reverse shell payloads (bash, nc, python, perl, ruby, php, node, powershell) and matching listeners. Set host and port once, copy any variant.

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.

Key takeaway

Restricted shells that block external commands by removing them from PATH can still be circumvented using bash built-ins alone, because $(<file) reads files without cat and double-Tab enumerates allowed commands without ls. The durable defense is to enforce the isolation at the kernel layer - mount namespaces, seccomp filters, or a non-shell interface - rather than pruning PATH.

Related reading

Useful tools for General Skills

Where to go next