후후 시험이 끝났다. 블로그도 안써버릇 하니 습관이 돼서 계속 안쓰게 되는 것 같다..
이번 글은 _chain field를 이용한 FSOP에 대한 글이다. 이 공격기법의 이름은 아직 잘 모르겠다.
이 방법은 최근 푼 문제 중 stdout에 입력을 받지만 vtable까지 덮지 못하고 _chain 필드 까지만 덮을 수 있는 문제가 있어서 알게 되었다.
우선 시작은 _IO_cleanup 함수를 호출하며 시작한다. 소스코드는 glibc 2.39 기준
_IO_cleanup
_IO_cleanup은 다음과 같은 세가지 경로를 통해서 호출할 수 있다.
- main에서 return시
- exit
- abort
_IO_cleanup 함수는 프로그램 종료 시 모든 열린 파일 스트림을 정리하는 역할을 수행한다.
이 때, FILE 구조체의 _chain field를 따라 모든 열려있는 파일 스트림을 순회하게 되며, _chain field가 공격자가 만들어 둔 fake FILE 구조체를 가리키게 하여 공격을 진행한다. 익스 방법을 먼저 설명하기 보단 호출 흐름을 따라가보면 어떤 원리로 진행되는 지 알기 쉽다.
int
_IO_cleanup (void)
{
int result = _IO_flush_all ();
_IO_unbuffer_all ();
return result;
}
먼저 _IO_cleanup 함수는 내부에서 _IO_flush_all 함수를 호출하게 된다.
_IO_flush_all
int
_IO_flush_all (void)
{
int result = 0;
FILE *fp;
#ifdef _IO_MTSAFE_IO
_IO_cleanup_region_start_noarg (flush_cleanup);
_IO_lock_lock (list_all_lock);
#endif
for (fp = (FILE *) _IO_list_all; fp != NULL; fp = fp->_chain)
{
run_fp = fp;
_IO_flockfile (fp);
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
|| (_IO_vtable_offset (fp) == 0
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base))
)
&& _IO_OVERFLOW (fp, EOF) == EOF)
result = EOF;
}
_IO_flush_all의 내부를 보면 _IO_OVERFLOW 함수가 보인다. 우리는 해당 함수를 호출해야한다.
호출하기 위한 조건식을 해석해보면 아래 두 조건중 하나를 만족시키면 된다.
1.
fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base
OR
2.
_IO_vtable_offset (fp) == 0 && fp->_mode > 0 &&
(fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
_IO_OVERFLOW
#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
#define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
#define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
(_IO_JUMPS_FUNC(_IO_list_all)->__overflow) (_IO_list_all, EOF)
# define _IO_JUMPS_FUNC(THIS) \
(IO_validate_vtable \
(*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (THIS) \
+ (THIS)->_vtable_offset)))
_IO_OVERFLOW는 최종적으로 _IO_JUMPS_FUNC을 호출하게 되는데, 해당 함수에서 IO_validate_vtable을 통해 vtable 검사를 진행하고 vtable offset에 해당하는 함수를 호출하게 된다.
Exploit
익스 개념은 다음과 같다.
만약 vtable을 조작할 수 있다면 _IO_wfile_jumps 등으로 조작을 해주면 vtable 검증을 우회하고 FSOP가 가능하다.
이 방법을 이용하면 FILE 구조체의 _chain field 까지만 덮을 수 있을 때에도 FSOP가 가능하다. 위에서 설명했듯 _chain field를 따라 순회하며 열린 파일 스트림을 flush 한다. 만약 fake FILE struct를 구성해두고 _chain을 적절히 조절하여 해당 구조체를 가리키게 하면 _IO_cleanup이 파일스트림을 순회하며 결국엔 fake struct를 flush 하는 과정을 거치게 되고 fake struct에서 _IO_JUMPS_FUNC을 호출하게 되며 FSOP 공격이 가능해진다.
'PWN > 개념' 카테고리의 다른 글
| House of botcake (0) | 2025.10.31 |
|---|---|
| calloc without memset .. (0) | 2025.10.27 |
| docker process에 gdb attach 방법 그 외 (0) | 2025.09.30 |
| fflush() 이용한 libc leak, 그 외 (0) | 2025.09.11 |
| fastbin attack시의 size check (0) | 2025.09.08 |