CTF Write-up: hash-only-1

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:

  1. A C++ string is constructed with the value: "/bin/bash -c 'md5sum /root/flag.txt'"
  2. The program prints the message: "Computing the MD5 hash of /root/flag.txt.... "
  3. A call to sleep(2) is made, pausing the program for two seconds.
  4. Privileges are elevated using setgid and setuid.
  5. The program calls the system() function with the command string.

This analysis reveals two potential attack vectors:

  1. 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.
  2. Command Injection / PATH Hijacking: The system() call invokes /bin/sh, which then has to find the md5sum executable. Since the command does not use an absolute path (e.g., /usr/bin/md5sum), the shell will search for md5sum in the directories listed in the PATH 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:

  1. Start flaghasher in the background.
  2. Get its Process ID (PID).
  3. Find the memory offset of the “md5sum” string.
  4. Use dd to overwrite it with "cat ".
  • Result: Failed. The command grep -abom1 "md5sum" /proc/$PID/mem returned a Permission 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:

  1. Create a Malicious md5sum Script:
    Since text editors like nano or vi might not be available, we can use echo to create our script. This script, when executed, will simply print the contents of the flag file.

    echo 'cat /root/flag.txt' > md5sum
    
  2. Make the Script Executable:
    The file needs execute permissions for the shell to run it as a command.

    chmod +x md5sum
    
  3. Modify the PATH and Execute:
    We prepend the current directory (.) to the PATH 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 modified PATH.
  • 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 modified PATH.
  • 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