hash-only-1 picoCTF 2025 Solution

Published: April 2, 2025

Description

The flaghasher binary runs with elevated privileges but only prints md5sum /root/flag.txt. Hijack the PATH so md5sum points to your own script that cats the flag.

SSH to shape-facility.picoctf.net -p 51426 (password 8d076785).

Confirm flaghasher is SUID-root and pull its strings to see what it shells out to.

Build a fake md5sum in your home directory and put . first in PATH so it wins the lookup.

bash
ssh -p 51426 ctf-player@shape-facility.picoctf.net
bash
ls -la flaghasher
bash
strings flaghasher | grep -E 'md5sum|/bin/'
bash
echo '/bin/cat /root/flag.txt' > md5sum && chmod +x md5sum
bash
export PATH=.:$PATH && ./flaghasher

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
PATH hijacking is the bread-and-butter of SUID privilege escalation; the Linux CLI for CTF guide covers strings, PATH, and SUID enumeration in depth.
  1. Step 1
    Discover the helper call
    Observation
    I noticed the binary runs with SUID-root privileges but behaves like a wrapper around an external command, which suggested using strings to inspect what it calls and whether that call uses an absolute path or a bare name that PATH controls.
    strings flaghasher produces hundreds of lines, so always pipe straight into grep rather than scrolling. The hit is /bin/bash -c 'md5sum /root/flag.txt'. The bare md5sum (no leading /) means Linux resolves it via PATH - so you control which binary runs.
    bash
    strings flaghasher | grep md5sum
    bash
    # Output: /bin/bash -c 'md5sum /root/flag.txt'
    What didn't work first

    Tried: Run ltrace ./flaghasher or strace ./flaghasher to see what system call it makes instead of using strings.

    ltrace and strace do show the execve call, but they run the binary in a traced mode that can suppress SUID privileges on many Linux kernels (ptrace attach is blocked for SUID binaries). You may see 'Permission denied' or an incomplete trace. strings is the right first step because it is purely static - it reads the ELF file without executing it - so SUID restrictions are irrelevant.

    Tried: Grep for an absolute path like /usr/bin/md5sum in the strings output to confirm it uses the full path.

    The binary intentionally omits the leading path, so grepping for /usr/bin/md5sum returns nothing and can mislead you into thinking md5sum is not involved at all. The correct grep target is the bare name md5sum (or a broader /bin/bash -c pattern), which reveals the unqualified invocation that makes PATH injection possible.

    Learn more

    PATH injection (also called PATH hijacking) exploits the fact that Linux resolves command names by searching directories listed in the PATH environment variable in order from left to right. When a program calls a command by name without specifying an absolute path (e.g., md5sum instead of /usr/bin/md5sum), the OS finds whatever file named md5sum appears first in PATH.

    The strings command is a first-line static analysis tool. Running it on any SUID binary quickly reveals hardcoded paths, system calls, and - as here - the exact shell command being executed. When that command uses an unqualified binary name (no leading /), PATH injection becomes viable. Real-world SUID binaries have historically been vulnerable to this in many Linux distributions.

    SUID (Set User ID) bits allow a binary to run with the permissions of its owner rather than the permissions of whoever executes it. A SUID binary owned by root runs as root regardless of which user launches it. This makes SUID binaries high-value targets for privilege escalation - any vulnerability in them potentially grants root access. Modern systems minimize SUID binaries and use capabilities or sudo policies instead.

  2. Step 2
    Drop in a fake md5sum
    Observation
    I noticed from the strings output that flaghasher calls md5sum by bare name with no leading /, which suggested that prepending . to PATH would make Linux resolve our own script first and run it under root privileges.
    Write a one-line md5sum that cats the flag, chmod +x, prepend . to PATH, and verify PATH order before running flaghasher. Critically, the fake script must NOT call any program named md5sum (no /usr/bin/md5sum, no recursion fallback) or the hijacked PATH will resolve it back to your own script and infinite-loop. If you SSH out and back in, PATH resets - re-export it.
    bash
    echo '/bin/cat /root/flag.txt' > md5sum
    bash
    chmod +x md5sum
    bash
    export PATH=.:$PATH
    bash
    echo $PATH | tr ':' '\n' | head -3   # verify '.' is first
    bash
    ./flaghasher

    Expected output

    picoCTF{sy5teM_b!n@riEs_4r3_5c@red_0f_yoU_bfa4...}
    What didn't work first

    Tried: Write the fake md5sum as #!/bin/sh /usr/bin/md5sum /root/flag.txt so it falls back to the real md5sum to cat the flag.

    Calling /usr/bin/md5sum inside the fake script does not read the flag - it hashes it and prints the MD5 digest, not the plaintext content. The goal is to exfiltrate the flag contents, so the script must use /bin/cat /root/flag.txt. Using the real md5sum defeats the entire hijack by just reproducing the original behavior.

    Tried: Set PATH with PATH=~:$PATH (home directory by tilde) instead of PATH=.:$PATH to avoid typing the dot.

    Tilde expansion (~) is performed by the shell before PATH is set, so PATH=~:$PATH correctly expands to your home directory path. However, ./flaghasher must be run from the same directory where the fake md5sum file lives. If you are in a different directory than your home, the binary will not find your fake script even with the correct PATH prefix - the fake md5sum must be in the directory that is prepended.

    Learn more

    Prepending . (the current directory) to PATH is a classic privilege escalation technique. The current directory is never in PATH by default on modern Linux systems specifically because of this attack - if it were, any malicious executable in the current directory would shadow system commands. However, PATH can be freely modified in a shell session, so an attacker who controls PATH can plant fake commands anywhere in the search order.

    The fake md5sum script demonstrates the principle of command substitution: replacing a legitimate system utility with a malicious one. In real penetration testing, this technique is used to establish persistence (replacing cron-invoked scripts), escalate privileges (replacing commands called by SUID binaries), or intercept sensitive data (replacing commands like ssh that handle credentials).

    The correct fix is for flaghasher to use the absolute path /usr/bin/md5sum instead of the bare name md5sum, and to sanitize or reset the PATH environment variable before calling any external commands. This is documented in the POSIX specification and in secure coding guidelines from CERT, MITRE, and CWE-78 (Improper Neutralization of Special Elements used in an OS Command).

Interactive tools
  • Pwntools ForgeGenerate a complete pwntools exploit script from a template: ret2win, shellcode, ret2libc, ROP chain, format string, or blank scaffold. Fill the form, copy or download the .py file. Fully editable before saving.
  • Hash IdentifierIdentify unknown hash types by length and prefix. Covers MD5, SHA-1, SHA-256, SHA-512, bcrypt, NTLM, and more.
  • Checksum CalculatorCompute CRC32, SHA-1, SHA-256, SHA-384, and SHA-512 hashes for text or uploaded files. Verify against known hashes.

Flag

Reveal flag

picoCTF{sy5teM_b!n@riEs_4r3_5c@red_0f_yoU_bfa4...}

Classic PATH hijacking, so always check PATH order when privileged scripts invoke system tools.

Key takeaway

PATH hijacking exploits the Unix command resolution mechanism: when a privileged process invokes a command by bare name rather than absolute path, the OS searches each directory in PATH left-to-right and runs the first match it finds. Any attacker who can write to a directory that appears earlier in PATH than the legitimate binary location can silently substitute a malicious replacement, inheriting all the privileges of the calling process. The fix is always to use absolute paths in privileged code and to reset or sanitize the PATH environment variable before invoking external commands, which is why hardened setuid programs typically begin by clearing the entire environment.

Related reading

Want more picoCTF 2025 writeups?

Useful tools for Binary Exploitation

What to try next