Allegro.cc - Online Community

Allegro.cc Forums » Off-Topic Ordeals » Stack Smasher

This thread is locked; no one can reply to it. rss feed Print
Stack Smasher
Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I've been a little bored waiting for my laptop power adapter to arrive so I can use my laptop again, and so I've been doing a little experimenting.

I managed to execute a function I never called. It's the little victories that make me happy. ;)

Anyway, here's some (/dangerous/) source code. Run at your own risk. It prints an error message inside the function that was never called. Obviously, I managed to overwrite the return address of the stack frame with the address of my function, so when it returns, arbitrary code is executed.

This is really nothing special, seeing as how it's my own code and I can make it do anything I want. I won't go into attack vectors, as that's not the point.

Initial attempts to 'fix' what I broke...
{"name":"611989","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/1\/9\/197c512c6993d729cf3925d2303cd03c.png","w":1356,"h":1057,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/1\/9\/197c512c6993d729cf3925d2303cd03c"}611989

#SelectExpand
1 2 3 4 5#warning RUN @ YOUR OWN RISK NEWBIE 6 7#include <stdio.h> 8#include <stdlib.h> 9 10 11 12typedef void (*VOIDFUNC)(); 13 14typedef VOIDFUNC* VOIDFUNCPTR; 15 16static VOIDFUNC old_data = (VOIDFUNC)0; 17static VOIDFUNCPTR return_address = (VOIDFUNCPTR)0; 18static VOIDFUNCPTR stack_address = (VOIDFUNCPTR)0; 19 20 21 22void put_it_back() { 23 if (return_address) { 24 /// Put back the previous value 25 fprintf(stderr , "Returning data {%p} to %p\n" , old_data , return_address); 26 *return_address = old_data; 27 } 28} 29 30 31 32void i_never_called_this_code() { 33 fprintf(stderr , "\n\nArbitrary code execution... oops!\n\n"); 34 /// Attempt to repair the damage, so it goes back to the 'CORRECT' frame 35 put_it_back(); 36 stack_address = return_address; 37 fprintf(stderr , "***Stack Address is %p ***\n" , stack_address); 38 39 /// We better abort before shit gets crazy 40// abort();/// haha just kidding 41 /// No really 42// abort();/// you really want to know what happens, don't you? 43 /// Ahem 44 abort(); 45} 46 47 48 49void break_the_stack(char* stackptr) { 50 51 /// Store the old data (ie, return address we're SUPPOSED to be going to 52 old_data = *(VOIDFUNCPTR)(stackptr); 53 return_address = (VOIDFUNCPTR)stackptr; 54 55 fprintf(stderr , "Reading data {%p} at %p\n" , old_data , return_address); 56 57 /// Smash the stackptr, 58 /// When we find the return address stored on the stack, we replace it with our function address 59 /// that's where it will go next 60 /// Basicallly, we're calling our code without calling it 61 /// Once that's done, 411 UR B4535 B310NG 2 US 62 *(VOIDFUNCPTR)(stackptr) = i_never_called_this_code; 63} 64 65 66 67int main(int argc , char** argv) { 68 69 char* stack = (char*)&stack; 70 71 (void)argc; 72 (void)argv; 73 74 while (1) { 75 stack -= sizeof(unsigned int); 76 fprintf(stdout , "0x%lx\n" , (unsigned long int)stack); 77 break_the_stack(stack); 78 put_it_back(); 79 if (stack_address) {/// == (VOIDFUNCPTR)stack) { 80 break; 81 } 82 else { 83 /// Cleanliness is next to godliness 84/// *return_address = old_data; 85 } 86 } 87 88 fprintf(stderr , "What just happened?\n"); 89 90 return 0; 91}

So you can see, it gets the address of an object on the stack, and then decrements the address, overwriting memory until the return address is overwritten with the address of the function I want to call.

NOTE : This will destroy your stack, and may? destroy your computer. Maybe you should run it in a VM, through something safe like GDB.

I can only test on Windows XP at the moment. This is with MinGW GCC 8.1.

So far I've managed to get it to run arbitrary code, but I can't seem to put humpty dumpty back together right yet.

{"name":"611990","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/7\/6793e6aa09dd2ff2b5e982912205ada2.png","w":1356,"h":1057,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/7\/6793e6aa09dd2ff2b5e982912205ada2"}611990

For now, it halts with an abort(); so it doesn't turn into Skynet.

Peter Hull
Member #1,136
March 2001

Looks cool!

Does anyone remember Progranisms? Now that was dangerous!

Chris Katko
Member #1,881
January 2002
avatar

Progranisms

Oh sweet youth

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

The first screenshot reminds me of those old cartoons (edit - Family Circus) where they run around the yard and it leaves a trail :

611991

Those addresses are totally random. It's just playing hopscotch through memory. :o

Can anyone give me a guide to the addresses on the stack?

if you look at the second screenshot you can see some addresses near 0 x 0 0 2 3 f f 0 0 through 0 x 0 0 2 4 0 0 0 0. That's 256 Bytes of memory. That's the address where the stack variables live. The addresses near 0x0401.... are the addresses of functions like main and break the stack. Static variables are near there too. In between the stack variables are the frame pointers.

I can't quite 'fix' the damage I caused though... when I put back the address of where it's supposed to return to it is actually trying to 'run' the stack memory. :-X

relpatseht
Member #5,034
September 2004
avatar

I'm posting now to help me remember tomorrow. I used to be heavily invested in this sort of thing. I think I may have posted my detouring library on here at some point (takes function address, decompiles function header, relocates/recompiles it somewhere else, inserts jump to your function where header was; so all function calls get redirected then return to the original function). Anyway, I want to look at what you did when I'm awake. Too exhausted to look now.

[[EDIT]]
Okay, back awake.

So, what it comes down to, is the call and ret instructions in ASM. The call instruction is functionally equivalent to

// RIP is the 64 bit pointer to the currently executing instruction. 
// Y is the size of a push instruction plus the size of the jmp function. 
// I say Y because in x86/64 land, these instructions come out to 
// different machine code width different numbers of bytes depending on
// the arguments. For instance, a jmp instruction can be anywhere between 2
// and 14 bytes long
push rip+Y // Push the return address (directly after the jmp) onto the stack
jmp SomeFunction // Move the instruction pointer to the function we want to call

Meanwhile, the ret instruction is functionally equivalent to

// Pull the top value off the stack and put it in the instruction
// pointer, effectively jumping there and removing it from the
// stack simultaneously
pop rip

So, obviously, as soon as you're in your function, the top stack entry is the return address, right? Wrong.
Just like in C/C++, in ASM there is the concept of stack scope, which is managed by RBP and RSP. RBP points to the current bottom of the stack, and RSP to the current head. This means calling push is functionally equivalent to

sub RSP, sizeof(X) // Move the head of the stack to make room (stack grows down for some reason lost to time)
mov [RSP], X // Store X at the current address of RSP, the stack head

And calling pop is the reverse.
What about RBP then? I said there was scope, and RBP is where it happens. The standard entry sequence to a function (the stuff that gets called before your variables are initialized) is

push RBP // Store the old base of the stack on the stack
mov RBP, RSP // Set the new base as the current top of the stack
sub RSP, X // Allocate X off the stack, where X is the sizeof all local variables

This means (because, again, the stack grows down for some reason) inside a function, you can use [RBP - X] to access local variables and [RBP + X + 8] to access the parent scope's variables (+8 to skip the return address). Writing into [RBP + X] would be bad, since that means you're modifying outside your scope. Of course, there's no safety here, so nothing is stopping you.

What it comes down to, is a stack frame for the following would be something like...

#SelectExpand
1void foo() 2{ 3 int x; 4 // stack frame for right here below 5} 6 7void bar() 8{ 9 int a, b; 10 // ... 11 foo(a + b) 12foo_done: 13} 14 15int main() 16{ 17 bar(); 18bar_done: 19}

// Note: This is assuming x64 with a __fastcall calling convention,
// meaning the first two parameters are passed in registers, rather
// than on the stack
// Address Value
   0x100   0x100    // Absolute base of the stack
   0x108   bar_done // Return address after bar is called. The next instruction
   0x110   0x100    // The base of the stack for main
   0x118   a        // bar::a
   0x120   b        // bar::b
   0x128   foo_done // The return address after foo is called.
   0x130   0x110    // The stack base pointer for bar
   0x138   x        // foo::x

That's all generally true anyway. In reality, optimizing compilers will sometimes give you non-standard stack frames, particularly on leaf functions (functions that don't call anything). Furthermore, a nice debugging compiler will probably insert byte patterns in the callstack in and around function calls which they can check for on return to make sure you didn't smash the stack.

If you want to know more, such as how to properly hot patch/detour a function, how to inject code into a foreign process, how to evade process intrusion detection facilities ... ... all for academic purposes only, let me know.

Neil Roy
Member #2,229
April 2002
avatar

If you want to see dangerous, I just posted another thread which has a video which shows that there is a RISC processor hidden on your X86 CPU that can bypass all security. The guy logged onto LINUX and gave himself root access when he was just a normal user using it.

He gives out the source code and programs he wrote so you may be interested in seeing what you can do with it. Mind blowing stuff anyhow. See the thread called "GOD MODE ENABLED..." etc.

---
“I love you too.” - last words of Wanda Roy

Go to: