How2Heap Walkthrough
I. House of Force - Exploit Top chunk
1. Source code và các ràng buộc
- Decription : Exploiting the Top Chunk (Wilderness) header in order to get malloc to return a nearly-arbitrary pointer
- Referrence: how2heap : HouseOfForce
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
#include <assert.h>
char bss_var[] = "This is a string that we want to overwrite.";
int main(int argc , char* argv[])
{
int a ;
scanf("%d",&a);
fprintf(stderr, "\nWelcome to the House of Force\n\n");
fprintf(stderr, "The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\n");
fprintf(stderr, "The top chunk is a special chunk. Is the last in memory "
"and is the chunk that will be resized when malloc asks for more space from the os.\n");
fprintf(stderr, "\nIn the end, we will use this to overwrite a variable at %p.\n", bss_var);
fprintf(stderr, "Its current value is: %s\n", bss_var);
fprintf(stderr, "\nLet's allocate the first chunk, taking space from the wilderness.\n");
intptr_t *p1 = malloc(256);
fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - 2);
fprintf(stderr, "\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\n");
int real_size = malloc_usable_size(p1);
fprintf(stderr, "Real size (aligned and all that jazz) of our allocated chunk is %ld.\n", real_size + sizeof(long)*2);
fprintf(stderr, "\nNow let's emulate a vulnerability that can overwrite the header of the Top Chunk\n");
intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));
fprintf(stderr, "\nThe top chunk starts at %p\n", ptr_top);
fprintf(stderr, "\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\n");
fprintf(stderr, "Old size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));
*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;
fprintf(stderr, "New size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));
fprintf(stderr, "\nThe size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.\n"
"Next, we will allocate a chunk that will get us right up against the desired region (with an integer\n"
"overflow) and will then be able to allocate a chunk right over the desired region.\n");
unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;
fprintf(stderr, "\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\n"
"we will malloc %#lx bytes.\n", bss_var, ptr_top, evil_size);
void *new_ptr = malloc(evil_size);
fprintf(stderr, "As expected, the new pointer is at the same place as the old top chunk: %p\n", new_ptr - sizeof(long)*2);
void* ctr_chunk = malloc(100);
fprintf(stderr, "\nNow, the next chunk we overwrite will point at our target buffer.\n");
fprintf(stderr, "malloc(100) => %p!\n", ctr_chunk);
fprintf(stderr, "Now, we can finally overwrite that value:\n");
fprintf(stderr, "... old string: %s\n", bss_var);
fprintf(stderr, "... doing strcpy overwrite with \"YEAH!!!\"...\n");
strcpy(ctr_chunk, "YEAH!!!");
fprintf(stderr, "... new string: %s\n", bss_var);
assert(ctr_chunk == bss_var);
}
2. Go to debug
2.1 Build challenge
- Build Docker 16.04 (với libc 2.23) -> chạy vào docker rồi build source code
- Build :
gcc --no-pie chall.c -o chall
2.2 Debug
- Kĩ thuật này nhìn chung khá đơn giản, dễ hiểu nên chúng ta sẽ điểm qua cũng thứ quan trọng nhất.
Quan sát bố cục của chunk:
-
Sau khi malloc lần 1 với size = 0x100
Image Not Showing
Possible Reasons
- The image was uploaded to a note which you don't have access to
- The note which the image was originally uploaded to has been deleted
Learn More →
chunk 1
:
- size : 0x111
- address : 0x2294020
-
Set-up sao cho Top_chunk
là 1 giá trị rất lớn
Image Not Showing
Possible Reasons
- The image was uploaded to a note which you don't have access to
- The note which the image was originally uploaded to has been deleted
Learn More →
- Điều này xuất phát từ cơ chế lấy chunk của hàm
malloc
- hàm malloc sẽ lấy chunk từ
Top chunk
- Khi kích thước của
Top chunk
không còn đủ để việc cấp phát -> OS sẽ sử dụng mmap
để cấp phát bộ nhớ
- Việc overwrite top_chunk thành 1 số rất lớn ( 0xfffffffffffffff ) -> khiến cho
malloc
sẽ không gọi được tới mmap
-
Tính toán size để ở lần malloc kế tiếp có thể malloc được vùng muốn ghi vào
Image Not Showing
Possible Reasons
- The image was uploaded to a note which you don't have access to
- The note which the image was originally uploaded to has been deleted
Learn More →
-> Ở lần malloc
tiếp, chúng ta sẽ có thể lấy chunk từ fake top_chunk
-
malloc chunk (target muốn overwrite)
Image Not Showing
Possible Reasons
- The image was uploaded to a note which you don't have access to
- The note which the image was originally uploaded to has been deleted
Learn More →
Image Not Showing
Possible Reasons
- The image was uploaded to a note which you don't have access to
- The note which the image was originally uploaded to has been deleted
Learn More →
3. Tổng kết :
3.1 Ý tưởng
- Thay đổi top_chunk làm cho malloc() sẽ không gọi được đến mmap khi tổng kích thước các chunk đã vượt quá top_chunk ban đầu -> Khi đó các địa chỉ sẽ được cộng dần và sẽ ghi đè vào các vùng nhớ liền kề với heap.
3.2 Ứng dụng
- Với
Relro
partial hoặc Disable, hoàn toàn có thể malloc đến GOT để overwrite GOT
- Nói chung là modify được các vùng nhớ ghi được : con trỏ hàm, GOT, fini_array, …
- Các dạng bài về Heap OverFlow
II. House Of Orange - Exploit Top_chunk - FSOP
1. Source code và các ràng buộc
- Description: Exploiting the Top Chunk (Wilderness) in order to gain arbitrary code execution
- Referrences: How2heap-HouseOfOrange
source code.c
2. Go to debug
- Trong kĩ thuật này mình debug trên phiên bản libc 2.23
- Ban đầu chúng ta sẽ malloc(0x400-16)

- Tiếp theo sẽ thay đổi
top_chunk
để chuẩn bị malloc 1 chunk > size top_chunk

2.1
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Overwrite top_chunk - Tại sao là 0x7f1 ???
- Điều làm chúng ta thắc mắc chính là size của
top_chunk
= 0x7f1 ?
- Đoạn này mình thử overwrite top_chunk = 0xc01(1 cái size khác) và xem kết quả :
malloc.c:2392: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
- Đọc source libc : assert

Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Yêu cầu điều kiện :
Để hàm assert()
không được gọi thì cần thỏa mãn 1 trong 2 điều kiện
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
TH1 : old_top == initial_top(av) && old_size == 0
- Ta có : old_size = chunksize (old_top)
-> Điều kiện này luôn sai
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
TH2 : ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)
- prev_inuse bật (=1)
- MINSIZE = 0x10
- ((unsigned long) old_end & (pagesize - 1)) == 0)) –––> old_end chia hêt 0x1000
- old_end = (char *) (chunk_at_offset (old_top, old_size));
- chunk_at_offset :
#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s)))

Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Tổng kêt điều kiện :
- Bit prev_inuse = 1
- Size của top_chunk + địa chỉ top_chunk chia hêt 0x1000
- size >= 0x10
put into unsorted bin
- Sau khi đã thỏa mãn các điều kiện, chương trình sẽ gọi int_free() để đưa top_chunk cũ vào unsorted bin

