On November 2nd, 1988 the famous Internet Worm “Morris Worm” brought down 60,000 machines. Robert Morris a graduate student in Computer Science at Cornell, wrote an experimental, self-replicating, self-propagating program called a worm and injected it into the Internet. The aim of the worm was to propagate slowly and measure the size of internet.There was a bug, which cause worm to be replicating and reinfecting machines at a much faster rate.
Worm exploited several holes in the UNIX operating system. One particularly gaping hole in the gets() library call, allowed the virus to create a root shell on a remote machine! Let’s dig into that hole more closely.
How many times have you seen this error and ignored ???
warning:the `gets' function is dangerous and should not be used.Now question is what was the exploit ? Well, answer is stack buffer overflow. What is stack buffer overflow ? The stack buffer overflow is to overwrite parts of memory which aren't supposed to be overwritten by arbitrary input and making the process execute this code. To see how and where an overflow takes place, lets take a look at image how memory is organized.
- code segment – data in this segment are assembler instructions that the processor executes.
- data segment – space for variables and dynamic buffers.
- stack segment – memory area where automatic variables are stored. A LIFO structure with pop and push operations grows from 0xBFFFFFFF down.
before diving into the code lets have a look at registers.
- esp - the stack pointer (esp) points to the top of stack, (TOS)
- ebp - the base pointer (ebp) points to the top of the current stack frame.
- eip – the instruction pointer (eip) points to the next machine instruction.
Now we are all set for the stack buffer overflow experiment .
Lets write a small function which allows buffer overflow exploit.
void test_func ( ) {
char buf[30];
gets (buf);
printf("%s\n", buf);
}
main() {
test_func ();
return 0;
}
To understand what the program does to call function() we compile it
with gcc and diassemble it with gdb.
raghav@raghav-VirtualBox ~/Desktop$ gcc -ggdb test.c -o test
raghav@raghav-VirtualBox ~/Desktop $ gdb test
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type “show copying”
and “show warranty” for details.
This GDB was configured as “i686-linux-gnu”.
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>…
Reading symbols from /home/raghav/Desktop/test…done.
(gdb)disas main
Dump of assembler code for function main:
0x80484c8 : pushl %ebp
0x80484c9 <main+1>: movl %esp,%ebp
0x80484cb <main+3>: call 0x80484a0
0x80484d0 <main+8>: leave
0x80484d1 <main+9>: ret
(gdb) disas test_func
Dump of assembler code for function test_func:
/* saving the frame pointer onto the stack right before the ret address */
0x80484a0 : pushl %ebp
0x80484a1 <test_func+1>: movl %esp,%ebp
/* enlarge the stack by 0x20 or 32. our buffer is 30 characters, but the
memory is allocated 4byte-wise (because the processor uses 32bit words)
this is the equivalent to: char buf[30]; */
0x80484a3 <test_func+3>: subl $0x20,%esp
/* load a pointer to small[30] (the space on the stack, which is located
at virtual address 0xffffffe0(%ebp)) on the stack, and call
the gets function: gets(small); */
0x80484a6 <test_func+6>: leal 0xffffffe0(%ebp),%eax
/* load the address of small and the address of "%s\n" string on stack
and call the print function: printf("%s\n", buf); */
0x80484b2 <test_func+18>: leal 0xffffffe0(%ebp),%eax
0x80484b5 <test_func+21>: pushl %eax
/* get the return address, 0x80484d0, from stack and return to that address.
you don't see that explicitly here because it is done by the CPU as 'ret' */
0x80484c3 <test_func+35>: leave
0x80484c4 <test_func+36>: ret
End of assembler dump.
Overflowing the program
# ./test
abcdefghijkilmnopqrstuvwxyzajh <- user input
abcdefghijkilmnopqrstuvwxyzajh
# ./test
abcdefghjikljhshajalkdhdajkhdjhasgdqwqwqwdagdjasydragddaskdas
abcdefghjikljhshajalkdhdajkhdjhasgdqwqwqwdagdjasydragddaskdas
*** stack smashing detected ***: /home/raghav/Desktop/test terminated
# gdb test core (gdb) info registers eax: 0x24 36 ecx: 0x804852f 134513967 edx: 0x1 1 ebx: 0x11a3c8 1156040 esp: 0xbffffdb8 -1073742408 ebp: 0x787878 7895160
EBP is 0×787878, this means that we have written more data on the stack than the input buffer could handle. 0×78 is the hex representation of ‘x’. The process had a buffer of 32 bytes maximum size. We have written more data into memory than allocated for user input and therefore overwritten EBP and the return address with ‘xxxx’, and the process tried to resume execution at address 0×787878, which caused it to get a segmentation fault.
Changing the return address
Lets try to exploit the program to return to test_func() instead of return. We have to change return address 0x80484d0 to 0x80484cb, that is all. In memory, we have: 32 bytes buffer space | 4 bytes saved EBP | 4 bytes RET. Here is a simple program to put the 4byte return address into a 1byte character buffer:
main()
{
int i=0; char buf[44];
for (i=0;i<=40;i+=4)
*(long *) &buf[i] = 0x80484cb;
puts(buf);
}
# ret
ËËËËËËËËËËË,
# (ret;cat)|./test
test <- user input
ËËËËËËËËËËË,test
test <- user input
test
Here we are, the program went through the function two times. If an overflow is present, the return address of functions can be changed to alter the programs execution thread.
Tagged: buffer overflow, computer, gets, instruction pointer, morris worm, security, stack buffer, stack pointer, stack segment memory, stack smashing, technology

Nice post
Thanks