안녕하세요 오늘은 FSOP의 최신 기법인 House of apple 2에 대해 알아보겠습니다.
저번 포스팅 때 알아본 House of Orange 기법은 vtable 자체에 대한 공격으로 glibc의 버전 2.24 이후 _IO_vtable_check() 함수가 추가되고, 익스플로잇이 불가능해졌습니다.
House of apple은 실제로 존재하는 vtable을 사용합니다. House of apple은 glibc 2.24버전부터 2.38, 2.39도 일부 익스플로잇 가능합니다. 제 로컬 ubuntu는 2.35버전을 사용중인데 따로 glibc버전을 링킹하지 않고도 익스플로잇이 가능했습니다.
house of apple 2는 기존 house of apple에서 조금 더 안정적이고 더 쉽게 변한 방식입니다.
house of apple 2는 다음과 같은 순서로 진행됩니다.
- _IO_FILE의 vtable 포인터를 GLIBC 내의 합법적인 vtable인 _IO_wfile_jumps로 덮어씌웁니다. 이러면 _IO_vtable_check를 통과합니다.
- _IO_wfile_jumps의 함수들은 실행 과정에서 fp->wide_data를 참조하는데 _wide_data 내부에는 _wide_vtable 포인터가 잇고, GLIBC는 이 _wide_vtable은 검증하지 않기 때문에 이 _wide_vtable을 조작하여 system 함수를 호출합니다.
- 힙에 가짜 _IO_FILE 구조체를 만들어 vtable을 _IO_wfile_jumps 주소로 설정합니다.
- 프로그램 종료 시 _IO_flush_all_lockp 가 호출되면서 ctable에 있는 _IO_wfile_overflow가 실행되면서 _IO_wdalloc이 호출됩니다.
- _IO_wdoalloc은 fp->wide_data->_wide_vtable에 있는 doallocate 함수 포인터를 호출하는데 저희가 system을 넣었기 때문에 쉘을 획득할 수 있습니다.
오늘 write-up을 작성할 문제 파일은 위와 같습니다.
해당 문제는 저번달 부경대-부산대 연합 해킹 캠프에서 ctf 문제로 나온 문제입니다.
출제자분께 허락맡고 write-up을 작성했습니다.


문제 코드는 다음과 같습니다.
해당 문제에서 제일 먼저 발생하는 취약점은 OOB입니다.
edit_memory 함수에서 발생하는데 scanf에서 idx에 대한 음수/양수 범위 검사가 없고, 검증되지 않은 주소에 데이터를 씁니다.
여기서 allocated_memory는 전역 변수로 선언 되어 있어서 BSS 영역 또는 data 영역에 위치합니다.
여기서 idx에 음수를 넣으면 allocated_memory배열의 시작 주소보다 앞쪽에 있는 메모리를 출력할 수 있습니다.
보통 전역 변수 배열 앞쪽에는 stdout,stdin,stderr포인터나 GOT등 중요 데이터가 위치하기 때문에 해당 데이터를 덮어 공격이 가능합니다.

allocated_memory의 위치를 확인하고 근처 위치를 찍어봤습니다

allocated_memory에서 -0x20에 stdout이 존재하는 걸 확인할 수 있습니다.
여기서 stdin 대신 stdout을 목표로 삼는 이유는 공격의 트리거가 되는 _IO_flush_all_lockp 함수가 데이터를 쓰는 과정에서 발생하는데 stdin은 기본적으로 쓰기 권한이 없어 호출 조건을 만족하기 힘들기 때문입니다.
메뉴 1번 allocate -> 메뉴 4번에서 idx값에 -4 -> 메뉴 3번에서 idx -4 순서대로 진행하면

입력값 다음에 특정 값이 leak되는 것을 알 수 있습니다.
idx -4의 위치에

위와 같은 libc 주소가 leak 되었는데

stdout의 내부 주소가 leak 되었습니다.
12312312라는 값을 넣었는데 leak된 값이 stdout+131인데