2.2
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Heap overflow exploit unsortedbin
Main_arena
- Trước khi đến với phần tiếp theo để liên kết kiến thức thì chung ta sẽ tìm hiểu 1 kiến thức cực kì quan trọng về Heap chinh là
main_arena
- Heap đưa struct main_arena vào ngay trong bộ nhớ tiến trình. Đây là struct có kiểu malloc_state và chứa các trường sau ( trích từ glibc-2.23/malloc/malloc.c ):
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
mutex_t mutex;
- là 1 số nguyên + glibc dùng để bảo vệ arena khỏi xung đột các thread
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
int flags;
- 1 số nguyên mà main_arena dùng để đánh dấu chinh nó hoặc gán các thuộc tính
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
mfastbinptr fastbinsY[NFASTBINS]
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
mchunkptr top + last_remainder + bins[NBINS * 2 - 2]
- struct : mchunkptr
mchunkptr top
: Đây là con trỏ trỏ tới chunk trên cùng của heap
mchunkptr bins
: Đây là một con trỏ trỏ tới phần đầu của các unsorted bins
unsigned int binmap
: Đây là một danh sách các chỉ mục cho tất cả các bins, chỉ ra bin nào đang trống
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
main_arena trong gdb
- Khi 1 chunk đưa vao
unsorted bin
-> sẽ lưu địa chỉ của main_arena
- Địa chỉ được lưu ở ubin chính là địa chỉ của con trỏ
*top
trong lưu (chính là địa chỉ top_chunk
hiện tại)


- Kích thước của
heap
cũng được mở rộng :



Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Unsortbin attack (HowtoHeap)
- Tuy không phải là kĩ thuật tổ quát hay duy nhất để thực hiện House of Orange nhưng nếu HowtoHeap đã gợi ý thì học luôn vậy. Đâm lao thì theo lao thôi :))
- Unsortbin attack
- Dựa trên kết quả từ Ubin Attack
Trước


