For Project 1, tweetchainz, we are given a binary that allows us to create a username and password, send and print tweets, and login as an admin user among other functions.
After every action taken, the main menu is printed which looks like this.
Running checksec on the program reveals that it is only compiled with partial RELRO, leaving the GOT vulnerable to overwrite attacks.
The format string vulnerability can be found in the print_menu function.
We can see that if a non-admin user calls this function, the right branch will be taken, which uses a fputs() call to print a user provided tweet to stdout.
However, if a user authenticates himself as an admin user, the left branch will be taken, which uses a printf() call to print the same tweet out.
The issue is that printf() always expects its first argument to be a format string.
fputs() on the other hand, parses its first argument as a plain text string and does not interpret it as a format string, so it is not susceptible to the same vulnerability.
Generally, instead of calling printf(string);, printf("%s", string); or fputs(string); should be used.
So now that we have found the vulnerability, we need to see how we can authenticate ourselves as an admin user in order to trigger it.
When the binary is first ran, the user is asked to provide a username and a salt, which, along with a randomly generated admin password, secretpass are used to calculate a regular user password.
If we take a closer look at the hash() function responsible for generating the regular user password, we can see something interesting.
The user password is generated byte-by-byte using the following algorithm.
So, if we are able to set the salt and username values to NULL or 0x0, our generated password will be the same as the admin password.
Note: Due to the way the way the username and salt are stored in the program, I was unable to set the username to 0x0 in my final script, so I had to xor the last 4-byte chunk of the user password with 0xa0000 but the idea is still the same.
System Leak + Exploit
Since partial RELRO is enabled, we want to leverage the format string vulnerability in order to overwrite an entry in the GOT.
I chose to overwrite the memcmp() entry because it is called in the maybe_admin() function and we can control the data in the first argument.
We will want to overwrite the pointer in memcmp@GOT with the address of system().
Then, we will login as an admin user again, but enter "/bin/sh" as the password, which will then by passed in as the first argument to system(), thereby affording us a shell.
When I first did this exercise, I hardcoded in the address of system() in my exploit. However, to get practice, I wrote another exploit that leaked the address of libc, by using the format string vulnerability to leak a pointer into libc that was stored on the stack, and subtracting from it, its known offset to calculate the base address of libc.
I then added the known offset of system() to that value to get the absolute value of system().
When using the format strings to overwrite the entry in memcpy@GOT, one issue that stumped me for a while was that I kept using width values that were too large, which gave me stdout and buffering issues. To overcome this problem, I had to write 1 byte at a time so that not too many bytes were being printed out.
Putting everything together, here are my solutions to this exercise.