# Trigger double free to get shell
# I. Source code
- Trong quá trình giải các chall về heap thì tự nhiên mình bắt gặp 1 hiện tượng "lạ" : Khi mình gây lỗi DBF trên fastbin(với TH "fast top" ) thì vô tình lại gọi hàm trong __malloc_hook . Và mình đặt ra câu hỏi tại sao??? -> Chính vì vậy quyết định debug kĩ hơn đoạn này.
- Source debug:
```clike
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <malloc.h>
int main() {
void *chunk = malloc(0x10);
free(chunk) ;
free(chunk) ;
return 0;
}
// gcc chall.c -o chall
```
-> Chương trình mình sẽ build ở 2 phiên bản libc khác nhau để debug so sánh (tcache và fastbin) gồm : libc2.23 và libc2.27
# II. DEBUG
## 1. Glibc 2.27 < demo trên tcache >
- Chạy thử ctrinh:

- Chương trình băt đầu chạy và mình sẽ thay đổi __malloc__hook sau khi gọi malloc (tránh malloc gọi __malloc_hook)

- Trước tiên hãy thử review qua [source code](https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4459):
```clike
if (__glibc_unlikely (e->key == tcache_key))
{
tcache_entry *tmp;
size_t cnt = 0;
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
for (tmp = tcache->entries[tc_idx];
tmp;
tmp = REVEAL_PTR (tmp->next), ++cnt)
{
if (cnt >= mp_.tcache_count)
malloc_printerr ("free(): too many chunks detected in tcache");
if (__glibc_unlikely (!aligned_OK (tmp)))
malloc_printerr ("free(): unaligned chunk detected in tcache 2");
if (tmp == e)
malloc_printerr ("free(): double free detected in tcache 2");
/* If we get here, it was a coincidence. We've wasted a
few cycles, but don't abort. */
}
}
```
-> Dựa vao output khi run binary và source thì mình sẽ debug từ `malloc_printerr ("free(): double free detected in tcache 2");`
- Trong GDB (ở lần free thứ 2)

- Đọc source [malloc_printerr](https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5661)
```clike
malloc_printerr (const char *str)
{
#if IS_IN (libc)
__libc_message (do_abort, "%s\n", str);
#else
__libc_fatal (str);
#endif
__builtin_unreachable ();
}
```

- hàm [__libc_message](https://elixir.bootlin.com/glibc/glibc-2.35/source/sysdeps/posix/libc_fatal.c#L57)
```clike
...
if (nlist > 0)
{
struct iovec *iov = alloca (nlist * sizeof (struct iovec));
ssize_t total = 0;
for (int cnt = nlist - 1; cnt >= 0; --cnt)
{
iov[cnt].iov_base = (char *) list->str;
iov[cnt].iov_len = list->len;
total += list->len;
list = list->next;
}
WRITEV_FOR_FATAL (fd, iov, nlist, total);
...
```
-> Đây là đoạn trực tiếp in ra message cho mình

- Đến 1 hàm quan trọng khác là [abort](https://elixir.bootlin.com/glibc/glibc-2.35/source/stdlib/abort.c#L48)


- Bên trong hàm abort thì chúng ta hãy quan tâm đên hàm [raise](https://elixir.bootlin.com/glibc/glibc-2.35/source/sysdeps/posix/raise.c#L24) ; hàm này sẽ gọi `__pthread_kill` và hàm này sẽ kill chương trình của mình
```clike
raise (int sig)
{
int ret = __pthread_kill (__pthread_self (), sig);
if (ret != 0)
{
__set_errno (ret);
ret = -1;
}
return ret;
}
```

- Ta có thể dùng lệnh `backtrace` tromg pwndbg :

## 2. Glibc 2.23 < debug fastbin >
- Debug tương tự như trên mình sẽ debug cùng với việc đọc source code libc
:::info
- Recommend sử dụng IDA để reverse + kết hợp GDB để tìm được hàm đúng( vì libc 1 hàm có thể nằm nhiều thư mục)
:::
- Ngoài ra mình sử dụng backtrace để xem workflow của chương trình.


-> Ta có thể thấy chương trình gọi hàm `malloc` mà trước đấy mình đã overwrite __malloc__hook -> ta thấy chương trình đáng `jmp` tới hàm ở `__malloc_hook`
- Check trong libc :

-> offset = 0x842f3

