Sign in to follow this  

Buffer Overflows and Stack Direction

This topic is 4094 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

From my understanding, the main way that buffer overflows can lead to code execution is by overwriting the return pointer that's on the stack:
before:
[local variables (buffers go here)][return address][pushed arguments] (previous function's stuff here)

after:
[buffer OVERFLOWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW][pushed arguments](previous function's stuff here)
This is all because "push" moves the stack pointer in the negative direction. What if push moved in the positive direction? buffer overflows would continue forward in the "unused" area and would hit the guard page.
before:
(previous function's stuff here)[pushed arguments][return address][local variables (including buffers)]

after:
(previous function's stuff here)[pushed arguments][return address][local variaBUFFER OVERFLOWWWWWWWWWWWWWWWWWW]
Unfortunately the way the "push" "pop" and "ret" instructions work on x86 machines won't let you do this. There's nothing stopping you from doing everything without using those particular instructions though... Is anyone else familiar with other types of problematic overflows? Does anyone know if there's any advantage to stack 'growing negative' like this (other than by silly convention)? Back in the day, the stack would grow in the opposite direction of the heap so that they could share one large region of memory. In modern applications, the stack is usually COMPLETELY isolated with nothing else around.

Share this post


Link to post
Share on other sites
Some architechures/OSes do do this. In fact I had never considered that a stack might grow in the negative direction until I began programming for PCs.

I certainly agree that it was a stupid idea to grow in the negative direction. However, if the operating-system/programming-language did array indexing the other way around to match, then there wouldn't be a problem... i.e
val = array[idx];
<->
val = *(array - idx);

Share this post


Link to post
Share on other sites
That wouldn't solve the problems with local variables being overwritten (where those local variables might contain flags to say whether an action is permitted, or function pointers, or objects with vtables, etc) - so compilers like VS2003/2005 (with /GS on) have to be careful in any case to arrange the stack so buffers can't overwrite local variables.

As on the diagram on that page, there's stuff on both sides of buffers during a function call - so changing the stack direction would add the problem that buffer overflows could overwrite any saved registers and temporary values which are stored on the stack (because those have to be pushed after the stack space is allocated for buffers). That could perhaps include the frame pointer or some similar critical value, so you could control where the function will return to and so you can control the return address (even if you can't modify the actual return address itself). You can add the security cookie just after the buffers (instead of before) - but then the compiler is doing the same amount of work as in the normal case, without a substantial security benefit, and with the problem of breaking compatibility with the processor architecture which has demonstrated that backward compatibility sells more than any other feature [smile]

Share this post


Link to post
Share on other sites
To be completely anal, RET can't be entirely emulated in x86, as it is impossible to pop a value into EIP. You could always

MOV EDX, [ESP]
ADD ESP, 4
JMP EDX

but that involves the use of a general-purpose register (in this case EDX). If you needed to preserve all the registers' contents, which is uncommon but feasible, you'd come unstuck. Anyway...

What you've described is a stack-overflow exploit, which was previously the hack-of-choice on Windows XP. Such exploits are much harder to pull off since SP2 though as the stack can no longer be executed under any circumstances. So you really have to get creative when coming up with some shellcode to 'execute arbitrary code', as the phrase goes.

There are many other types of exploit though. Heap overflows are similar to stack overflows, and have similar potential. Format-string attacks are also common, and have rendered millions of C programmers very dangerous, as they all seem to love printf() and friends. The web is rich with discussion on common exploits, so check it out some time.

I think the main reason the stack grows downwards is compatibility. Many programmers and compilers just got comfortable with the fact that local variables live at [EBP+], arguments at [EBP-] and that you could 'get away with' hacking the stack by manually pulling pieces of data from where they 'should' be. If, one day, the return value no longer lived at [EBP-4] then all hell would break loose.
It also makes things that little bit easier on the compiler, knowing that a page-fault at 0x12D000 is necessarily a stack-overflow. With compilers choosing different stack sizes for different applications, the guard-page would need to be put at a different place in lomem for each process.
Besides, when you list memory increasing downwards (as we all do) it's pleasing to see the stack growing 'up the page' [smile].

Regards
Admiral

Share this post


Link to post
Share on other sites

This topic is 4094 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this