본문 바로가기

_IO_cleanup, _chain 조작을 통한 FSOP

@eouya22025. 12. 12. 22:00

 

후후 시험이 끝났다. 블로그도 안써버릇 하니 습관이 돼서 계속 안쓰게 되는 것 같다..

 

이번 글은 _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
eouya2
@eouya2 :: eouya2

개인공부 기록 / 틀린거 있으면 돌팔매질 부탁드립니다

목차