简体   繁体   中英

Stack-based overflow code from Hacking: The Art of Exploitation

This might be related to this , but i'm not sure if it's in the same boat.

So i've been re-reading Hacking: The Art of Exploitation and I have a question about some of the C code in the book, which doesn't quite make sense to me:

Let's just assume we're back in ~2000 and we don't really have stack cookies and ASLR (maybe we do, but it hasn't been implemented or isn't widespread), or any other type of protection we have now-a-days.

He shows us this piece of code to exploit a simple stack-based overflow:

#include <stdlib.h>

char shellcode[] = "..." // omitted

unsigned long sp(void) 
{ __asm__("movl %esp, %eax); }

int main(int argc, char *argv[]) {
int i, offset;
long esp, ret, *addr_ptr;
char *buffer, *ptr;

offset = 0;
esp = sp();
ret = esp - offset;

// bunch of printfs here...

buffer = malloc(600);

ptr = buffer;
addr_ptr = (long *) ptr;
for(i = 0; i < 600; i+=4)
{ *(addr_ptr++) = ret; }

for(i = 0; i < 200; i++)
{ buffer[i] = '\x90'; }

ptr = buffer + 200;
for(i = 0; i < strlen(shellcode); i++)
{ *(ptr++) = shellcode[i]; }

buffer[600-1] = 0;

execl("./vuln", "vuln", buffer, 0);

free(buffer);

return 0;
}

So what he wants to do is take the address of the ESP and overwrite the saved EIP with that address, so that the processor will jump to his NOP sled in memory and execute the shellcode in the stack.

What I do not understand, is how he can use that particular ESP value that he gets from sp() at the current time he calls it.

From what I understand, the stack looks "something" like this:

...
saved ebp <-- execl
saved eip
"./vuln"
"vuln"
buffer
0
*ptr <-- sp() returns this address?
*buffer
*addr_ptr
ret
esp
offset
i
saved ebp <-- main
saved eip
argc
argv
...

Since he calls (I know it's a function pointer, so I guess not entirely accurate wording?) sp() so early in the exploit, shouldn't it give him a bad ESP address? Even for that matter, I don't see how he can even use that technique here, because he will never get the ESP that points to the top of the buffer inside his vuln program.

Thanks.

I don't see how he can even use that technique here, because he will never get the ESP that points to the top of the buffer inside his vuln program.

I haven't read much of the book, but I think I've figured this out. Here's what *buffer looks like:

NOP sled | shellcode | Address of buffer in the exploit's stack frame

When vuln does strcpy() on buffer into its own stack, it fails to check the bounds and overwrites its own EIP with the address of the start of the buffer in the exploit's stack frame, or at least something close to it (hence the NOP sled). The NOP sled and shellcode copied to vuln 's stack frame are incidental; that's not where they run from. It's critical that the sled and shellcode be smaller than how big vuln expects buffer to be, otherwise the saved EIP will be overwritten by shellcode rather than the address of buffer .

Then, when whatever portion of vuln that uses strcpy() on buffer returns, it goes to the NOP sled and executes the shellcode.

The important point is that buffer is read twice , in two different places.

Edit: Disregard that, I confused myself (though thanks for the accept!). Hopefully I will not confuse you too, which is why I'm writing this edit. The vulnerable program is in a completely different virtual memory space since it is run by the operating system in a separate process (or the same process with a new image? Whatever). So there'd be no way for vuln to access the exploit's stack or heap.

The ESP trickery must be some way to guess where the NOP sled in the copied buffer ends up in vuln 's stack. I personally would expect a much larger offset than 0 since the exploit's stack is quite small compared to vuln 's.

That said, I'm pretty sure there are still two copies of the shellcode in vuln (otherwise, what could it strcpy() from?). With an offset of 0, perhaps he is running the shellcode stored in argv[] ...?!? In that case, you'd still have a situation where the address in one buffer points to the NOP sled in another buffer, as in my original answer. I've been wrong before though, so let me know if that makes no sense.

A lot will depend on what specific OS the code was intended to exploit. Without knowing this, any discussion has to be somewhat generic [and a guess on my part].

One possibility is that there was something significant in the "bunch of printfs" you've left out...

If there's really nothing clever happening there, I would guess that the vulnerability it's trying to exploit is within the execl(..) call and/or the OS when effectively passed a long (600 byte) command-line parameter. Somewhere in there [I'm guessing] a subroutine will be setting up the environment for the new process, and along the way will be copying the 600-byte string passed in as a parameter ( buffer ) into what may well be a small(ish) fixed size buffer on the stack of the new process, and [presumably] overwrites the return address of this "setup" function with many copies of the stack pointer from the original call. When the "command-line copying function" returns, it will thus return to the carefully-prepared buffer from the original copy and execute the shellcode.

(If the omitted shellcode contained a zero byte ...\\x00... then this cannot be what's happening, as it would mark the end of the string being copied in setting up the command-line buffer).

:) I had been at this for days trying to find out what was actually going on. In the process i also discovered this post. Now that i have a glimpse of what is going on i thought i should share what i understood so that others like 'me' would also find this useful.

unsigned long getesp()
{
__asm__("movl %esp, %eax");
}

This function is actually used to guess the return address. It returns the ESP value of our shellcode injection program NOT the ESP value of the vulnerable program. But since the stack starts at almost the same address ( for systems without ASLR enabled) and as aleph one mentions in his article "Most programs do not push more than a few hundred or a few thousand bytes into the stack at any one time. Therefore by knowing where the stack starts we can try to guess where the buffer we are trying to overflow will be ", we can get an idea of where our shellcode should be. Let me explain.

Suppose that for our test program, the stack starts at 1000. This address is actually returned by the above code when we execute it. Now consider our vulnerable program and suppose that the buffer we are trying to inject to is at address 970, and the return address is stored at 1040.

[BUFFER 970][RETURN_ADDRESS 1070]

Ok? Now the buffer is filed with NOPS till the first half,then with the shellcode and then with the return address right..

[NOP SLED][SHELL_CODE][RETURN_ADDRESS]

lets fill it like this

NOPS [970-1010] SHELLCODE[ 1010-1050 ] RETURN_ADDRESS[1050-1070]

The value returned by the getesp() gives an idea of where the stack will be. so if we rewrite the return address with 1000 which was returned by getesp(), we can see that the exploit would still work because the address at 1000 is filed with nops. the exection will slide down to the shellcode !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM