// Name: bypass_valid_vtable
// gcc -o bypass_valid_vtable bypass_valid_vtable.c -no-pie
#include <stdio.h>
#include <unistd.h>
FILE *fp;
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
int main() {
init();
fp = fopen("/dev/urandom", "r");
printf("stdout: %p\n", stdout);
printf("Data: ");
read(0, fp, 300);
fclose(fp);
}
코드를 보면 libc leak이 가능하고 fp 값을 덮어 쓸 수 있다.
fclose 내부 함수를 system 함수로 변조하여 실행시킬 수 있다.
문제에서 라이브러리를 제공해주고 있어 환경 변수인 LD_PRELOAD에 제공된 라이브러리를 넣어 바이너리 실행 시 제공된 라이브러리를 사용할 수 있게 만든다.
libc_base 주소를 이용해 필요한 함수 주소를 가져온다.
1. _IO_str_overflow의 주소를 값으로하는 주소는 _IO_file_jumps + 0xd8 위치에 있다.
: _IO_file_jumps 구조체 다음에 _IO_str_jumps 구조체가 위치하고 있다. 따라서 구조체이기에 offset이 고정된 값일테니 항상 참조할 수 있는 것이다.
2. io_str_overflow - 0x10 한 fake_overflow는 무엇인가?
: fclose 함수를 실행하면 내부 함수에서 vtable 주소 + 0x10 에 위치한 함수 포인터를 실행한다. -> 실행시키고 싶은 함수 주소를 넣는게 아니라 실행시키고 싶은 함수 주소를 값으로 하는 주소를 넣어야 한다! ㄷㄷ;
따라서, _IO_str_overflow 실행시켜야 하므로 fake_overflow 값을 넣는다(_IO_file_overflow 주소를 값으로 함!)
3. fp는 왜 구함??
: _IO_FILE 구조체에서 _lock 멤버 변수는 멀티스레딩 환경에서 파일을 읽고 쓸때 race condition을 막기 위해 사용되는 변수로 무조건 쓰기 권한이 있는 영역의 주소를 넣어야 한다. 따라서, 전역변수인 fp를 구했다. -> bss 영역!
한번에 성공하지 못하는 경우가 있으니 offset을 바꿔가며 시도 해보자
최종 익스 코드이다.
payload는 파일 구조체를 덮기위한 값이다. 아래 vtable 다음에 system 주소를 넣는 이유는 _s._allocate_buffer가 vtable 주소 + 8 byte의 위치를 참조하기 때문에 vtable 주소 뒤에 써준 것이다.
성공!
알게 된 점
1. LD_PRELOAD 변수에 라이브러리를 등록하면 해당 라이브러리로 load하여 바이너리를 실행한다.
참고
https://wyv3rn.tistory.com/114
'Dreamhack - pwnable' 카테고리의 다른 글
iofile_aaw (0) | 2023.03.27 |
---|---|
send_sig (0) | 2023.03.15 |
Sigreturn-Oriented Progamming (0) | 2023.03.14 |
rtld (0) | 2023.03.12 |
environ (0) | 2023.03.06 |