Created
October 10, 2023 14:27
-
-
Save R077A6r1an/258f99ec53604b4114367ac455e9eed7 to your computer and use it in GitHub Desktop.
This snippet demonstrates, how to hack the return address, where a x86_64 CPU will continue executing after the main() function in this exsample. For understanding this snippet, you should be familiar with basic concepts of x64 assembler and the stack. #hacking #cracking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: | |
gcc -o StackOverflow overflow.c -fno-stack-protector | |
exec: all | |
./StackOverflow | |
clean: | |
rm StackOverflow | |
.PHONY: all | |
.PHONY: exec | |
.PHONY: clean | |
.SILENT: all | |
.SILENT: exec | |
.SILENT: clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
void overflow(char* buf); | |
void exploit(void); | |
int main(void) { // The normal main function | |
char buffer[8] = "Hello W"; // defining just a normal string | |
overflow(buffer); // call the overflow function | |
return 0; // return with exitcode 0 | |
} //XXX: Normaly this call ends the programm, | |
// BUT GOES TO exploit()!!! | |
void overflow(char* buf) { // The normal function definition | |
for(int i = 0; i < 24; i++) { // Print the bytes of the current stack on the console | |
char in = buf[i]; //XXX: Just a few byte extraction things | |
int x = 0; | |
void* ptr = &x; | |
char* c_ptr = (char*)ptr; | |
c_ptr[0] = in; | |
printf("%d\n", x); // Print the byte on the console in ascii | |
} | |
void* b = (void*)buf; // Converting the buffer into the type we need | |
uintptr_t* ptr = (uintptr_t*)b; // This pointer is a 64-bit sized buffer, pointing to @buf | |
/* | |
* XXX: THIS IS THE LINE | |
* | |
* We expect without stack-guard, that the stack is build as following: | |
* | |
* rbp-offset | var on stack | Description | |
* | |
* 0x00 +-------------+ | |
* | glibc | <- The return address after the main() function | |
* -0x08 +-------------+ | |
* | rbp | <- The stack base pointer, pushed 'cause System-V-abi gcc uses | |
* -0x10 +-------------+ | |
* | buffer | <- The @buffer variable in the main() function | |
* -0x18 +-------------+ | |
* | main | <- The return address from the overflow() function to the main function | |
* -0x20 +-------------+ | |
* | rbp | <- The stack base pointer, modified for the main() function | |
* -0x28 +-------------+ | |
* | b | <- The @b variable in the overflow() function | |
* -0x30 +-------------+ | |
* | ptr | <- The @ptr variable in the overflow() function | |
* -0x38 +-------------+ | |
* | |
* Now, the @ptr variable of the overflow function points at the @buffer array in the main() | |
* function, but in the stack allocating 64-bit size. The @buffer array contains 64-bit, | |
* and after skipping 64-bit, that contains the stack base pointer, we get to the return address, | |
* where the cpu will continue executing after the main() function somewhere in the glibc. | |
* | |
* Modifying this 64-bit address, at 16 bytes over the @buffer array on the stack, we can say where | |
* the cpu will continue executing after the main() function. Here we will set the function address | |
* to the exploit() function, where we will print a message, that this hack was successfully executed. | |
*/ | |
ptr[2] = (uintptr_t)exploit; //XXX: Setting the main() function return address | |
printf("-----------------\n"); //XXX: Just printing the stack bytes another time on the console | |
for(int i = 0; i < 24; i++) { | |
char in = buf[i]; | |
int x = 0; | |
int fjg = 200; | |
void* ptr = &x; | |
char* c_ptr = (char*)ptr; | |
c_ptr[0] = in; | |
printf("%d\n", x); | |
} | |
fflush(stdout); | |
return; // And returning to the main() function | |
} | |
void exploit(void) { // The exploit function. Callen only after the hack from above. | |
printf("Get hacked!\n"); //XXX: Success message for this call | |
fflush(stdout); // flushing, for have this message printed on the console in real-time | |
return; // XXX: Now we will create a memory error, 'cause the cpu expects 16 bytes over the current @rsp | |
// register value the return address. But there is no valid address set! | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment