CTF Write-up: hash-only-1
Challenge Summary
The challenge provides a 64-bit ELF binary named flaghasher
. When run, the program has sufficient privileges to access /root/flag.txt
, but it is designed to only output the MD5 hash of the file’s contents, not the contents themselves. The goal is to find a way to make the program reveal the flag.
Initial Analysis & Reconnaissance
The first step is to understand what the binary does. Analyzing the decompiled code (using a tool like Ghidra or IDA Pro, or simply running strings
on the binary) reveals the core logic in the main
function:
- A C++ string is constructed with the value:
"/bin/bash -c 'md5sum /root/flag.txt'"
- The program prints the message:
"Computing the MD5 hash of /root/flag.txt.... "
- A call to
sleep(2)
is made, pausing the program for two seconds. - Privileges are elevated using
setgid
andsetuid
. - The program calls the
system()
function with the command string.
This analysis reveals two potential attack vectors:
- Race Condition (TOCTOU): The 2-second
sleep
provides a window between when the command is prepared and when it is executed. We could try to modify the command string in memory during this pause. - Command Injection / PATH Hijacking: The
system()
call invokes/bin/sh
, which then has to find themd5sum
executable. Since the command does not use an absolute path (e.g.,/usr/bin/md5sum
), the shell will search formd5sum
in the directories listed in thePATH
environment variable.
Attack Path 1: Race Condition (Failed)
This was the most obvious path due to the sleep(2)
call. The plan was to patch the program’s memory during the 2-second window.
Attempt A: Using GDB
The standard approach is to use a debugger. We would run the program under gdb
, set a breakpoint after the sleep
call (or on the system
call itself), modify the command string in the rdi
register from "md5sum"
to "cat "
, and continue execution.
- Result: Failed. The remote server did not have
gdb
installed.
Attempt B: Using /proc/$PID/mem
Without a debugger, we can still try to directly write to the process’s memory via the /proc
filesystem. The plan was to run a script or one-liner that would:
- Start
flaghasher
in the background. - Get its Process ID (PID).
- Find the memory offset of the “md5sum” string.
- Use
dd
to overwrite it with "cat ".
- Result: Failed. The command
grep -abom1 "md5sum" /proc/$PID/mem
returned aPermission denied
error. This is due to a modern Linux kernel security feature called Yama, which restricts a process from reading/writing another process’s memory, even when owned by the same user. This security hardening effectively prevented the memory-patching attack.
Attack Path 2: PATH Hijacking (Successful)
Since direct memory modification was blocked, we pivot to the second vulnerability: the insecure call to system()
. The program relies on the shell’s PATH
search to find md5sum
. We can exploit this by creating our own malicious md5sum
and manipulating the PATH
to ensure ours is found first.
The Exploit Steps:
-
Create a Malicious
md5sum
Script:
Since text editors likenano
orvi
might not be available, we can useecho
to create our script. This script, when executed, will simply print the contents of the flag file.echo 'cat /root/flag.txt' > md5sum
-
Make the Script Executable:
The file needs execute permissions for the shell to run it as a command.chmod +x md5sum
-
Modify the PATH and Execute:
We prepend the current directory (.
) to thePATH
environment variable for the duration of a single command. This forces the shell to look for executables in our current directory before looking in standard locations like/usr/bin
.PATH=.:$PATH ./flaghasher
Execution Flow:
- Our command starts
flaghasher
with a modifiedPATH
. flaghasher
prints its message and sleeps for 2 seconds.- It calls
system("... md5sum ...")
. - The shell looks for
md5sum
. It first checks the current directory (.
) because of our modifiedPATH
. - It finds and executes our malicious
md5sum
script. - Our script runs
cat /root/flag.txt
, printing the flag to standard output.
Conclusion
The intended solution was a PATH hijacking attack. The sleep(2)
call was a red herring that led to a more complex but ultimately blocked race condition attack. The key vulnerability was the developer’s failure to use an absolute path for an executable within a privileged program, allowing an attacker to control which program gets executed.
Comments
Post a Comment