페이로드를 보내고 stdout 구조체를 보면 flag값에 825440817값이 들어가있습니다.
해당 값은 16진수로 0x31323133으로 1,2,1,3이고, 저희가 입력한 값은 stdout의 _flags에 덮어씌어진것을 확인할 수 있습니다.
그리고 메뉴 3번에서 Seek을 누르면 flags값이후 /null바이트가 없기 때문에 _IO_read_ptr에 있는 주소가 출력되는 것도 확인할 수 있습니다.
여기서 edit을 통해 payload를 덮어쓰면 stdout의 구조체가 덮어써지게 됩니다.
일단 필요한 주솟값들입니다.
stdout은 _IO_2_1_stdout_ 구조체의 실제 주소입니다. 저희가 덮어쓰는 주소입니다.
system은 /sh를 실행하기 위해 필요하고,
wfile은 _IO_wfile_jumps의 vtable 주소로 Vtable check를 통과하기 위함입니다.
buf와 lock은 프로그램이 죽지 않기 위함으로 쓰기 가능한 메모리 주소를 아무데나 넣어주면 됩니다.
_IO_read_end,_IO_write_base같은 포인터들을 초기화할 때 쓰며, 해당 값이 NULL이면 안되거나 비교 연산을 수행할 때 에러가 나지 않도록 아무 주소나 넣습니다(쓰기 가능한 주소)
정석적인 house of apple에서는 heap leak과 _IO_list_all 포인터를 조작하기 위한 Large bin attack이나 Tcache poisoning이 추가적으로 필요하게 됩니다
하지만 해당 문제에서는 이미 존재하는 stdout에 대한 덮어쓰기가 가능하기 때문에 _IO_list_all 포인터 조작이 필요 없고, 일반적인 방식에서 순차적 연결 (저번 House_of_orange에서의 libc leak -> heap leak)이 아닌 자기 참조와 중첩으로 해결 가능합니다.
payload입니다. 상세 역할은 주석으로 적어뒀습니다.
처음에 프로그램이 stdout을 보고 vtable에 적힌 _IO_wfile_overflow를 실행합니다(마지막 줄)
저희가 _mode값을 -1로 설정해뒀기 때문이 프로그램이 와이드 모드라고 착각해 wide_data를 가져오기 위해 fp + 0xa0위치를 봅니다.
해당 주소에는 저희가 넣은 stdout - 0x10 주소가 들어있기 때문에 프로그램은 stdout - 0x10 (우리가 작성한 payload)를 참조합니다.
이때 프로그램은 다시 wide_data안의 _wide_vtable을 찾습니다.
이 때 _wide_data + 0xe0만큼 떨어진 곳의 데이터를 참조하는데 해당 위치에는 저희가 다시 stdout위치를 작성해뒀습니다.
payload += p64(stdout)
이때 doallocate함수를 실행하기 위해 _wide_vtable 테이블 시작점에서 0x68만큼 떨어진 곳에 있는 함수를 실행하는데 저희가
payload += p64(system)
를 넣어뒀기 때문에 system이 실행됩니다.
여기서 payload에 stdout - 0x10을 넣은 이유는
이 두 vtable을 둘 다 호출하기 위함인데 _IO_FILE의 구조 정의상 vtable은 항상 끝부분인 오프셋 0xd8에 위치합니다.
그리고 _IO_wide_data의 _wide_vtable은 오프셋 0xe0에 존재합니다.
저희는 stdout을 넣을 때 fp->wide_data 포인터를 stdout-0x10으로 조작했기 때문에 (stdout - 0x10) + 0xe0 = stdout + 0xd0이기 때문에 두 포인터는 8바이트 차이로 나란히 이어지게 됩니다.
프로그램을 진행하면 다음과 같이 쉘을 획득하게 됩니다.

앞에 ERROR가 뜨는건 저희가 fp에 인자를 넣을때 \x00을 넣어서 그렇습니다
이상으로 포스팅을 마치겠습니다.
대학교 동아리 행사 ctf에서 나온 문제라 간단하게만 봤는데 문제가 생각보다 너무 잘만들어져서 배울점이 너무 많았던 것 같습니다.
하지만 정석적인 House of apple 기법은 아니어서 최근 열린 큰 대회중 house of apple 기법을 사용한 문제가 있는지 확인하고 풀어볼 예정입니다.
읽어주셔서 감사하고, 문제와 포스팅을 허락해주신 문제 제작자분께도 감사드립니다!
'취약점분석 > Pwnable' 카테고리의 다른 글
| [Pwnable] FSOP - 1 (_IO_FILE) (0) | 2025.11.25 |
|---|---|
| [Pwnable]ACS 2025 포너블 문제 풀이 (urgent) (0) | 2025.11.24 |
| [Pwnable] tcache_key, Safe Linking (0) | 2025.11.12 |
| [Pwnable] Format String Bug (0) | 2025.11.10 |
| [Pwnable]Tcache_dup2 write-up (0) | 2025.11.09 |