ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • FSOP - File Stream Oriented Programming
    Heap exploitation 2020. 2. 15. 04:28

    FSOP란 glibc의 FILE 구조체가 가지고있는 vtable을 변경하는 등 파일 스트림을 이용해 프로그램의 흐름을 변경하는 기법이다. 

     

    Structures


    struct _IO_FILE

    이 구조체는 glibc에서 사용하는 FILE 구조체이다. 파일을 선언하고 fopen으로 파일을 열게 되면 힙에 이 구조체가 생성된다.

    struct _IO_FILE {
      int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */
    #define _IO_file_flags _flags
     
      /* The following pointers correspond to the C++ streambuf protocol. */
      /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
      char* _IO_read_ptr;   /* Current read pointer */
      char* _IO_read_end;   /* End of get area. */
      char* _IO_read_base;  /* Start of putback+get area. */
      char* _IO_write_base; /* Start of put area. */
      char* _IO_write_ptr;  /* Current put pointer. */
      char* _IO_write_end;  /* End of put area. */
      char* _IO_buf_base;   /* Start of reserve area. */
      char* _IO_buf_end;    /* End of reserve area. */
      /* The following fields are used to support backing up and undo. */
      char *_IO_save_base; /* Pointer to start of non-current get area. */
      char *_IO_backup_base;  /* Pointer to first valid character of backup area */
      char *_IO_save_end; /* Pointer to end of non-current get area. */
     
      struct _IO_marker *_markers;
     
      struct _IO_FILE *_chain;
     
      int _fileno;
    #if 0
      int _blksize;
    #else
      int _flags2;
    #endif
      _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
     
    #define __HAVE_COLUMN /* temporary */
      /* 1+column number of pbase(); 0 is unknown. */
      unsigned short _cur_column;
      signed char _vtable_offset;
      char _shortbuf[1];
     
      /*  char* _save_gptr;  char* _save_egptr; */
     
      _IO_lock_t *_lock;
    #ifdef _IO_USE_OLD_IO_FILE
    };

    공격자의 입장에서 자세히 볼 내용은 _IO_lock_t이다. 멀티쓰레딩 환경에서 파일을 읽고 쓸 때 race condition을 방지하기 위해 이 포인터를 사용한다. 이 포인터는 write 권한이 있는 메모리 영역을 가리키고 있어야 한다. 일반적으로 자기 FILE 구조체를 가리키게 하거나 bss영역을 넣기도 한다.

     

    struct _IO_FILE_plus

    struct _IO_FILE_plus
    {
    	_IO_FILE file;
    	const struct _IO_jump_t *vtable; // + 216
    }

    이 구조체는 최신 glibc에서 사용하는 FILE 구조체로, _IO_FILE에 _IO_jump_t를 가리키는 포인터가 추가되었다.

     

    struct _IO_jump_t

    이 구조체는 _IO_FILE_plus에서 사용하는 vtable 구조체이다.

    struct _IO_jump_t
    {
        JUMP_FIELD(size_t, __dummy);
        JUMP_FIELD(size_t, __dummy2);
        JUMP_FIELD(_IO_finish_t, __finish);
        JUMP_FIELD(_IO_overflow_t, __overflow);
        JUMP_FIELD(_IO_underflow_t, __underflow);
        JUMP_FIELD(_IO_underflow_t, __uflow);
        JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
        /* showmany */
        JUMP_FIELD(_IO_xsputn_t, __xsputn);
        JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
        JUMP_FIELD(_IO_seekoff_t, __seekoff);
        JUMP_FIELD(_IO_seekpos_t, __seekpos);
        JUMP_FIELD(_IO_setbuf_t, __setbuf);
        JUMP_FIELD(_IO_sync_t, __sync);
        JUMP_FIELD(_IO_doallocate_t, __doallocate);
        JUMP_FIELD(_IO_read_t, __read);
        JUMP_FIELD(_IO_write_t, __write);
        JUMP_FIELD(_IO_seek_t, __seek);
        JUMP_FIELD(_IO_close_t, __close); // + 136
        JUMP_FIELD(_IO_stat_t, __stat);
        JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
        JUMP_FIELD(_IO_imbue_t, __imbue);
    #if 0
        get_column;
        set_column;
    #endif
    };

     

    결론적으로 보면 아래 그림과 같이 구조체가 생성된다.

    _IO_FILE_plus

    Exploit Flow


    일반적으로 FILE*를 변경하는 경우 위 두 구조체를 직접 다 만들어주어도 되고, vtable*만 덮을 경우 _IO_file_jumps를 만들어주면 된다.

    아니면 libc에 있는 stdin의 vtable에 접근해 직접 vtable을 변조하는 등 여러가지 방법이 존재한다.

    댓글

Designed by Tistory.