Okay we are given the following code:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "callstack.h"
#include "tweetIt.h"
#include "badguy2.c"
static char *correctPassword = "ceriaslyserious";
char *message = NULL;
int validateSanity(char *password) {
for(int i=0;i<strlen(password);i++)
if(!isalpha(password[i]))
return 0;
unsigned int magic = 0x12345678;
return badguy(password);
}
int validate(char *password) {
printf("--Validating something\n", password);
if (strlen(password) > 128) return 0;
char *passwordCopy = malloc(strlen(password) + 1);
strcpy(passwordCopy, password);
return validateSanity(passwordCopy);
}
int check(char *password, char *expectedPassword) {
return (strcmp(password, expectedPassword) == 0);
}
int main() {
char *password = "wrongpassword";
unsigned int magic = 0xABCDE;
char *expectedPassword = correctPassword;
if (!validate(password)) {
printf("--Invalid password!\n");
return 1;
}
if (check(password, expectedPassword)) {
if (message == NULL) {
printf("--No message!\n");
return 1;
} else {
tweetIt(message, strlen(message));
printf("--Message sent.\n");
}
} else {
printf("--Incorrect password!\n");
}
return 0;
}
We are supposed to trick main into sending a tweet using the function badguy. In badguy we have an offset from a previous problem which is the difference between the declaration of password in main and the argument passed to badguy. We have been instructed to use this offset to find the addresses of the correctPassword and password in main and manipulate the value in password to correctPassword so when the password check occurs, it is believed to be legitimate. I am having some trouble figuring out how to use this offset to find the addresses and continuing from there.
First of all, make sure you have good control over your compiler behavior. That is: make sure you know the calling conventions and that they’re being respected (not optimized away or altered in any manner). This usually boils down to turn off optimization settings, at least for testing under more controlled conditions until a robust method is devised. Pay special attention to variables such as
expectedPassword, since it is highly likely they’ll be optimized away (expectedPasswordmight never be created in the stack, being substituted with the equivalent ofcorrectPassword, rendering you with no stack reference to the correct password at all).Secondly, note that
"wrongpassword"is shorter than"ceriaslyserious"; in other words, if I got it straight, attempting to crack into the buffer pointed to bypasswordCopy(whose size is the length of"wrongpassword"plus one) in order to copy"ceriaslyserious"into there could result in a segmentation violation. Nonetheless, it should be relatively simple to track the address ofexpectedPasswordin the call stack, if it exists (see above), specially if you do have already an offset frommain()‘s stack frame.Considering an x86 32-bit target under controlled circumstances,
expectedPasswordwill reside 8 bytes belowpassword(4 forpassword, 4 formagicif it is not optimized away). Having an offset frompasswordto a parameter as you said, it should suffice to subtract the offset from the address of that parameter, and then add 8. The resulting pointer should beexpectedPassword, which then points to the static area containing the password. Again, double check your environment. Check this for an explanation on the stack layout in x64 (the layout in the 32-bit case is similar).Lastly, if
expectedPassworddoes not exist in the call stack, then, sincecorrectPasswordis a global static, it will reside in a data segment, rendering the method useless. To achieve the goal in this situation, you would need to carefully scan the data segment with a more intelligent algorithm. It would probably be easier, though, to simply attempt to find the test forcheck()‘s return value in the program text and replace withnops (after properly manipulating the page permissions to allow writing to the text segment).If you’re having problems, inspecting the resulting assembly code is the way to go. If you’re using GCC,
gcc -Shalts the compilation just before assembling (that is, producing an assembly source code file as output).objdump -dcould also help.gdbcan step between instructions, show the disassembly of a frame and display register contents; check the documentation.These exercises are specially useful to understand how security breaches occur in common programs, and to provide some basic notions on defensive programming.