Description
The Multiverse is within your grasp! Unfortunately, the server that contains the secrets of the multiverse is in a universe where keyboards only have numbers and (most) symbols.
Setup
SSH to mimas.picoctf.net on port <PORT_FROM_INSTANCE> with password <PASSWORD_FROM_INSTANCE>.
Experiment with the restricted shell to learn which characters it accepts and how it responds to your input.
Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Capture an error message into a variable
ObservationThe shell rejects every letter you type, so the only letters available are the ones it prints itself. Trap a predictable error message into a variable and index into it later.Pick an invalid token like $ or ? and trap its error output in a variable. _1=$ 2>&1runs the bare $ as a command, redirects stderr into stdout, and stores "bash: $: command not found" in $_1. Verify with echo "$_1" - if you see that single line printed back, you have your character source.bash_1=`$ 2>&1`bashecho "$_1"Expected output
bash: $: command not found
What didn't work first
Tried: Using $() instead of backticks to capture the error, without the 2>&1 redirect
A plain capture takes stdout only, and bash error messages go to stderr, so the variable ends up empty and you have no character source. Merge stderr into stdout inside the capture with 2>&1.
Tried: Running a random word like foo 2>&1 instead of $ to trigger the error
Letters are rejected before the shell ever tries to run anything, so no error message is produced. Use a non-letter token like $ instead: the shell tries to evaluate it, fails, and prints the 'command not found' string you need.
Learn more
The keyboard restriction blocks letters, but parameter expansion (
${_1:N:1}) does not require typing letters - it indexes into a string by position. By trappingbash's own error output we get a long string full of letters we can index into.$as a standalone command is the cleanest trigger because it parses, fails immediately, and produces exactly one line:bash: $: command not found.?works similarly. Wrapping in backticks runs the command in a subshell and captures its output; the2>&1redirection sends stderr (where the error actually lives) onto stdout so it can be captured. Without that redirection$_1would silently end up empty, and the next step would fail with no useful diagnostic.Step 2Extract characters with parameter expansion
ObservationThat captured string, 'bash: $: command not found', puts common lowercase letters at fixed positions. Bash's substring expansion plucks them out one at a time without typing a letter.Bash ${var:offset:length} slices a substring out of a variable. Test locally first: echo "${_1:9:1}" should print c, and echo "${_1:10:1}" should print o. From the captured string "bash: $: command not found", you can pluck b/a/s/h/c/o/m/n/d/t/f/u as needed.Learn more
This is the heart of the trick. The captured error string contains the lower-case alphabet's commonly-used letters in fixed positions, so you can spell short command names like
echo,cat,sh, orbashby chaining slices.String: bash: $: command not found Position: 01234567890123456789012345 1111111111222222 Useful indices (verify with: echo "${_1:N:1}"): ${_1:0:1} = b ${_1:1:1} = a ${_1:2:1} = s ${_1:3:1} = h ${_1:9:1} = c <- needed for /bin/echo ${_1:10:1} = o <- needed for /bin/echo ${_1:13:1} = a ${_1:15:1} = d ${_1:17:1} = n ${_1:19:1} = t ${_1:23:1} = u ${_1:24:1} = nStep 3Locate the flag file with a glob
ObservationWildcards need no letters at all. A glob like ./*/* enumerates files with no ls available, and bash names the matched path in the error it prints when it tries to execute it.Run ./*/* and bash expands the glob to whatever paths exist; the listing here shows the flag at ./blargh/flag.txt, matching pattern ./*/????.???.bash./*/*Expected output
bash: ./blargh/flag.txt: Permission denied
What didn't work first
Tried: Running /* or /* /* to enumerate files instead of ./*/*
A glob starting at / expands across the whole filesystem root and buries the challenge directory in system paths. Anchor it with ./ so it stays in the working directory where the flag is.
Tried: Trying to read the flag directly after seeing the path in the error message
The shell answered 'Permission denied' when it tried to execute the file as a command, so that route is closed. You have the path; reading it still needs a constructed command or command substitution, which the next steps build.
Learn more
./*/*expands to the list of files exactly one directory deep below the current directory. Bash tries to execute the first match as a command, which (helpfully) fails and prints the file path in the error message - giving you the path even when you can't type letters tols. The pattern./*/????.???later picks out the same path by structure (4-letter name, 3-letter extension).Step 4Build /bin/echo from globs and slices
Observation/bin is exactly three characters and echo is four. A pattern like /???/?c?o, with the c and o supplied as slices of the captured error string, resolves to /bin/echo with no letters typed.Glob /???/?${_1:9:1}?${_1:10:1} into the path /bin/echo. The /??? matches /bin (any 3-char directory under /), and the inner ?c?o pattern matches "echo" via positions 9 and 10 of $_1. The earlier attempt at /usr/bin/cat (/?${_1:2:1}?/???/??${_1:19:1}) failed because the ?s? prefix didn't expand to /usr on the box (the glob found no matches and bash printed "bash: /?s?/???/??t: No such file or directory").bash/???/?${_1:9:1}?${_1:10:1}What didn't work first
Tried: Trying to reach /usr/bin/cat with /?${_1:2:1}?/???/??${_1:19:1} because cat is more familiar than echo
That pattern needs a three-character directory under / with s in the middle, and this machine has none. Bash leaves the unmatched pattern literal and the shell reports no such file. The simpler /???/?c?o works because /bin is three characters and echo is four.
Tried: Constructing the full path as a string in a variable, then running it with ${_cmd}
Assigning the path to a variable is blocked, since the assignment contains letters. Even built from slices, expanding it as the command name does not behave like a bare glob: bash does not re-split and re-expand a variable used in that position. Put the glob directly in the command slot.
Learn more
Verify the glob expands the way you expect before using it as the command:
echo /???/?${_1:9:1}?${_1:10:1}should print/bin/echoand nothing else. If it prints multiple matches or the literal pattern, the glob is wrong and the command will not execute echo.When a glob has no match, bash on this image leaves the literal pattern in place, so the failure surface is "
/?s?/???/??t: No such file or directory" - the unmatched pattern itself appears in the error, which doubles as a diagnostic. That's the failure mode you should expect, and how you knew the/usr/bin/catpath's middle?s?wedge wasn't hitting/usr.Step 5Read the flag
ObservationBash's $(<file) reads a file as a builtin, with no cat involved. Pair it with a glob for the flag path and the reconstructed echo, and the flag prints.Combine the constructed echo with bash command substitution: /???/?${_1:9:1}?${_1:10:1} "$(<./*/????.???)" prints the contents of ./blargh/flag.txt without ever typing the literal letters cat, less, or grep.bash/???/?${_1:9:1}?${_1:10:1} "$(<./*/????.???)"Expected output
picoCTF{7h15_mu171v3r53_15_m4dn355_145...}What didn't work first
Tried: Using $(cat ./blargh/flag.txt) instead of $(<./blargh/flag.txt) to read the file
The word cat contains letters, which the restricted shell blocks before the command can even run. $(<file) is a bash builtin read-file form that requires no external program and no letters, making it the only viable option in this environment.
Tried: Passing the glob ./*/????.??? directly as a shell argument without the $(<...) wrapper
Without the $(<...) wrapper the glob expands to the path as a string, so echo prints the path rather than the flag. That wrapper is what makes bash open the file and pass its contents along.
Learn more
$(<file)is a bash builtin that reads the contents offilewithout spawningcat; it's the bash idiom for "file slurp." Combined with the globbed-upecho, the final command never types any disallowed character but still prints the flag contents.
Interactive tools
- Regex TesterTest regular expressions against a string with live match highlighting, flag toggles, and common CTF pattern shortcuts.
- 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{7h15_mu171v3r53_15_m4dn355_145...}
There are multiple paths through this challenge - the key insight from the hint 'Where can you get some letters?' is to harvest characters from error messages and use bash parameter expansion to build valid commands.