codezen.fr code hacking, zen coding

26Feb/12Off

CodeGate 2012 – VULN 300

Posted by aXs

$ ssh codeXing@1.234.41.2
Password:
...
FreeBSD 8.2-RELEASE (GENERIC) #0: Fri Feb 18 02:24:46 UTC 2011

$ ls -l
-rwsr-xr-x 1 codeXing2 codeXing2 5916 Feb 24 08:49 X
-r-------- 1 codeXing2 codeXing2 26 Feb 24 08:56 password

Looks like a classic binary exploitation challenge right ?

$ echo "ABCDEFGHIJKLOMNOPQRSTUVWXYZ" > test
$ ./X test
Segmentation fault

Except it segfaults almost immediately for a change. The binary is small and doesn't do much, it will open a file, read 12 bytes from it and then mangles a pointer from the address of an unused function with various or, and, xor and finally call this pointer.

First, the start of the stack is overwritten:

.text:080485A4 mov dword ptr [esp+8], 12h ; n
.text:080485AC mov dword ptr [esp+4], 90h ; c
.text:080485B4 lea eax, [ebp+s]
.text:080485B7 mov [esp], eax ; s
.text:080485BA call _memset

Then we have the pointer operations:

.text:0804862E movzx eax, byte ptr [ebp+var_42+1]
.text:08048632 mov byte ptr [ebp+var_36+1], al
.text:08048635 movzx eax, byte ptr [ebp+var_3E+2]
.text:08048639 mov byte ptr [ebp+var_36], al
.text:0804863C mov ds:funcc, offset func
.text:08048646 mov eax, ds:funcc
.text:0804864B mov [ebp+var_18], eax
.text:0804864E movzx eax, byte ptr [ebp+var_42]
.text:08048652 or eax, 1
.text:08048655 movsx eax, al
.text:08048658 xor al, 0E0h
.text:0804865A shl eax, 18h
.text:0804865D mov [ebp+var_20], eax
.text:08048660 movzx eax, byte ptr [ebp+s+1]
.text:08048664 or eax, 1
.text:08048667 movsx eax, al
.text:0804866A xor al, 0E0h
.text:0804866C shl eax, 10h
.text:0804866F mov [ebp+var_1C], eax
.text:08048672 mov dword ptr [esp+8], 12h ; n
.text:0804867A lea eax, [ebp+s]
.text:0804867D mov [esp+4], eax ; src
.text:08048681 mov dword ptr [esp], offset test ; dest
.text:08048688 call _strncpy
.text:0804868D mov eax, [ebp+var_20]
.text:08048690 mov [ebp+var_30], eax
.text:08048693 mov eax, ds:funcc
.text:08048698 mov [ebp+var_18], eax
.text:0804869B mov eax, [ebp+var_18]
.text:0804869E mov [ebp+var_2C], eax
.text:080486A1 and [ebp+var_2C], 0FFFFh
.text:080486A8 mov eax, [ebp+var_2C]
.text:080486AB or eax, [ebp+var_20]
.text:080486AE mov [ebp+var_28], eax
.text:080486B1 mov eax, [ebp+var_28]
.text:080486B4 or eax, [ebp+var_1C]
.text:080486B7 mov [ebp+var_24], eax
.text:080486BA mov eax, [ebp+var_24]
.text:080486BD mov [ebp+var_18], eax
.text:080486C0 mov eax, [ebp+var_18]
.text:080486C3 mov ds:funcc, eax
.text:080486C8 mov eax, ds:funcc
.text:080486CD call eax ; funcc

What we want, of course, is get total control of eax so we can call our shellcode.

We have very little space since only 12 bytes will be read from the file. So our strategy will be as follow:

- control eax using the file
- put our shell code in environment (eggshell)

After spending some time setting up a local FreeBSD VM because the challenge server was flacky, our team mate bpint3 found that we could control the low part of eax using positions 6 and 11 in the file.

That was a good start now we need to control the upper part and put it in the range of the environment. Luckily there was no ASLR and this memory space was always at the same place following consecutive executions.

I couldn't really make any sense of the pointer mangling code in a predictable way and time was running out, so we went for a bruteforce.

First we copied and modified the binary to remove the "call eax" and replaced it with 2 syscalls:

- one call to the write() syscall to print the value of eax
- one call to exit() to avoid a segfault and