Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
house_of_apple_2 — glibc 2.43에서 House of Apple 2 FSOP 기법을 다루는 대화형 GDB 워크스루로, vtable 우회, 스택 피보팅, ROP를 아우르는 재현 가능한 샌드박스를 제공합니다. | Kitploit
도구/GitHubGitHub/jazho76/house_of_apple_2
Memory ForensicsExploitationReverse EngineeringShellcodeDebuggersPapers & ResearchLearning & EducationPayload DevelopmentBinary ExploitationLabs & Practice
GitHubjazho76/house_of_apple_2
511281개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

house_of_apple_2

glibc 2.43에서 House of Apple 2 FSOP 기법을 다루는 대화형 GDB 워크스루로, vtable 우회, 스택 피보팅, ROP를 아우르는 재현 가능한 샌드박스를 제공합니다.

저장소 보기

최신 glibc에서의 House of Apple 2 탐구

이 저장소는 pwn.college의 File Struct Exploitation 모듈에서 영감을 받은 자체 완결형 놀이터입니다. House of Apple 2의 새로운 변형을 소개하는 것이 아니라, 이 기법이 최신 버전의 glibc에서 어떻게 유지되는지, 그리고 여전히 실행 가능한 익스플로잇 경로인지에 대한 저의 호기심을 해결해 줄 뿐입니다. 이 문서는 독자들이 샌드박스와 함께 따라 하며 이 프리미티브에 대한 보다 직관적인 이해를 키울 수 있도록 대화형 GDB 워크스루를 제공합니다. 모든 실험은 작성 시점에 Ubuntu 26.04와 Fedora 44에서 패키징된 glibc 2.43을 사용합니다.

File Stream Oriented Programming (FSOP). 이는 glibc 파일 스트림 구조를 조작하여 제어 흐름을 탈취하는 것에 관한 것입니다. 이를 수행하는 한 가지 방법은 _IO_FILE_plus의 vtable 디스패치 메커니즘을 손상시키는 것입니다. 최신 glibc는 이 vtable을 검증하므로, 이를 임의의 주소로 교체하는 뻔한 접근 방식은 통하지 않습니다.

원래 Roderick이 소개한 House of Apple 2는 유효한 _IO_FILE_plus vtable을 사용하여 와이드 문자 스트림 기계에 도달함으로써 이 제한을 우회합니다. 여기서 보조 vtable은 범위 검증 없이 직접 디스패치됩니다. 이는 우리가 스택 피벗과 ROP 체인으로 확장할 수 있는 arbitrary call 프리미티브를 제공합니다.

익스플로잇 전제 조건

이 탐구는 FILE 구조를 덮어쓸 수 있고 힙 릭과 libc 릭을 모두 가지고 있다고 가정합니다. 대상 바이너리는 이미 이를 제공합니다.

샌드박스 환경

샌드박스는 Ubuntu 26.04 LTS를 실행하여 이 기법을 탐구할 최신 환경을 제공합니다.

이미지에는 GDB, pwndbg, pwntools, ropper 및 tmux가 포함되어 있습니다. 또한 fopen, fread, fwrite, fclose와 같은 파일 스트림 작업을 호출하기 위한 대화형 메뉴가 있는 대상 바이너리도 포함되어 있습니다. 이는 디버깅하고 아이디어를 테스트하는 동안 스트림을 조작할 수 있는 편리한 방법을 제공합니다.

다음 명령으로 샌드박스를 빌드하고 실행하세요:``` ./build.sh ./run.sh

## 탐색

먼저 `_IO_FILE` 및 `_IO_FILE_plus` 구조체를 살펴보겠습니다:```c
pwndbg> ptype struct _IO_FILE
type = struct _IO_FILE {
    int _flags;
    char *_IO_read_ptr;
    char *_IO_read_end;
    char *_IO_read_base;
    char *_IO_write_base;
    char *_IO_write_ptr;
    char *_IO_write_end;
    char *_IO_buf_base;
    char *_IO_buf_end;
    char *_IO_save_base;
    char *_IO_backup_base;
    char *_IO_save_end;
    struct _IO_marker *_markers;
    struct _IO_FILE *_chain;
    int _fileno;
    int _flags2 : 24;
    char _short_backupbuf[1];
    __off_t _old_offset;
    unsigned short _cur_column;
    signed char _vtable_offset;
    char _shortbuf[1];
    _IO_lock_t *_lock;
    __off64_t _offset;
    struct _IO_codecvt *_codecvt;
    struct _IO_wide_data *_wide_data;
    struct _IO_FILE *_freeres_list;
    void *_freeres_buf;
    struct _IO_FILE **_prevchain;
    int _mode;
    int _unused3;
    __uint64_t _total_written;
    char _unused2[8];
}
pwndbg> ptype struct _IO_FILE_plus
type = struct _IO_FILE_plus {
    FILE file;
    const struct _IO_jump_t *vtable;
}

실제로 _IO_FILE_plus는 vtable 포인터를 가진 _IO_FILE이다. 이는 즉시 흥미로워 보인다. 이 포인터를 제어할 수 있다면 간접 호출을 리다이렉트하여 제어 흐름을 탈취할 수 있을지도 모른다.

파일 스트림 vtable 검사하기

vtable을 검사하기 위해, fopen이 반환한 FILE 포인터를 살펴보자.```c pwndbg> p *(struct _IO_FILE_plus *)0x37ecf010 $4 = { file = { _flags = 0xfbad2480, _IO_read_ptr = 0x0, _IO_read_end = 0x0, _IO_read_base = 0x0, _IO_write_base = 0x0, _IO_write_ptr = 0x0, _IO_write_end = 0x0, _IO_buf_base = 0x0, _IO_buf_end = 0x0, _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7f58a7f4b4a0 <IO_2_1_stderr>, _fileno = 0x3, _flags2 = 0x0, _short_backupbuf = "", _old_offset = 0x0, _cur_column = 0x0, _vtable_offset = 0x0, _shortbuf = "", _lock = 0x37ecf0f0, _offset = 0xffffffffffffffff, _codecvt = 0x0, _wide_data = 0x37ecf100, _freeres_list = 0x0, _freeres_buf = 0x0, _prevchain = 0x7f58a7f4b480 <_IO_list_all>, _mode = 0x0, _unused3 = 0x0, _total_written = 0x0, _unused2 = "\000\000\000\000\000\000\000" }, vtable = 0x7f58a7f49030 <_IO_file_jumps> }

포인터는 `_IO_file_jumps` 테이블을 가리킵니다.

![3](https://assets.kitploit.com/production/public/readmes/56272/9fe7069dbb47c87d81c704a23ff483a6c630d5506150361db4aefee1516d0423/054e7475018b9adcfdb102b7e142ea8ad09116b38787416fdc30f8cb2f209bfa-display-v1.webp)

이것은 21개의 함수 포인터 집합입니다. 파일 스트림 연산은 실행 경로에 따라 서로 다른 항목을 통해 디스패치됩니다.

### `fwrite` 경로 따라가기

이번 탐구에서는 `fwrite` 경로에 집중하겠습니다. 각 함수에 중단점을 설정하고 `fwrite`를 호출한 후, 처음으로 도달하는 중단점은 `_IO_file_xsputn`입니다.

![4](https://assets.kitploit.com/production/public/readmes/56272/f77a8ff4f3d548b46916a97897b811b5cfb650de4843184b6574bdac51e1c2d0/e7de50a63631e0e1b18e8aa0b2d0852b62457428b4be3936ea3ea6a776531329-display-v1.webp)

이 호출은 `fwrite+216`에서 발생합니다. 이는 [glibc 소스](https://elixir.bootlin.com/glibc/glibc-2.43/source/libio/iofwrite.c#L44)와 일치합니다: `_IO_sputn`은 vtable을 통해 디스패치되는 매크로로, 이 스트림에 대해서는 `_IO_file_xsputn`으로 해석됩니다.```asm
   0x00007fd5181d362a <+202>:	mov    rdx,rcx
   0x00007fd5181d362d <+205>:	mov    rdi,rbx
   0x00007fd5181d3630 <+208>:	mov    QWORD PTR [rbp-0x30],r8
   0x00007fd5181d3634 <+212>:	mov    QWORD PTR [rbp-0x28],rcx
   0x00007fd5181d3638 <+216>:	call   QWORD PTR [rax+0x38]

vtable 교체 시도

