HO_9
HO9
HO_9
전체 방문자
오늘
어제
  • 분류 전체보기 (104)
    • Write Up (3)
    • WarGame (21)
      • The Lord of Bufferoverflow(.. (7)
      • The Lord of Sql Injection(L.. (1)
      • Pwnable.kr (1)
      • Pwnable.tw (0)
      • XSS GAME (6)
      • Pwnable.xyz (5)
    • SYSTEM HACKING (49)
      • 기법 (24)
      • 문제 풀이 (24)
    • CODING (2)
      • PYTHON (2)
    • WEB HACKING (1)
    • Plan (0)
    • PROJECT (0)
    • iOS (6)
    • ALGORITHM (0)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록

공지사항

  • .

인기 글

태그

  • 취약점
  • 아파치
  • log4j
  • JNDI

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
HO_9

HO9

_IO_FILE vtable overwrite & _IO_FILE Structure
SYSTEM HACKING/기법

_IO_FILE vtable overwrite & _IO_FILE Structure

2022. 6. 4. 19:51
728x90

_IO_FILE관련 내용 정리 겸 작성합니다.

이해가 완벽히 되지는 않아서

틀린 점이나 모호하게 쓰는 점 이해 부탁드립니다. 혹시라도 잘못된 점 있으면 댓글 달아주세요!!

추가로 해당글은 Dreamhack강의를 기반으로 작성하였습니다.

 

_IO_FILE

리눅스 시스템 표준 라이브러리에서 파일 스트림을 나타내기 위한 구조체.

fopen(),fclose(),fwrite()...와 같은 함수들에 대해서 적용된다.

 

해당 구조체는 아래와 같으며 /libio/bits/types/struct_FILE.h에 정의되어 있다.

struct _IO_FILE
{
  int _flags;        /* High-order word is _IO_MAGIC; rest is flags. */
 
  /* The following pointers correspond to the C++ streambuf protocol. */
  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;
  int _flags2;
  __off_t _old_offset; /* This used to be _offset but it's too small.  */
 
  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];
 
  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

 

해당 구조체의 몇가지 변수들에 대해서 설명을 해보자면

 

_flags

해당 설명은 /usr/include/libio.h에 명시되어있다.

/* Magic numbers and bits for the _flags field.
   The magic numbers use the high-order bits of _flags;
   the remaining bits are available for variable flags.
   Note: The magic numbers must all be negative if stdio
   emulation is desired. */

#define _IO_MAGIC 0xFBAD0000 /* Magic number */
#define _OLD_STDIO_MAGIC 0xFABC0000 /* Emulate old stdio. */
#define _IO_MAGIC_MASK 0xFFFF0000
#define _IO_USER_BUF 1 /* User owns buffer; don't delete it on close. */
#define _IO_UNBUFFERED 2
#define _IO_NO_READS 4 /* Reading not allowed */
#define _IO_NO_WRITES 8 /* Writing not allowd */
#define _IO_EOF_SEEN 0x10
#define _IO_ERR_SEEN 0x20
#define _IO_DELETE_DONT_CLOSE 0x40 /* Don't call close(_fileno) on cleanup. */
#define _IO_LINKED 0x80 /* Set if linked (using _chain) to streambuf::_list_all.*/
#define _IO_IN_BACKUP 0x100
#define _IO_LINE_BUF 0x200
#define _IO_TIED_PUT_GET 0x400 /* Set if put and get pointer logicly tied. */
#define _IO_CURRENTLY_PUTTING 0x800
#define _IO_IS_APPENDING 0x1000
#define _IO_IS_FILEBUF 0x2000
#define _IO_BAD_SEEN 0x4000
#define _IO_USER_LOCK 0x8000

flags값은 기본적으로 Magic number인 0xFBAD0000으로 설정이 되며,

나머지는 하위 2바이트는 비트 플래그들로 값을 조합을하여 설정된다.

 

_IO_read_ptr

파일 읽기 버퍼에 대한 포인터

 

_IO_read_end

파일 읽기 버퍼의 끝을 명시하는 포인터

 

_IO_read_base

파일 읽기 버퍼의 시작을 명시하는 포인터

 

_chain

_IO_FILE구조체는 _chain을 통해서 링크드 리스트로 만들고 이를 _IO_list_all에 저장시킴

 

_fileno

파일 디스크립터

 


_IO_FILE - vtable

 

실제 파일 스트림을 열때는 _IO_FILE이 아닌 _IO_FILE_plus구조체가 리턴된다.

이는 파일스트림에서의 함수 호출을 용이하게 하기 위해서 vtable을 추가로 넣어서 만든 구조체이다.

 

struct _IO_FILE_plus
{
  FILE file; // typedef struct _IO_FILE FILE; (glibc/libio/bits/types/FILE.h)
  const struct _IO_jump_t *vtable;
};

FILE은 _IO_FILE 구조체 이므로 넘어가고

새롭게 _IO_jump_t 구조체의 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);
    JUMP_FIELD(_IO_stat_t, __stat);
    JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
    JUMP_FIELD(_IO_imbue_t, __imbue);
};

 

