Professor’s course material was out of date. He made an example of a buffer overflow attack several years ago. Then here I am, asked to fix the code to work on a modern machine, 64bit Kali Linux.
There should be no difference with other Linux; you can practice this on any 64bit Linux machine.
Prerequisite
First, you need GCC and GDB; on Kali, GCC is already installed, so you just need to install the GDB.
sudo apt install gdb
Then we go to write the vulnerable code. Let’s create a file called demo.c.
$ vi demo.c
//demo.c
#include<stdio.h>
void getinput()
{
char buffer[8];
gets(buffer);
puts(buffer);
}
int main()
{
getinput();
return 0;
}
This program waits for a string input of 8 characters from the user and then writes it back. However, it has no check for boundaries. So when the user inputs a string with more than 8 characters, something unpredictable will happens.
Now let’s install GDB and compile our demo.c
without a stack protector. Why? Because by default, GCC already protects the program from stack smashing caused by a non-educated developer who doesn’t care about the input boundary.
$ sudo apt install gdb
$ gcc -ggdb -fno-stack-protector -o demo demo.c
Run the program and input more than 8 characters; try 16 characters and hit Enter.
$ ./demo
That should cause the program to be terminated by a segmentation fault.
What happens?
Look at the stack layout below. The buffer starts from the lowest memory, it will be filled from low to high memory. What will happen when the user fills the buffer with more than 8 bytes? The Base Pointer (RBP) and return address (RET) are in danger, and they will be smashed by the user input.
![](https://i0.wp.com/derrylab.com/wp-content/uploads/2022/10/image-3.png?resize=570%2C510&ssl=1)
Closer Look
Let’s take a closer look by using GDB.
$ gdb demo
Create a breakpoint at getinput()
function, and run the program.
(gdb) b getinput
(gdb) run
The program will run and stop at the prologue of getinput() function. Here we can check the return address in current condition.
(gdb) x/8xg $rsp
![](https://i0.wp.com/derrylab.com/wp-content/uploads/2022/10/image-5.png?resize=860%2C230&ssl=1)
That address should be pointing to the line after the getinput()
function is called. Let’s confirm it by using disassembly
. Note that this return address on different machines may be different.
(gdb) disas 0x55555555517f
![](https://i0.wp.com/derrylab.com/wp-content/uploads/2022/10/image-6.png?resize=851%2C234&ssl=1)
getinput()
function is calledAs expected, the return address points to the line right after the getinput()
function is called.
Buffer Overflow!
Use n
to go to the next instruction, the program will wait for input. Let’s input more than 8 characters to trigger a buffer overflow and hit Enter.
(gdb) n
1234567890abcdefghijklmn
At this time, we already overflow the buffer, lets check the stack values.
(gdb) x/8xg $rsp
![](https://i0.wp.com/derrylab.com/wp-content/uploads/2022/10/image-7.png?resize=918%2C446&ssl=1)
The figure above shows that the first 8 bytes of our input are put in the buffer space, but the other 16 smashes through the RBP and return the address. This means we can modify our last 8-byte input to a value that points to another address which concludes that we can jump to any address we want after getinput()
function is called.
![](https://i0.wp.com/derrylab.com/wp-content/uploads/2022/10/image-8.png?resize=835%2C388&ssl=1)
If you continue the program, it will be terminated by segmentation fault because the address 0x6e6d6c6b6a696867
is invalid and does not exist in our program.
We are Free to Travel Anywhere
The return address can be replaced by any values we want. Let’s use it to jump to a hidden function that can never be called. Create a new demo called demo2.c
//demo2.c
#include<stdio.h>
void canneverexecute()
{
printf(”I can never be executed in normal case!\n”);
}
void getinput()
{
char buffer[8];
gets(buffer);
puts(buffer);
}
int main()
{
printf(“Secret function at %p\n”, (void *) canneverexecute);
getinput();
return 0;
}
Compile just like before without a stack protector and run.
$ gcc -ggdb -fno-stack-protector -o demo2 demo2.c
$ ./demo2
Each time you run the program, the address keeps changing, so we will disable the user address randomization to make it easier.
$ sudo bash -c 'echo 0 > /proc/sys/kernel/randomize_va_space’
Run the program again, and it will show that the address of the hidden function does not change anymore. We will use this address as input to replace the return address in the stack so that after the getinput()
function is called, the hidden function will run.
![](https://i0.wp.com/derrylab.com/wp-content/uploads/2022/10/image-9.png?resize=768%2C244&ssl=1)
Congratulations! Now you understand how the buffer overflow works. Even if this technique is classic, there are still many legacy machines out there living with this vulnerability. Even the newest machine can still suffer from this vulnerability. See it in CVE.
![](https://i0.wp.com/derrylab.com/wp-content/uploads/2022/10/image-10.png?resize=1024%2C1014&ssl=1)
I hope this post can help you understand the classic vulnerability. See you in another post, and have a nice day! 🙂
One response to “How to Do Buffer Overflow Attack on 64bit Machine”
Nice article! Thanks for sharing