House of Force는 top chunk size를 조작해 임의의 주소에 힙 청크를 할당하는 공격 기법이다.
탑 청크의 사이즈를 조작할 수 있어야 하고 원하는 크기의 malloc 요청을 할 수 있어야 한다.
오버라이트 하고자 하는 임의의 주소인 target address와 top chunk address를 알아야 한다.
house of force는 glibc 2.29 버전에서 패치 되었다.
익스플로잇을 할 때 먼저 탑 청크의 사이즈를 표현할 수 있는 최대 값으로 조작한다.
이후 첫 번째 malloc으로 target 주소 - header size – 탑 청크 주소 – header size 만큼 요청해
target 주소 – header size까지 힙 청크를 할당한다.
이제 두 번째 malloc으로 한 번 더 힙 청크를 요청하면 target 주소에 데이터를 쓸 수 있는 힙 청크가 할당된다.
64비트일 때 청크는 메타데이터의 크기인 0x10으로 정렬되기 때문에
target의 주소가 0x10단위가 아닐 땐 첫 번째 malloc 요청 사이즈에 주의해야 한다.
메모리 구조
이 자료는 TCP스쿨의 메모리 구조에 대해 설명하는 자료다.
코드 영역은 프로그램 코드가 저장되는 영역이다.
데이터 영역은 프로그램의 전역 변수, 정적 변수가 저장된다.
(리눅스에선 bss 영역을 생각하면 될 것 같다)
힙 영역은 사용자가 직접 관리할 수 있는, ‘관리해야만 하는’ 영역이다.
마지막으로 스택 영역은 함수의 호출과 관계되는 지역 변수와 매개변수가 저장된다.
malloc의 힙 영역 할당
이 자료는 리눅스 저널에서 가져온 자료이며 원문 자료를 이해한대로 정리해 보았다.
왼쪽 그림은 힙 영역과 스택 영역이다.
프로세스는 메모리가 필요할 때 brk 또는 sbrk 시스템 콜을 사용해 힙 영역을 확장한다고 한다.
시스템 콜은 라이브러리 함수에 비해 CPU 사용 측면에서 비용이 비싸기 때문에
brk로 큰 청크를 가져온 다음 malloc() 함수로 잘라서 사용하는 것이 더 좋은 전략이라고 설명되어 있다.
엄청 큰 요청이 들어오면 malloc은 mmap 시스템 콜로 영역을 찾는데
이렇게 mmap syscall로 찾는 과정은 메모리 단편화를 줄이는데 도움이 되다고 한다.
malloc.c 분석
malloc 함수의 힙 청크 할당 과정에 대해 살펴보자.
glibc-2.27의 malloc.c 소스 코드의 일부를 가져왔다.
av는 main arena를 의미하고, p는 탑 청크 주소, size는 탑 청크의 헤더를 포함한 사이즈이다.
nb는 malloc으로 요청한 크기이고, MINSIZE는 정의 부분을 살펴보니 청크 헤더 사이즈인 것 같다.
if 문을 보면 탑 청크의 가용 사이즈가 malloc으로 요청한 크기인 nb에 청크 헤더를 더한 크기보다 크거나 같으면
힙 영역에 청크를 할당하게 된다.
빨간색 박스를 보면 malloc의 반환 값(새로운 청크의 주소)인 변수 p가 계산되는 과정을 확인할 수 있다.
chunk_at_offset, chunk2mem 함수를 호출한다.
이 두 함수는 매크로 함수로 이렇게 정의되어 있다.
먼저 chunk_at_offset 함수를 통해 탑청크 주소 p를 요청 크기 nb만큼 더해 이동시킨다.
해당 값을 변수 리마인더를 이용해 탑 청크의 주소로 갱신시킨다.
마지막으로 리턴할 때 청크의 헤더 사이즈를 더한 값(즉, 데이터를 적는 주소)을 최종적으로 반환한다.
How2Heap 분석
how2heap의 house_of_force.c 예제를 살펴보자.
설명하는 부분을 빼고 중요한 부분만 가져왔다.
해당 예제의 목표는 bss 영역 0x404060에 존재하는 문자 배열 변수 bss_var를 변조시키는 것이다.
디버깅하면서 살펴보면
먼저 malloc을 통해 청크를 할당해준다.
malloc으로 요청한 청크는 0xfad250에 할당되었으며 탑 청크는 0xfad360에 위치한다.
메모리를 확인해보면 현재 탑 청크의 사이즈는 0x20ca1 이다.
탑 청크 사이즈는 부호가 없는 타입이기 때문에 integer underflow를 이용해 -1로 변조하게 되면 최대 값으로 변하게 된다.
how2heap에서는 이렇게 되면 mmap을 호출하지 않는다고 설명한다.
이 말은 brk로 힙 영역을 확장시키게 될 것 이라는 의미인 것 같다.
이제 탑 청크의 주소를 타겟 주소로 옮기는 작업이 진행된다.
이렇게 malloc으로 요청할 때 임의의 크기를 요청하자 탑 청크의 주소가 0xfad360에서 0x404050으로 변하는 모습을 볼 수 있다.
이 부분을 좀 더 자세히 알아보자.
탑 청크 주소는 malloc으로 요청한 크기 nb만큼 증가한다.
탑 청크에 요청 크기를 더한 값이 bss_var가 되도록 해야 하므로
요청 크기는 자연스럽게 타겟 주소인 bss_var의 주소에서 탑 청크 주소를 뺀 값이 될 것이다.
malloc은 마지막에 chunk2mem 함수로 인해 헤더 사이즈만큼 더해진 주소를 반환한다.
익스플로잇 수행 과정에서 malloc이 두 번 수행되어야 하기 때문에 아까 계산한 nb 값에서 헤더 사이즈를 두 번 빼 줘야한다.
그래서 요청 값 nb는 아래 빨간 박스와 같이 공식화 된다.
이 과정을 그림으로 정리해보면 먼저 첫 번째 malloc으로 bss_var – header size 까지 할당 받는다.
이제 탑 청크 주소는 bss_var – header size인 0x404050이 된다.
이제 두 번째 malloc으로 data 영역의 시작이 bss_var인 청크를 할당 받는다.
두 번째 malloc의 반환 값인 데이터가 시작되는 주소는 정확히 bss_var의 주소인 0x404060이 된다.
정리하면 타겟 주소에서 탑 청크 주소를 빼준 뒤 헤더를 2번 빼주면 된다.
이제 how2heap 예제를 이어서 보면
맨 위는 첫 번째 malloc을 요청하는 부분이다.
타겟 주소인 bss_var와 탑 청크의 주소 차이 만큼에서 헤더 사이즈를 2번 빼준 값을 요청한다.
여기선 long 타입의 크기 0x8에 4를 곱해서 빼줬다.
첫 번째 malloc이 수행되고 난 후 탑 청크의 주소가 bss_var – 헤더 사이즈(0x10)인 0x404050임을 확인할 수 있다.
두 번째 malloc으로 할당된 주소에 strcpy함수를 사용하면 이번 예제의 목표였던 bss_var의 값 변조에 성공하게 된다.
이는 보호기법에 따라 got overwrite 등의 공격으로도 연계가 가능함을 시사한다.
레퍼런스
http://www.tcpschool.com/c/c_memory_structure
https://learn.dreamhack.io/16#71
https://www.linuxjournal.com/article/6390
https://github.com/shellphish/how2heap/blob/master/glibc_2.27/house_of_force.c
https://rninche01.tistory.com/entry/heap-exploit-House-Of-Force
'background > linux' 카테고리의 다른 글
[how2heap] House of Lore (glibc 2.35) (0) | 2023.07.03 |
---|---|
[how2heap] Tcache House of Spirit (glibc 2.35) (0) | 2023.05.23 |
Frame Pointer Overwirte (One Byte Overflow) (0) | 2023.03.26 |
return-to-csu x64 (RTC, JIT ROP) (0) | 2023.01.12 |
[glibc-2.27] fwrite & fputs (1) | 2023.01.11 |