Estimated difficulty: 💜💜💜💜🤍
Hulk, smash? Stacks, that is!
Welcome to another strictly hax-themed blog, and something a little out of my comfort zone I must say – so if there are any constructive criticisms or feedback…
Please let me know!
This month we are covering the basics of stack smashing AKA stack-based buffer overflows, we’ll run through the basics of “what is a stack?” followed by “what is the smashing of stacks?” and then round off with preventing the hax (and how to mitigate stack-based buffer overflow vulnerabilities)
What is a Stack?
A stack is described as a data structure that stores an an array of data and states (such as flags, registers and procedure arguments). Stacks also help programs keep track of what goes next by using something called “return control” after each subroutine is executed, helping tell the program where to go to keep running.
A stack is also an area of short-term computer memory that stores temporary local variables that are created by functions. They are made up of various elements, such as functions and parameters, and are often wiped after a task is completed.
There are two main basic functions in a stack; push and pop.
“Pushing” adds items to the top of the stack, “popping” removes items from the top of the stack. Stacks are a Last In, First Out (LIFO) data structure – and alas, always work from the top of the stack.
Imagine having a stack of plates, you can only remove plates from the top of the stack and you can only add plates to the top of the stack. If you want to access a plate at the bottom of the stack, you have to remove all the plates on top first! This is exactly how a stack works, and to help manage this – stack pointers (ESP) are used to identify the top of the stack.
<
Now hopefully I haven’t lost you just yet, and now we know the basics of what a stack is – let’s talk about smashing stacks!
What is “Stack Smashing”?
Stack smashing is simply haxx0r lingo for the vulnerability stack-based buffer overflow. This particular vulnerability involves flooding the memory address space (the buffer) with more than it’s allocated.
This usually leads to the program panicking and crashing (same program, same) and may cause something called a segmentation fault. Segmentation faults are described as “a failure condition, raised by hardware with memory protection, notifying an operating system the software has attempted to access a restricted area of memory”.
Once a memory has been “overflowed” an attacker can use this vulnerability to execute additional code to modify the memory and control the program flow, maliciously providing input beyond the allocated memory of the stack.
Let’s take a look at a simple example (adapted from thegeekstuff)…
We have the following C program, a simple program that asks you for a password – if you provide the correct password, prompts you with a “Congrats!” otherwise you are prompted with a “Wrong Password”
Let’s take a closer look at the program…
#include <stdio.h> #include <string.h> int main(void) { char buff[20]; int pass = 0; printf("\n Enter the password : \n"); gets(buff); if(strcmp(buff, "supersecurepassword")) { printf ("\n Wrong Password \n"); } else { printf ("\n Correct Password \n"); pass = 1; } if(pass) { printf ("\n Congrats! \n"); } return 0; }
What we are most interested in is the line char buff [15]
This specifies the character buffer space of 15 bytes – let’s recap our stack diagram:
If we provide an input larger than 15 bytes, this is more than what the buffer can hold and we start to overwrite other areas of the stack…
We can then provide enough input to overwrite the memory of the specified integer for pass (which is 0)…
char buff[15]; int pass = 0;
Anything that is not a 0 is read as true by the program, and therefore by overwriting the memory of the integer – our input (the value of “pass”) became a non-zero… and we still get the congratulations message!
And there we have it, an absolute basic example of smashing stacks!
Once you “smash a stack” there are loads of things you can do to your advantage. Some attackers may just want to try to change the behaviour of the program, but more often than not attackers try to do some form of remote code execution (RCE) using their own malicious shellcode.
How do we stop the hax?
There are a few things we can do to prevent stack-based buffer overflows, the most common (general) mitigation techniques can be found below:
- Not using “risky” functions in code
- For example, the gets() function in C does not do array checking and is therefore vulnerable to the attack
- Address space randomisation (also known as “ASLR”)
- Using ASLR randomly moves around address spaces that makes it more difficult to identify the different areas of the stack
- Implementing executable space protection (also known as Data Execution Prevention “DEP”)
- Some segments of the stack will be marked as “non-executable” which means malicious actors will not be able to inject their own input…
And that’s a wrap! This has been an absolute bare bones introduction to smashing stacks, as this topic can get quite complex, quite quickly… but hopefully this post has been of use to understand what your haxx0r friends are on about when they’ve just managed to “smash a stack” 😉
If DEP and ASLR are deployed for the demo program, how would you bypass them? 😛