We have started looking at common classes of vulnerabilities
Today, we begin looking at memory corruption vulnerabilities, focusing on stack-based overflows
Program writes data to a buffer and overruns the buffer's boundary overwriting adjacent memory
Mostly relevant in C and C++ programs
Memory-safe languages are not affected
Buffer overflows are associated with a number of insecure library functions that make it easy to overwrite buffers
String manipulation functions without boundary checking
gets
strcat
strcpy
String manipulation function with boundary checking that are used incorrectly
fgets
Each running program is allocated a number of distinct memory regions
Usually grows towards smaller memory addresses
Register points to top of stack
sp
Composed of frames
#include <stdio.h>
int foo(int a, int b) {
int c = 42;
return a * b + c;
}
int main(int argc, char **argv) {
int c = 0;
c = foo(1, 2);
printf("%d\n", c);
return 0;
}
(gdb) disass main
Dump of assembler code for function main:
0x080483fd <+0>: push %ebp
0x080483fe <+1>: mov %esp,%ebp
0x08048400 <+3>: and $0xfffffff0,%esp
0x08048403 <+6>: sub $0x20,%esp
0x08048406 <+9>: movl $0x0,0x1c(%esp)
0x0804840e <+17>: movl $0x2,0x4(%esp)
0x08048416 <+25>: movl $0x1,(%esp)
0x0804841d <+32>: call 0x80483e4 <foo>
0x08048422 <+37>: mov %eax,0x1c(%esp)
0x08048426 <+41>: mov $0x8048520,%eax
0x0804842b <+46>: mov 0x1c(%esp),%edx
0x0804842f <+50>: mov %edx,0x4(%esp)
0x08048433 <+54>: mov %eax,(%esp)
0x08048436 <+57>: call 0x8048300 <printf@plt>
0x0804843b <+62>: mov $0x0,%eax
0x08048440 <+67>: leave
0x08048441 <+68>: ret
End of assembler dump.
(gdb)
(gdb) disass foo
Dump of assembler code for function foo:
0x080483e4 <+0>: push %ebp
0x080483e5 <+1>: mov %esp,%ebp
0x080483e7 <+3>: sub $0x10,%esp
0x080483ea <+6>: movl $0x2a,-0x4(%ebp)
0x080483f1 <+13>: mov 0x8(%ebp),%eax
0x080483f4 <+16>: imul 0xc(%ebp),%eax
0x080483f8 <+20>: add -0x4(%ebp),%eax
0x080483fb <+23>: leave
0x080483fc <+24>: ret
End of assembler dump.
(gdb) br foo
Breakpoint 1 at 0x80483ea: file procedure.c, line 4.
(gdb) r
Starting program: procedure foo
Breakpoint 1, foo (a=1, b=2) at procedure.c:4
4 int c = 42;
(gdb) stepi
6 return a * b + c;
(gdb) x/8wx $ebp -4
0xbffff174: 0x0000002a 0xbffff1a8 0x08048422 0x00000001
0xbffff184: 0x00000002 0x002d8ff4 0x00166225 0x0011f270
(gdb) x/i $eip
=> 0x80483fb <foo+23>: leave
(gdb) info r ebp esp
ebp 0xbffff178 0xbffff178
esp 0xbffff168 0xbffff168
(gdb) si
(gdb) info r ebp esp
ebp 0xbffff1a8 0xbffff1a8
esp 0xbffff17c 0xbffff17c
(gdb) x/i $eip
=> 0x80483fc <foo+24>: ret
(gdb) si
0x08048422 in main (argc=2, argv=0xbffff244) at procedure.c:12
(gdb) info r ebp esp eip
ebp 0xbffff1a8 0xbffff1a8
esp 0xbffff180 0xbffff180
eip 0x8048422 0x8048422 <main+37>
Intel 64 and IA-32 Architectures Software Developer's Manual
#include <stdio.h>
#include <string.h>
void vulnerable(char* param)
{
char buffer[100];
strcpy(buffer, param);
}
int main(int argc, char** argv)
{
vulnerable(argv[1]);
printf("OK\n");
return 0;
}
06-buffer-overflows$ ./vuln test
OK
marco@ubuntu:$ ./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
$ gdb vuln
(gdb) r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) info r esp ebp eip
esp 0xbffff0f0 0xbffff0f0
ebp 0x41414141 0x41414141
eip 0x41414141 0x41414141
(gdb) x/20wx $esp -32
0xbffff0d0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff0e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff0f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff100: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff110: 0x41414141 0x41414141 0x41414141 0x41414141
Disable defense mechanisms!
gcc -fno-stack-protector \
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
vuln.c -o vuln
Key idea: Overwrite a pointer with the address of code we want to execute
A procedure contains a local buffer variable allocated on the stack
The procedure copies user-controlled data to the buffer without verifying that the data size is smaller than the buffer
The user data overwrites the other variables on the stack, up to the return address saved in the function frame
When the procedure returns, the program fetches the return address from the stack and copies it to the EIP register
Since we can control the return address, we can jump to an address of our choice
Address inside a buffer whose content is under user control
Address of an environment variable
Address of a function inside the program
The buffer that we are overflowing is usually a good place to put the code (shellcode) that we want to execute
The buffer is somewhere on the stack, but in most cases the exact address is unknown
The address must be precise: jumping one byte before or after would just make the application crash
On the local system, it is possible to calculate the address with a debugger, but it is very unlikely to be the same address on a different machine: any change to the environment variables affects the stack position
A sled is a “landing area” in front of the shellcode
Must be created in a way such that whenever the program jumps into it:
The simplest sled is a sequence of NOP instructions
Find a register that points to the buffer (or somewhere into it)
Locate an instruction that jumps/calls using that register
Overwrite the return address with the address of that instruction
We will talk about this later on…
Morris worm (1988): overflow in fingerd
CodeRed (2001): overflow in MS IIS server
SQL Slammer (2003): overflow in MS SQL server
In 2003, around 75% of the vulnerabilities were buffer overflow (CERT)
Aleph One, Smashing The Stack For Fun And Profit, Phrack 49, 1996
spoonm, Recent Shellcode Developments, ReCon 2005
Solution 1: NOP sled
Assembling the malicious input
Assembling the malicious input
Solution 2: Register-based jump
Heap spraying
Some history
References