[Dreamhack] _IO_FILE: 3 - _IO_FILE Arbitrary Address Write- 1

securitykss·2023년 3월 1일
0

Pwnable 강의(dreamhack)

목록 보기
56/58
post-thumbnail

https://dreamhack.io/lecture/courses/274 을 토대로 작성한 글입니다.

1. Introduction

파일을 읽는 과정에서 파일 구조체를 조작해 임의 메모리에 값을 쓰는 실습을 해보자.

예제 코드

// Name: iofile_aaw
// gcc -o iofile_aaw iofile_aaw.c -no-pie 

#include <stdio.h>
#include <unistd.h>
#include <string.h>

char account_buf[1024];
int overwrite_me;

void init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
}

int read_account() {
	FILE *fp;
	fp = fopen("/etc/passwd", "r");
	fread(account_buf, sizeof(char), sizeof(account_buf), fp);

	write(1, account_buf, sizeof(account_buf));

	fclose(fp);
}

int main() {
  FILE *fp;
  char file_buf[1024];

  init();

  fp = fopen("/etc/issue", "r");

  printf("Data: ");

  read(0, fp, 300);

  fread(file_buf, 1, sizeof(file_buf)-1, fp);
  printf("%s", file_buf);

  if( overwrite_me == 0xDEADBEEF) 
  	read_account();
  fclose(fp);
}

2. 파일 읽기 함수 분석

2.1 파일 읽기 과정

_IO_new_file_xsputn 함수

_IO_size_t
_IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
{
  _IO_size_t want, have;
  _IO_ssize_t count;
  _char *s = data;
  want = n;
    ...
	  /* If we now want less than a buffer, underflow and repeat
	     the copy.  Otherwise, _IO_SYSREAD directly to
	     the user buffer. */

	  if (fp->_IO_buf_base
	      && want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base))
	    {
	      if (__underflow (fp) == EOF)
		break;
	      continue;
	    }
	...
}

파일의 내용을 읽기 위한 함수는 대표적으로 fread, fgets가 있다.

해당 함수는 라이브러리 내부에서 _IO_file_xsgetn 함수를 호출한다.

위 코드를 살펴보면, 해당 함수는 파일 함수의 인자로 전달된 n이 _IO_buf_end - _IO_buf_base 값보다 작은지를 검사하고

__underflow 즉, _IO_new_file_underflow 함수를 호출한다.

실제로 파일의 내용을 읽는 과정은 _IO_new_file_underflow를 시작으로 다양한 함수가 호출되면서 이뤄진다.

그렇다면 해당 함수 내부에서 어떻게 파일의 내용을 읽는지 자세하게 알아보자.

2.2 _IO_new_file_underflow

_IO_new_file_underflow 함수

int _IO_new_file_underflow (FILE *fp)
{
  ssize_t count;
  if (fp->_flags & _IO_NO_READS)           
    {
      fp->_flags |= _IO_ERR_SEEN;
      __set_errno (EBADF);
      return EOF;
    }
   ...
   count = _IO_SYSREAD (fp, fp->_IO_buf_base,     
	fp->_IO_buf_end - fp->_IO_buf_base);
}

위 코드는 _IO_new_file_underflow 함수로,

코드를 살펴보면, 해당 함수 내부에서는 파일 포인터의 _flags 변수에 읽기 권한이 부여되어 있는지를 확인한다.

이후 _IO_SYSREAD 함수의 인자로 파일 포인터와 파일 구조체의 멤버 변수를 연산한 값이 전달된다.

_IO_SYSREAD 함수는 vtable의 _IO_file_read 함수로, 아래 매크로에서 확인할 수 있다.

#define _IO_SYSREAD(FP, DATA, LEN) JUMP2 (__read, FP, DATA, LEN)

2.3 _IO_file_read

_IO_ssize_t
_IO_file_read (_IO_FILE *fp, void *buf, _IO_ssize_t size)
{
  return (__builtin_expect (fp->_flags2 & _IO_FLAGS2_NOTCANCEL, 0)
	  ? __read_nocancel (fp->_fileno, buf, size)
	  : __read (fp->_fileno, buf, size));
}

_IO_file_read 함수 내부에서는 read 시스템 콜을 사용해 파일의 데이터를 읽는다.

시스템 콜의 인자로 파일 구조체에서 파일 디스크립터를 나타내는 _fileno, _IO_buf_base인 buf, 그리고 _IO_buf_end - _IO_buf_base로 연산된 size 변수가 전달된다.

전달되는 인자를 파일 구조체로 표현하면 다음과 같다.

read(f->_fileno, _IO_buf_base, _IO_buf_end - _IO_buf_base);

Reference

https://dreamhack.io/lecture/courses/274

profile
보안 공부를 하는 학생입니다.

0개의 댓글