Sau

2.3
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
From _IO_flush_all_lockp
to FSOP
- Kĩ thuật này được đề cập trong FSOP-Angel Boy
- Khi malloc gặp lỗi sẽ call
malloc_printerr
-> … -> IO_flush _all_lockp
(là nơi trực tiếp bị khai thác)

_IO_flush_all_lockp
hoạt động như thế nào ?
- Dựa trên source code thì ta có thể hàm này sẽ duyệt qua các IO_FILE và sau đó call
_IO_OVERFLOW (fp, EOF) == EOF)
(đây là 1 con trỏ hàm trong vtable)
Target FSOP
- Sau khi hiểu được workflow, ta có được ý tưởng của kĩ thuật này như sau :

Ta có thể kĩ thuật này sẽ thay đổi chain
của IO_FILE + vtable
để khi call _IO_OVERFLOW
sẽ call hàm mà chúng ta mong muốn
III. UnsortedBin Attack
- Trong quá trình làm kĩ thuật House of Orange mà HowtoHeap đê cập nên nó sẽ bổ trợ của HOO .
- Kĩ thuật xuất phát từ cách dùng lại chunk ở trong UnsortedBin
- Bố trí của Unsortbin chính là 1 DSLK đôi

- Sau khi overwrite bk của chunk trong ubin -> vị trí tại
(bk+0x10) = fd
IV. House Of Tangerine
- Là 1 phiên bản mới hơn so với House Of Orange
- Ý tưởng : overwrite top_chunk thành size phù hợp với fastbin hoặc tcache sau đó đưa top_chunk vào bin.
-> House này thường sẽ có Heap Overflow
-> Ngoài ra còn có thể gây lỗi Use After Free/Double Free
V. Heap consolidation - Off by one


- Byte tràn là
NULL
. Khi kích thước là 0x100, việc tràn byte NULL sẽ làm cho prev_in_usebit
bị xóa, do đó khối trước đó được coi là khối trống
. (1) Lúc này bạn có thể chọn sử dụng phương thức hủy liên kết (xem phần hủy liên kết) để xử lý. (2) Ngoài ra, khi trường prev_size
được bật, bạn có thể giả mạo prev_size
, gây ra sự chồng chéo
giữa các khối. Chìa khóa của phương pháp này là việc hủy liên kết không kiểm tra xem khối cuối cùng của khối được tìm thấy bởi prev_size(về mặt lý thuyết là khối hiện chưa được liên kết) có bằng kích thước khối hiện đang được hủy liên kết hay không.

VI. House of spirit - Fast bin bypass
- Mục tiêu : fake địa chỉ stack, bss, … vào fastbin để ghi vào đó tùy ý.
note
long fake_chunks[10] __attribute__ ((aligned (0x10)));
- long : 64bit
- attribute ((aligned (0x10))) : Địa chỉ bắt đầu của mảng
fake_chunks
sẽ được căn thẳng tới biên có bội số của 16 byte.
fake_chunks[9] = 0x1234
: mục tiêu đoạn này là bypass qua check size của next chunk của fastbin
ý tưởng
- Bản chất cảu skill này là thực hiện fake chunk trên fast bin sao cho chuẩn với các bài test của glibc
misaligned_chunk

- Kiểm tra xem phần địa chỉ
userdata
có chia hết cho 16 hay không
- Lí do cho việc :
__attribute__ ((aligned (0x10)));
next chunk
minsize

- size của next chunk phải có kích thức >= 0x20 (với 64bit)
- Ở phần tính toán
next chunk
thì dựa vào address + size
maxsize

- max_size ≈ 128 KB -> nói chung fake bé bé thôi

VII. House Of Botcake (double free -> tcache to stack)
- Target : fake chunk đến địa chỉ tùy ý (stack, libc,…) dựa trên double free
- Ý tưởng :
- Lấp đầy
tcache
(7 chunk) -> đưa chunk vào unsorted -> sau sẽ tận dụng để gộp chunk trong unsorted
- Đưa 1 chunk vào unsorted -> có thể lấy libc qua UAF, chuẩn bị gộp chunk

- free
chunk c
để b+c gộp lại -> sau này malloc(0x100+0x100) -> control được cả chunk c

- lấy
chunk a
khỏi tcache bằng malloc(0x100) -> tcache còn dư 1 vị trí
- free chunk c (lỗi double free) -> chunk c sẽ nằm ở đỉnh tcache
- control chunk c (control luôn tcache) bằng malloc(0x100+0x100) thì sẽ lấy luôn cả chunk b+c
