house of spirit은 free 함수의 인자를 조작해 임의의 주소를 해제하여 해당 주소에 쓰기가 가능해지는 공격이다.
free 함수의 인자를 조작할 수 있어야 하며 오버라이트 하고자 하는 타겟의 주소를 알아야 한다.
해당 영역에 값을 쓸 수 있다면 스택 내에 존재하는 다른 지역변수나 리턴 주소를 조작할 수 있다.
가상의 익스플로잇 시나리오를 만들어 보면
1. 먼저 버퍼 등에 fake chunk header를 작성한다.
(청크 구조를 고려해 0x8 또는 0x10단위로 작성)
2. 최초 malloc 호출을 통해 힙 영역을 세팅한 뒤 free 함수로 buffer + 0x10을 해제힌다.
(0x10을 더하는 이유는 fake chunk header를 작성한 부분을 헤더로 인식하도록 하기 위함)
3. 다시 malloc 함수를 호출하면 해제되었던 fake chunk가 다시 할당되면서 타겟 주소에 쓰기가 가능해진다.
(fake chunk header에 적어 두었던 청크 size 값에서 헤더 사이즈 0x10을 뺀 값을 요청)
배경지식
chunk, bin, fastbin, arena, tcache에 대한 간단한 개념 정도만 알면 이 공격을 이해하는데 큰 무리가 없을 것이라 생각한다.
fastbin
arena
tcache
_int_free
라이브러리 버전은 2.27이다.
버전을 타는 공격은 아니므로 함수 흐름을 파악하기 위해 참고 삼아 읽어 봤다.
다음에 나오는 그림들은 2.27버전 malloc.c에 있는 _int_free 함수의 코드 중 일부이다.
먼저 메모리가 해제 된 청크의 포인터가 올바르게 정렬 된 포인터인지 확인하기 위해 misaligned_chunk를 요청한다.
정렬이 올바르지 않으면 invalid pointer 에러가 발생한다.
다음으로 해당 chunk의 "size"에 저장된 값이 MINSIZE 보다 크고 정상적으로 정렬된 값인지 확인한다.
정상적이지 않다면 invalid size 에러가 발생한다.
misaligned_chunk 함수와 aligned_OK 함수는 요렇게 정의되어 있다.
tcache를 사용하고 있을 경우
tcache에 들어갈 사이즈인지 확인하고 인덱스를 체크하는 것 외에 특별한 검증 코드가 없다.
tcache가 꽉 찼거나 사용되지 않는 환경이면 fastbin에 들어갈 수 있는 지 확인한다.
fastbin 범위 내의 사이즈라면 해당 청크의 사이즈가 청크 헤더 사이즈 이상이면서
사이즈에 들어간 플래그 값을 제거한 사이즈가 메인 아레나의 system_mem보다 작아야 한다.
그렇지 않을 때 fail 변수에 true가 저장되면서 invalid next size (fast) 에러가 발생하게 된다.
또한 아레나에 락이 적용되지 않았을 땐 system_mem의 변조 위험이 있어 잠근 뒤 위에서 검사한 조건을 다시 검사한다.
조건을 통과하게 되면 fastbin에 청크 주소가 저장된다.
익스플로잇 흐름
먼저 0x30만큼 입력이 가능한 스택이 있다고 가정해보자.
여기에 0x10만큼 fake chunk header를 작성한다.
(크기를 0x50 만큼 줬다는 점에 주목)
다음으로 malloc 함수를 최초 호출하여 힙 영역을 세팅한다.
호출 결과, 그림과 같이 힙 영역이 세팅되며 청크 하나가 만들어진다.
다음으로 버퍼주소+0x10에 해당하는 0x7fff...E310 주소를 free 함수의 인자로 지정한다.
이렇게 되면 사이즈가 0x50인 하나의 청크로 인식되어 tcache에 저장된다.
이제 malloc으로 tcache로 보냈던 청크를 다시 받아온다.
0x40(0x50[fake chunk size]-0x10[chunk header size])만큼 요청하는 점에 주목한다.
이제 0xE310부터 0xE350까지 0x40만큼 원하는 데이터를 쓸 수 있게 된다.
이는 RET 영역의 조작으로 이어질 수 있음을 시사한다.
How2Heap
how2heap의 tcache house of spirit 예제이다.
해당 예제의 목표는 원하는 임의의 주소에 청크를 할당받는 것 이다.
fake_chunks 배열을 활용해서 size 영역인 2번째 요소, 즉 1번 인덱스에 size 값 0x40을 넣은 뒤
데이터 영역에 해당하는 3번째 요소, 즉 2번 인덱스를 해제 시킨다.
다시 malloc으로 0x30만큼 요청하면 2번 인덱스의 주소부터 데이터 영역인 청크를 할당 받을 수 있다.
빨간 박스를 친 부분에 브레이크 포인트를 걸고 디버깅해보면서 살펴보자.
먼저 첫 번째로 최초 malloc 함수 호출인 malloc(1)을 호출하는 부분이다.
0x5555555592a0가 청크로 할당되는 것을 볼 수 있다.
다음으로 2번 인덱스를 인자로 하여 free 함수를 호출하는 부분이다.
호출 이후 heapinfo 명령으로 확인해보면 tcache_entry에 해당 주소가 저장됨을 확인할 수 있다.
이제 fake_chunk_size에서 0x10을 뺀 0x30만큼 malloc으로 요청하면
아까 tcache_entry에 저장됐던 주소가 malloc 함수의 리턴 값으로 RAX 레지스터에 저장되는 것을 확인할 수 있다.
이로써 해당 주소부터 0x30만큼 데이터를 쓸 수 있게 되었다.
레퍼런스
https://learn.dreamhack.io/16#96
https://learn.dreamhack.io/98#3
https://github.com/shellphish/how2heap/blob/master/glibc_2.35/tcache_house_of_spirit.c
https://www.lazenca.net/pages/viewpage.action?pageId=1148022
'background > linux' 카테고리의 다른 글
stack pivoting (0) | 2023.07.06 |
---|---|
[how2heap] House of Lore (glibc 2.35) (0) | 2023.07.03 |
[how2heap] House of Force (glibc 2.27) (0) | 2023.03.26 |
Frame Pointer Overwirte (One Byte Overflow) (0) | 2023.03.26 |
return-to-csu x64 (RTC, JIT ROP) (0) | 2023.01.12 |