As part of a security CS course, my class has been given the task of exploiting a vulnerability to beat a password check using a stack/buffer overflow. The code with the vulnerability is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>
int main(int argc, char **argv) {
char correct_hash[16] = {
0xd0, 0xf9, 0x19, 0x94, 0x4a, 0xf3, 0x10, 0x92,
0x32, 0x98, 0x11, 0x8c, 0x33, 0x27, 0x91, 0xeb
};
char password[16];
printf("Insert your password: ");
scanf("%29s", password);
MD5(password, strlen(password), password);
if (memcmp(password, correct_hash, 16) == 0) {
printf("Correct Password!\n");
} else {
printf("Wrong Password, sorry!\n");
}
return 0;
}
I understand the classic "stack-smashing" principle (I think), and there is a clear overflow vulnerability here, where the first 14 bytes of the correct_hash
array can be overwritten, by inputting a password longer than 15 characters when prompted. However, I don't understand how to leverage this to allow the memcmp
check to pass, completing the challenge. Some of the things I have discovered/attempted:
Setting password
to be the equivalent of correct_hash
doesn't work, as password
gets hashed using MD5()
(setting the two to be equal is impossible anyway, as scanf
will insert precisely one unique ASCII NUL
character into the 30 spaces available to it, meaning the two arrays can never be equivalent. NUL
characters additionally (to my knowledge) cannot be inserted in the middle of a scanf
string).
Overwriting the maximum number of bytes with scanf
(which will always append a NUL
character) means the last 3 bytes of correct_hash
will always be 0x00 0x91 0xeb
. Attempting to randomly generate a 16-character password that then hashes to something with these last 3 bytes/characters (reasonably computationally easy, given the use of MD5) doesn't work, however, due to the use of strlen(password)
(which will give a value of 29 instead of something convenient like 16 thanks to only finishing the length count upon hitting a NUL
character) in the call to MD5()
. This means that instead of hashing the 16-character password to produce the expected output, the call to MD5()
will hash 16 characters from password
followed by 13 characters from correct_hash
, producing a different final hashed value.
0x00 0x91 0xeb
. I'm not sure how viable finding this through random MD5 hash computation is, but it don't fancy my chances. Some notes (mentioned in the explanations above):
scanf
is limited to a 29 character string, but will append an ASCII NUL
character, allowing 30 characters total (16 from the password
array, 14 from the correct_hash
array) to be overwritten.
ASCII NUL
characters cannot be input via scanf
so the strlen(password)
in the call to MD5()
(if the maximum password length is used) will be 29.
So the question here is, how else could I go about doing this? Am I missing something extremely obvious? Is random generation a viable solution, or even the only solution?
The solution has to use a buffer overflow (otherwise I imagine I could do something like preload a memcmp
that always returns 0), and has to be executable as a shell script (if that's of any relevance).
Just to merge the comments into an answer here:
The crux of the matter is, that scanf
will happily accept a zero-byte as part of a string and will not treat it as whitespace (thus, will not stop reading further bytes into the string).
Redirect a file or use echo -ne "\x00" | program
or the like. Redirecting an input file is probably the preferred way here.
The final command I used was: echo -ne "\x49\x5a\x4e\x52\x48\x49\x41\x56\x5a\x43\x54\x52\x51\x4c\x43\x00\x81\xae\xf3\xdf\xa2\x45\xb1\x57\x19\xb3\xa9\xb8\x7d\x00\x91\xeb" | ./vulnerable
where the first set of 16 bytes (up to and including the first \x00
) were a randomly generated string, which, when hashed produced the second set of 16 bytes, with the required \x00\x91\xeb
ending. The last 3 bytes there weren't copied anyway, but I left them in to show the string and hash.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With