첫 번째 시도로, vtable 포인터를 desired_func - 0x38로 덮어쓰고 fwrite+216에 중단점을 설정해 보겠습니다.```c pwndbg> p &win $3 = (<text variable, no debug info> *) 0x4019e1 pwndbg> p/x &win - 0x38 $4 = 0x4019a9 pwndbg> set ((struct _IO_FILE_plus *)0x5334010)->vtable = (void *)0x4019a9 pwndbg> b *fwrite+216 Breakpoint 4 at 0x7fd5181d3638: file ./libio/libioP.h, line 1042.

![5](https://assets.kitploit.com/production/public/readmes/56272/68d9bb8c66bf4088af2741eebea1ecb9b56d047f77983d068d26c9566a97e533/1c9d22964dbe7735df03440da27a86e739989f7cf62bd6e126cecf1083b2cf4b-display-v1.webp)

중단점에 도달하기 전에 실행이 중단됩니다. 오류는 glibc가 간접 호출을 수행하기 전에 vtable 포인터를 검증한다는 것을 시사합니다. 백트레이스를 검사하여 이것이 어디에서 발생하는지 살펴보겠습니다.

`fwrite`가 `_IO_vtable_check`에 도달하고, 이 함수가 위조된 vtable 포인터를 거부합니다.

![6](https://assets.kitploit.com/production/public/readmes/56272/5d88a98e9a1a20ab8c510aad7374f24884a05f363bd46f94d0d05cad513c7c0a/6974f1daec533bb453c59cff614c11f59c29bb1f85a0d23235e05c22433c0b48-display-v1.webp)

구현에는 외부 vtable을 허용하는 메커니즘이 있지만, 이는 우리의 통제 하에 있지 않습니다. 관련 코드는 [`vtables.c`](https://elixir.bootlin.com/glibc/glibc-2.43/source/libio/vtables.c#L504)에서 확인할 수 있습니다.

### vtable 검증 이해하기

`_IO_vtable_check`가 호출될 때쯤이면 이미 너무 늦었습니다. vtable 검증이 실패한 것입니다. 백트레이스의 앞선 `IO_validate_vtable` 프레임이 흥미로운 부분이므로, 대신 그것을 검사해 보겠습니다.```c
pwndbg> disass IO_validate_vtable
❌️ No symbol "IO_validate_vtable" in current context.

GDB는 IO_validate_vtable을 심볼로 확인할 수 없습니다. 소스를 보면, 이것이 fwrite에 인라인되어 있음을 알 수 있습니다.```asm 0x00007fd5181d35f6 <+150>: lea rdi,[rip+0x1838e3] # 0x7fd518356ee0 <__io_vtables> 0x00007fd5181d35fd <+157>: mov rax,QWORD PTR [rbx+0xd8] 0x00007fd5181d3604 <+164>: mov r14,QWORD PTR [rbx+0xc8] 0x00007fd5181d360b <+171>: mov r15,QWORD PTR [rbx+0x28] 0x00007fd5181d360f <+175>: mov r12,QWORD PTR [rbx+0x20] 0x00007fd5181d3613 <+179>: mov rdx,rax 0x00007fd5181d3616 <+182>: sub rdx,rdi 0x00007fd5181d3619 <+185>: cmp rdx,0x92f 0x00007fd5181d3620 <+192>: ja 0x7fd5181d3780 <__GI__IO_fwrite+544>

vtable 포인터는 `[__io_vtables, __io_vtables + IO_VTABLES_LEN)` 범위에 속할 때만 허용된다. 따라서 원하는 아무 곳이나 가리킬 수는 없다. 그래도 이 범위는 꽤 넓은 영역으로 여러 점프 테이블을 포함하고 있어 탐색할 여지가 있다.

유효 범위는 다음과 같이 시작한다:

![7](https://assets.kitploit.com/production/public/readmes/56272/a44872f4cc95b7607ed0abe8cfe35f7a6eae6be8bbb5a5d017e693b9c67bf87f/4a82dee7de96ab2a558a6a37688145328eb42a0e2c4dbbe74267a08049d1832c-display-v1.webp)

## House of Apple 2

이제 기본 메커니즘과 그 주요 제약, 즉 `_IO_FILE_plus` vtable이 glibc의 유효한 vtable 영역 내부 어딘가를 가리켜야 한다는 점을 이해했다. 이는 명백한 접근 방식을 막지만, 완전히 문을 닫는 것은 아니다.

House of Apple 2는 와이드 문자 스트림 메커니즘을 통해 두 번째 vtable에 도달함으로써 이 제약을 우회한다. 이 두 번째 vtable은 같은 방식으로 검증되지 않는다. GDB에서 그 경로를 따라가며 조각들이 어떻게 연결되는지 살펴보자.

### 와이드 문자 스트림 메커니즘
도구 다운로드