_IO_jump_t를 확인해보면 fread,fwrite,fopen,write,read...들과 같은 여러가지 함수 포인터들이 존재한다.

여기서 fread,fwrite는 없는데?? 할수도 있지만!

 

size_t
_IO_fwrite (const void *buf, size_t size, size_t count, FILE *fp)
{
  size_t request = size * count;
  size_t written = 0;
  CHECK_FILE (fp, 0);
  if (request == 0)
    return 0;
  _IO_acquire_lock (fp);
  if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
    written = _IO_sputn (fp, (const char *) buf, request);
  _IO_release_lock (fp);
  .
  .
  .
#define _IO_sputn(__fp, __s, __n) _IO_XSPUTN (__fp, __s, __n)

_IO_XSPUTN은 vtable 내부에 있는 함수 포인터다.

이와 같이 vtable 내부의 함수 포인터들을 이용해서 함수를 호출하는 것으로 나타난다.

 

그러면 여기서 생각해볼 수 있는 것이

vtable을 overwrite하면 원하는 함수를 호출할 수 있지 않을까?

실제로 vtable을 덮으면 원하는 함수 호출이 가능하다.

(glibc 2.24이상 버전부터 _IO_validate_vtable()이 생겨서 안됩니다!)

 

Dreamhack에 있는 코드로 실제로 메모리 확인을 해보면서 진행해보겠습니다!

(https://learn.dreamhack.io/11#47)

실제로 _IO_FILE 구조체를 본 것과 메모리에 할당되어있는 값들의 offset이 조금씩 다른 것 같아서

(제가 모르는 것 일수도 있슴다)

실제로 메모리 확인을 통해서 어느 부분이 어디를 가리키는지 확인했습니다.

 

 

조작되기 이전의 메모리 상태입니다.

현재 fp는 0x602010을 가리키고 있는 상태입니다.

0x602010을 확인 해보면 _IO_FILE 구조체가 있는 것을 확인 할 수가 있습니다.

그중에서 0x602e8주소를 보면 vtable주소가 들어있습니다.

 

이를 조작하기 위해서 fp변수에 넣어줄 값을

name변수에 _IO_FILE 구조체에 맞게 값을 작성해주고

vtable주소에 원하는 함수가 들어있는 주소로 할당이 되게 값을 조작해준다.

 

임의로 *vtable 값에 junk값을 넣어서 확인하면 아래와 같은 부분에서 오류가 뜨는 것을 확인 할 수가 있다.

..vtable 값을 넘겨줄때는 rax+0x40된 부분에 원하는 함수 주소를 적어주어야 한다는 것이다.

 

 

Exploit code (https://learn.dreamhack.io/11#49)를 확인하면 익스코드가 존재한다..

 

.

.

.

.

.

Glibc 2.24 버전 이후의 방법은 다음 글에서 설명하겠습니다.

'SYSTEM HACKING > 기법' 카테고리의 다른 글

Exploit using _rtld_global & exit()  (0) 2022.05.28
House of Force?  (0) 2022.01.25
Unsafe unlink in glibc 2.23, 2.27 and over 2.27  (0) 2022.01.22
Poison Null Byte in glibc 2.27  (0) 2022.01.16
Fastbin double free in Glibc 2.3.x  (0) 2022.01.01
    'SYSTEM HACKING/기법' 카테고리의 다른 글
    • Exploit using _rtld_global & exit()
    • House of Force?
    • Unsafe unlink in glibc 2.23, 2.27 and over 2.27
    • Poison Null Byte in glibc 2.27
    HO_9
    HO_9

    티스토리툴바