Discover more from Bitesized Engineering
Chronicles of C++ Memory Management - Part 5
What a better way to do closure on the whole “how Stack works” story then with some common ways used to exploit them. As usual, first the infographic and then the more detailed text.
(click on image to enlarge)
Even though OS’ and Programming Languages have come a long way in order to prevent these kinds of attacks, you still do hear about them and they still happen. The difference is that they became more sophisticated and more complex then before, but deep-down, they rely on same techniques. The (in)famous “Stack Buffer Overflow” or “Stack Buffer Overrun”.
The thing is that Stack’s biggest advantage (i.e. how data is stored - stacked on top of eaech other) is it’s biggest weakness as well. Just think about it really - from POV of memory, it’s just linear sequence of bits. There’s no concept of “these bits belong to X”. It’s just range of bits, that logically get interpreted by OS and programming language. Here’s where the problem lies - if you stack multiple objects on top of each other, and you allow user to provide some (unchecked) input - they might intentionally write MORE than they are supposed to, and those additional bits will spill over to next address, which might have been reserved for something else.
I’ll give you a practical example. Think of a whiteboard. You can only write so much on your whiteboard. Whiteboard would be the Stack size and anything you write outside and on the walls will be interpreted as malicious attempt and will get you thrown out of your class.
Now imagine that I draw vertical lines and split the Whiteboard into multiple areas. What really prevents you to overwrite multiple of those is my hope that you won’t do it; or that there will be somebody penalising you if you do so. Well, the thing is that C and C++, if left unchecked, won’t penalise you for it. And this is called overflow.
Stack really stores quite some stuff, and some of those stuff are pointers to where you came from, and other variables that might get executed by CPU. So if you were able to overwrite those bits (i.e. by spilling your data over), you might force CPU into executing some malicious stuff (e.g. creating reverse shell, or installing some malicious backdoor in your system). Or you might want to interrupt the service by sending more data than Stack can store, effectively forcing OS to kill that process. Possibilities are endless, really :)
This was way bigger problem back in the day, than it is today. And that’s primarily because many techniques were developed to battle that.
One simple way is to, instead of providing linear address space, you actually randomize it a bit, hoping that attacker might not figure out the randomization scheme and, as such, memory layout. This is what Address Space Layour Randomization (ASLR) does.
Another one is more hardcore - simply mark segments of memory that are NOT allowed to contain executable bits. That’s what Data Execution Prevention (DEP) does. And there’s even a more hardened, hardware version of it.
Finally, there’s a simple technique used since the dawn of time - use a spy. Or a snitch. That’s what Stack Canaries are for. You add a predefined value on top of Stack and then you check if it was overwritten. If it was - you are dealing with malicious attempt.
And that’d be about it for today :) If you want to explore more, I came around an interesting website called Binary Exploitation Notes where you can read more about various interesting techniques.
Next week I’m going to put focus back on Containers, but after that I’m jumping into Heaps, Allocations, and everything else that goes with it. Until then, if you haven’t subscribed already, you might want to do so now:
Thanks for reading Bitesized Engineering! Subscribe for free to receive new posts and support my work.
If you missed some of the previous articles, here are the last three: