[Dreamhack] iofile_vtable_check
[Dreamhack] iofile_vtable_check
문제 링크
https://dreamhack.io/wargame/challenges/57
문제 설명
Description
이 문제는 서버에서 작동하고 있는 서비스(iofile_vtable_check)의 바이너리와 소스 코드가 주어집니다.
프로그램의 취약점을 찾고 익스플로잇해 셸을 획득한 후, “flag” 파일을 읽으세요.
“flag” 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{…} 입니다.
Environment
1
2
3
4
5
6
Ubuntu 18.04
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
문제 분석
- “/dev/urandom” 파일을
fopen
함수로 연 결과인fp
포인터에 사용자가0x300
바이트만큼 입력할 수 있다.- 하지만 Ubuntu-18.04 버전이므로
_IO_FILE
구조체의vtable
필드에 대한 검증이 추가되어,vtable
에 대한 조작은 어렵다.
- 하지만 Ubuntu-18.04 버전이므로
- 프로그램 코드를 보면 사용자로부터 입력을 받은 후
fclose(fp);
를 호출하는데,fclose
는 내부적으로_IO_file_finish
함수를 호출한다. _IO_file_jumps
가상 함수 테이블의 바로 뒤 영역(+0xc0
)에는_IO_str_jumps
가상 함수 테이블이 존재하는데, 이는_IO_file_jumps
와 동일한 형태를 가지고 있고, 해당 가상 함수 테이블을 사용하여도 가상 함수 테이블 검증에 걸리지 않는다.- 이때
_IO_file_finish
와 대응되는_IO_str_finish
함수는 다음과 같은 형태를 가진다.1 2 3 4 5 6 7 8
void _IO_str_finish (_IO_FILE *fp, int dummy) { if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF)) (((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base); fp->_IO_buf_base = NULL; _IO_default_finish (fp, 0); }
fp->_IO_buf_base
가 0이 아니고, 플래그의fp->_IO_USER_BUF
비트가 0인 경우fp->_s._free_buffer
를 함수 포인터로 하여 호출한다. 이때 인자는fp->_IO_buf_base
이다.
- 이때
- 파일 구조체를
_IO_strfile
구조체로 변환하여 보면 다음과 같다.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
pwndbg> p/x *(_IO_strfile *)0x00007ffff7dce680 $4 = { _sbf = { _f = { _flags = 0xfbad2086, _IO_read_ptr = 0x0, _IO_read_end = 0x0, _IO_read_base = 0x0, _IO_write_base = 0x0, _IO_write_ptr = 0x0, _IO_write_end = 0x0, _IO_buf_base = 0x0, _IO_buf_end = 0x0, _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7ffff7dce760, _fileno = 0x2, _flags2 = 0x0, _old_offset = 0xffffffffffffffff, _cur_column = 0x0, _vtable_offset = 0x0, _shortbuf = {0x0}, _lock = 0x7ffff7dcf8b0, _offset = 0xffffffffffffffff, _codecvt = 0x0, _wide_data = 0x7ffff7dcd780, _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0x0, _mode = 0x0, _unused2 = {0x0 <repeats 20 times>} }, vtable = 0x7ffff7dca2a0 }, _s = { _allocate_buffer_unused = 0xfbad2887, _free_buffer_unused = 0x7ffff7dce7e3 } }
- 최하단에
_free_buffer
필드가 있는 것을 확인할 수 있다. - 로컬 환경이 Ubuntu-18.04 최신 버전이므로 해당 필드는 unused 처리되어 더 이상 사용되지 않고 있는 상태이며,
fp->_s._free_buffer)
가 호출되는 것이 아닌free
가 호출되도록 패치되어 있는 상태이다. - 하지만 서버 환경은 패치되기 이전의 상태이므로 위 방법을 이용할 수 있다.
- 최하단에
- 따라서, 파일 구조체를 덮어쓸 때 최하단의
_free_buffer
필드는system
함수의 주소로,_IO_buf_base
필드는 “/bin/sh” 문자열 주소로 한 후, vtable 필드는+0xc0
만큼 떨어진_IO_str_jumps
구조체를 가리키도록 조작하면, 셸을 얻을 수 있다.
새롭게 알게된 점
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.