Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Binary Exploitation - Heap

Binary Exploitation - Heap

2021/05/26 台科資安社 社課
直播記錄檔(1): https://www.youtube.com/watch?v=I3X69ADZOnw
直播記錄檔(2): https://www.youtube.com/watch?v=ItpJY9Lpw-o
- Heap-Based Buffer Overflow
- UAF
- Double Free
- Hooks
- Fastbin Dup
- Tcache Dup
- Unsafe Unlink

LJP-TW

May 26, 2021
Tweet

More Decks by LJP-TW

Other Decks in Technology

Transcript

  1. # whoami - LJP / LJP-TW - Pwn / Rev

    - NTUST / NCTU / NYCU - 10sec CTF Team 1
  2. Outline - What is Heap? - Tool - Pwngdb -

    基礎知識 – ptmalloc - 基礎知識 – Chunk - 基礎知識 – Fastbin - 基礎知識 – Tcache - Heap-Based Buffer Overflow - UAF - Double Free 2 - Hooks - Fastbin Dup - Tcache Dup - 基礎知識 – Unsorted Bin - 基礎知識 – Consolidate - Unsafe Unlink
  3. What is Heap - 執行時期動態配置的記憶體區段 - 若過於頻繁呼叫 syscall 則會導致程式經常在 Kernel

    / User Mode 切換, 導致效能低落 - 所以許多 Library 實作皆為向 Kernel 申請一大塊記憶體, 並自行 實作一套機制管理這塊記憶體, 去實作切割、 分配、 回收、 合併等 各種操作 5
  4. What is Heap - 各種實作 - Glibc:ptmalloc - Chrome:tcmalloc -

    uClibc-ng:dlmalloc - 這一篇簡報是講 glibc 的 ptmalloc - Libc 2.31 ptmalloc Source Code 6
  5. ptmalloc - 第一次呼叫 malloc 時初始化 main_arena, 並向 Kernel 申請一 大塊記憶體,

    再從這一大塊記憶體分割出一個 chunk, 讓 malloc 回傳給程式 - main_arena 存在於 libc 裡, 紀錄著各種資訊 - 各種 bins 鏈表 - Top chunk 位址 - … - 之後的 malloc/free 都是在分割/回收 chunk, 並利用 main_arena 紀錄的 bins 鏈表管理回收回來的 chunk 11
  6. ptmalloc - 用簡易的例子來幫助想像 (示意圖為簡化過後的版本) 13 Heap char *ptr1 = malloc(0x20);

    char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C 未初始化 main_arena
  7. ptmalloc - 第一次呼叫 malloc, 首先申請一大塊記憶體作為 Heap 14 Heap char *ptr1

    = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C 未初始化 main_arena
  8. ptmalloc - 接著切割 0x20 大小的 Chunk 給 ptr1, 剩下的為 Top

    Chunk 15 Heap char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C 已初始化 main_arena
  9. ptmalloc - Chunk 大小怎麼算後續講解 16 Heap char *ptr1 = malloc(0x20);

    char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C 已初始化 main_arena 0x30 Chunk (Ptr1) Top Chunk
  10. ptmalloc - 從 Top Chunk 切割出 0x20 大小的 Chunk 給

    ptr2 17 Heap char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C 已初始化 main_arena 0x30 Chunk (Ptr1) Top Chunk
  11. ptmalloc - 從 Top Chunk 切割出 0x20 大小的 Chunk 給

    ptr3 18 Heap char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C 已初始化 main_arena 0x30 Chunk (Ptr1) Top Chunk 0x30 Chunk (Ptr2)
  12. ptmalloc - 往 ptr1 (指向第一個 Chunk 的 Chunk Data) 寫入

    0x20 個 A 19 Heap char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C 已初始化 main_arena 0x30 Chunk (Ptr1) Top Chunk 0x30 Chunk (Ptr2) 0x30 Chunk (Ptr3)
  13. ptmalloc - 釋放 ptr1 指向的 Chunk 20 Heap char *ptr1

    = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C 已初始化 main_arena 0x30 Chunk (Ptr1) AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA Top Chunk 0x30 Chunk (Ptr2) 0x30 Chunk (Ptr3)
  14. ptmalloc - 釋放 ptr2 指向的 Chunk 21 Heap char *ptr1

    = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena 0x30 Chunk (Ptr1) AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA Top Chunk 0x30 Chunk (Ptr2) 0x30 Chunk (Ptr3) Bins (垃圾桶鏈表)
  15. ptmalloc - 釋放 ptr3 指向的 Chunk 22 Heap char *ptr1

    = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena 0x30 Chunk (Ptr1) AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA Top Chunk 0x30 Chunk (Ptr2) 0x30 Chunk (Ptr3) Bins (垃圾桶鏈表)
  16. ptmalloc - 之後再度分配同大小的 Chunk 時, 會從鏈表中拿 23 Heap char *ptr1

    = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena 0x30 Chunk (Ptr1) AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA Top Chunk 0x30 Chunk (Ptr2) 0x30 Chunk (Ptr3) Bins (垃圾桶鏈表)
  17. ptmalloc - Chunk 分成 - Allocated Chunk - Free Chunk

    - Top Chunk 24 - Bins 分成 - Fast bin - Small bin - Large bin - Unsorted bin - Tcache
  18. Chunk - 每種 Chunk 有著大同小異的資料結構 - 大致分為 Chunk Header 與

    Chunk Data - 差異在 Chunk Header 26 Chunk Data Chunk Header
  19. Allocated Chunk - prev_size/data: 鄰近的上一個 Chunk 的 size 或 data

    - size: 此 Chunk 的 size - A (NON_MAIN_ARENA bit): 是否由其他的 arena 管理, 而非 main_arena - M (IS_MMAPPED bit): 是否由 mmap 創出來的 - P (PREV_INUSE bit): 鄰近的上一個 Chunk 是否正在使用 27 Ref: https://elixir.bootlin.com/glibc/glibc-2.31/source/malloc/malloc.c#L1076 Chunk Data prev_size / data size P M A size M A 1 data Chunk size 8 Bytes 8 Bytes
  20. Chunk - 要求了 0x20 大小的空間, 實際上分配出的 Chunk 不只 0x20 大

    - 實際計算 Chunk 該多大考慮了以下 - Chunk Header 大小要算進去 - 對齊記憶體 28
  21. Chunk - 實際計算方式如下方截圖 - 加上 SIZE_SZ (8) 保留 Chunk 中放

    size 的空間 - 加上 MALLOC_ALIGN_MASK 後 and ~MALLOC_ALIGN_MASK - 強制進位, 使齊對齊記憶體 - 假設 var = req + 8, 若 var 為 0x21 ~ 0x2f, 則進位為 0x30 - 若 var 為 0x20, 則不進位, 維持 0x20 29 Ref: https://elixir.bootlin.com/glibc/glibc-2.31/source/malloc/malloc.c#L1196
  22. Chunk - 要求了 0x20 大小的空間, 實際上分配出的 Chunk 不只 0x20 大

    - malloc(0x20) -> Chunk Size: 0x30 - malloc(0x28) -> Chunk Size: 0x30 - malloc(0x29) -> Chunk Size: 0x40 - malloc(0x2f) -> Chunk Size: 0x40 - malloc(0x30) -> Chunk Size: 0x40 - malloc(0x38) -> Chunk Size: 0x40 30 Ref: https://elixir.bootlin.com/glibc/glibc-2.31/source/malloc/malloc.c#L1196
  23. Allocated Chunk - 來個栗子 - malloc(0x20) 後寫入 0x20 個 A

    - 計算 Chunk Size: (0x20 + 0x8 + 0xf) & ~0xf = 0x30 - A bit 為 0, 表示在 main_arena - M bit 為 0, 表示非 mmap 分配 - P bit 為 1, 表示鄰近的上一塊 Chunk 正在使用中 31 AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA (Chunk data) XXXXXXXX (prev_size / data) 0x31 (size) 0x20ce1 (size) XXXXXXXX (data) Chunk size 8 Bytes 8 Bytes
  24. Allocated Chunk - Chunk 正在使用中, 圈選處目前作為 Data 32 AAAAAAAA AAAAAAAA

    AAAAAAAA AAAAAAAA (Chunk data) XXXXXXXX (prev_size / data) 0x31 (size) 0x20ce1 (size) XXXXXXXX (data) Chunk size 8 Bytes 8 Bytes
  25. P 0xe1 -> 0b11100001 Allocated Chunk - 下一個 Chunk 的

    P bit 為 1, 表示其鄰近的上一塊 Chunk 目前正在使用中 33 AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA (Chunk data) XXXXXXXX (prev_size / data) 0x31 (size) 0x20ce1 (size) XXXXXXXX (data) Chunk size 8 Bytes 8 Bytes M A
  26. Allocated Chunk - 來個不同的栗子 - malloc(0x28) 後寫入 0x28 個 A

    - 計算 Chunk Size: (0x28 + 0x8 + 0xf) & ~0xf = 0x30 34 AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA (Chunk data) XXXXXXXX (prev_size / data) 0x31 (size) 0x20ce1 (size) AAAAAAAA (data) Chunk size 8 Bytes 8 Bytes
  27. Allocated Chunk - Chunk 正在使用中, 圈選處目前作為 Data 35 AAAAAAAA AAAAAAAA

    AAAAAAAA AAAAAAAA (Chunk data) XXXXXXXX (prev_size / data) 0x31 (size) 0x20ce1 (size) AAAAAAAA (data) Chunk size 8 Bytes 8 Bytes
  28. P 0xe1 -> 0b11100001 Allocated Chunk - 下一個 Chunk 的

    P bit 為 1, 表示其鄰近的上一塊 Chunk 目前正在使用中 36 AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA (Chunk data) XXXXXXXX (prev_size / data) 0x31 (size) 0x20ce1 (size) AAAAAAAA (data) Chunk size 8 Bytes 8 Bytes M A
  29. Allocated Chunk - 再來個不同的栗子 - malloc(0x29) 後寫入 0x29 個 A

    - 計算 Chunk Size: (0x29 + 0x8 + 0xf) & ~0xf = 0x40 37 AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA AXXXXXXX (Chunk data) XXXXXXXX (prev_size / data) 0x41 (size) 0x20ce1 (size) XXXXXXXX (data) Chunk size 8 Bytes 8 Bytes
  30. Allocated Chunk - Chunk 正在使用中, 圈選處目前作為 Data 38 AAAAAAAA AAAAAAAA

    AAAAAAAA AAAAAAAA AAAAAAAA AXXXXXXX (Chunk data) XXXXXXXX (prev_size / data) 0x41 (size) 0x20ce1 (size) XXXXXXXX (data) Chunk size 8 Bytes 8 Bytes
  31. P 0xe1 -> 0b11100001 Allocated Chunk - 下一個 Chunk 的

    P bit 為 1, 表示其鄰近的上一塊 Chunk 目前正在使用中 39 AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA AXXXXXXX (Chunk data) XXXXXXXX (prev_size / data) 0x41 (size) 0x20ce1 (size) XXXXXXXX (data) Chunk size 8 Bytes 8 Bytes M A
  32. Free Chunk - Free 掉後的 Chunk 會根據 Size 而進到不同的 Bins

    中 - fd: Forward Pointer, 指向下一塊 Free 的 Chunk - bk: Backward Pointer, 指向上一塊 Free 的 Chunk - 以 fd, bk 將各個 Free Chunk 串聯起來 40 Chunk Data (沒有實際作用) prev_size / data size P M A size M A 0 prev_size Chunk size 8 Bytes 8 Bytes fd bk
  33. Free Chunk - 若是 Free Chunk, 則圈選處作為 prev_size - 下一塊

    Chunk 通過 P 為 0 得知上一塊 Chunk 是 Free Chunk - 下一塊 Chunk 通過 prev_size 得知上一塊 Chunk 大小 41 Chunk Data (沒有實際作用) prev_size / data size P M A size M A 0 prev_size Chunk size 8 Bytes 8 Bytes fd bk
  34. Top Chunk - 在 Heap 頂端的 Chunk, 代表著剩餘的空間 42 Free

    Space prev_size / data size 1 0 0 Chunk size 8 Bytes 8 Bytes
  35. Fastbin - Free 掉 Chunk Size 小於等於 global_max_fast 的 Chunk,

    會回 收至 Fastbin - global_max_fast 預設為 0x80 - Fastbin 共有 7 個, 分別為 [0x20, 0x30, 0x40, …, 0x80] - 為 singly linked list - e.g. - Free 掉 Chunk Size 為 0x20 的 Chunk, 會進到代表 0x20 Fastbin 的鏈表 - Free 這類 Chunk 時, 不會清除下一塊 Chunk 的 P bit 44
  36. Fastbin - 釋放 ptr1 指向的 Chunk, 首先先把圖改詳細一點 45 Heap char

    *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena.fastbinsY 0x30 Chunk (Ptr1) AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA Top Chunk 0x30 Chunk (Ptr2) 0x30 Chunk (Ptr3) 0x20 bins 0x30 bins 0x40 bins 0x50 bins 0x60 bins 0x70 bins 0x80 bins
  37. Fastbin - 釋放 ptr1 指向的 Chunk 46 Heap char *ptr1

    = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena.fastbinsY Top Chunk 0x20 bins 0x30 bins 0x40 bins 0x50 bins 0x60 bins 0x70 bins 0x80 bins 0 0x31 0 0x31 0x30 Chunk (Ptr3) 0 0 0 0 AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA
  38. Fastbin - 入鏈, 向 fd 寫入 list head 後, 將

    list head 指向該 Chunk 47 char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena.fastbinsY 0x20 bins 0x30 bins 0x40 bins 0x50 bins 0x60 bins 0x70 bins 0x80 bins Heap Top Chunk 0 0x31 0 0x31 0x30 Chunk (Ptr3) 0 0 0 0 0 AAAAAAAA AAAAAAAA AAAAAAAA
  39. Fastbin - 可以注意到下一塊 Chunk 的 P bit 並沒有被清除 48 char

    *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena.fastbinsY 0x20 bins 0x30 bins 0x40 bins 0x50 bins 0x60 bins 0x70 bins 0x80 bins Heap Top Chunk 0 0x31 0 0x31 0x30 Chunk (Ptr3) 0 0 0 0 0 AAAAAAAA AAAAAAAA AAAAAAAA
  40. Fastbin - 繼續 free ptr2 指向的 Chunk 49 char *ptr1

    = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena.fastbinsY 0x20 bins 0x30 bins 0x40 bins 0x50 bins 0x60 bins 0x70 bins 0x80 bins Heap Top Chunk 0 0x31 0 0x31 0 0 0 0 0 AAAAAAAA AAAAAAAA AAAAAAAA 0 0x31 0 0 0 0
  41. Fastbin - 入鏈, 向 fd 寫入 list head 後, 將

    list head 指向該 Chunk 50 char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena.fastbinsY 0x20 bins 0x30 bins 0x40 bins 0x50 bins 0x60 bins 0x70 bins 0x80 bins Heap Top Chunk 0 0x31 0 0x31 &Chunk1 0 0 0 0 AAAAAAAA AAAAAAAA AAAAAAAA 0 0x31 0 0 0 0
  42. Fastbin - 繼續 free ptr3 指向的 Chunk 51 char *ptr1

    = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena.fastbinsY 0x20 bins 0x30 bins 0x40 bins 0x50 bins 0x60 bins 0x70 bins 0x80 bins Heap Top Chunk 0 0x31 0 0x31 &Chunk1 0 0 0 0 AAAAAAAA AAAAAAAA AAAAAAAA 0 0x31 0 0 0 0
  43. Fastbin - 入鏈, 向 fd 寫入 list head 後, 將

    list head 指向該 Chunk 52 char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); C main_arena.fastbinsY 0x20 bins 0x30 bins 0x40 bins 0x50 bins 0x60 bins 0x70 bins 0x80 bins Heap Top Chunk 0 0x31 0 0x31 &Chunk1 0 0 0 0 AAAAAAAA AAAAAAAA AAAAAAAA 0 0x31 &Chunk2 0 0 0
  44. Fastbin - 若後續 malloc 的 Chunk Size 為 0x30 53

    char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C main_arena.fastbinsY 0x20 bins 0x30 bins 0x40 bins 0x50 bins 0x60 bins 0x70 bins 0x80 bins Heap Top Chunk 0 0x31 0 0x31 &Chunk1 0 0 0 0 AAAAAAAA AAAAAAAA AAAAAAAA 0 0x31 &Chunk2 0 0 0
  45. Fastbin - 則從 0x30 Fastbin 拿出一個 Chunk 54 char *ptr1

    = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C main_arena.fastbinsY 0x20 bins 0x30 bins 0x40 bins 0x50 bins 0x60 bins 0x70 bins 0x80 bins Heap Top Chunk 0 0x31 0 0x31 &Chunk1 0 0 0 0 AAAAAAAA AAAAAAAA AAAAAAAA 0 0x31 &Chunk2 0 0 0
  46. Fastbin - 小總結 - LIFO - fd 指向下一塊 Free Chunk

    的 Chunk Header - 不會改鄰近的下一塊 Chunk 的 P bit - 在 free 時, 如果下一塊是 Top Chunk, 並不會被合併進去 55
  47. Tcache - 從 libc 2.26 開始使用 - 為了再加速程式效率而誕生 - Tcache

    有許多, 分別為 [0x20, 0x30, 0x40, …, 0x410] - 為 singly linked list - 每個 Tcache 最多收 7 個 Chunks - Free 這類 Chunk 時, 不會清除下一塊 Chunk 的 P bit - 用結構 tcache_perthread_struct 管理 Tcache - 指向此結構的指標存在於 TLS 中 59
  48. Tcache - 第一次呼叫 malloc, 首先申請一大塊記憶體作為 Heap 60 Heap char *ptr1

    = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 未初始化 main_arena NULL tcache libc TLS
  49. Tcache - 接著初始化 tcache_perthread_struct 61 Heap char *ptr1 = malloc(0x20);

    char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena NULL tcache
  50. Tcache - 接著初始化 tcache_perthread_struct, 展開看一下內部 62 Heap char *ptr1 =

    malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291
  51. Tcache - tcache_perthread_struct 分成 Counts 和 Entries 63 Heap char

    *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 0x30 Cnt 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry ……
  52. Tcache - 初始化都做完後才分配 Chunk 64 Heap char *ptr1 = malloc(0x20);

    char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 0x30 Cnt 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry ……
  53. Tcache - 分配 Chunk2 65 Heap char *ptr1 = malloc(0x20);

    char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 0x30 Cnt 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry …… 0 0x31 Chunk1 Data
  54. Tcache - 分配 Chunk3 66 Heap char *ptr1 = malloc(0x20);

    char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 0x30 Cnt 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry …… 0 0x31 Chunk1 Data 0 0x31 Chunk2 Data
  55. Tcache - 寫入 0x20 個 A 至 Chunk1 67 Heap

    char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 0x30 Cnt 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry …… 0 0x31 Chunk1 Data 0 0x31 Chunk2 Data 0 0x31 Chunk3 Data
  56. Tcache - Free Chunk1 68 Heap char *ptr1 = malloc(0x20);

    char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 0x30 Cnt 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry …… 0 0x31 0 0x31 Chunk2 Data 0 0x31 Chunk3 Data AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA
  57. Tcache - 填入 fd, 此時 bk 不代表 bk, 而是代表 Key,

    用作於安全檢查 69 Heap char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 1 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry …… 0 0x31 0 0x31 Chunk2 Data 0 0x31 Chunk3 Data 0 tcache AAAAAAAA AAAAAAAA
  58. Tcache - Free Chunk2 70 Heap char *ptr1 = malloc(0x20);

    char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 1 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry …… 0 0x31 0 0x31 Chunk2 Data 0 0x31 Chunk3 Data 0 tcache AAAAAAAA AAAAAAAA
  59. Tcache - Free Chunk3 71 Heap char *ptr1 = malloc(0x20);

    char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 2 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry …… 0 0x31 0 0x31 0 0x31 Chunk3 Data 0 tcache AAAAAAAA AAAAAAAA &Chunk1 tcache AAAAAAAA AAAAAAAA
  60. &Chunk2 tcache AAAAAAAA AAAAAAAA Tcache - 再度分配 Chunk size 0x30

    72 Heap char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 3 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry …… 0 0x31 0 0x31 0 0x31 0 tcache AAAAAAAA AAAAAAAA &Chunk1 tcache AAAAAAAA AAAAAAAA
  61. &Chunk2 tcache AAAAAAAA AAAAAAAA Tcache - 從 Tcache 拿出 Size

    剛好的 Chunk 73 Heap char *ptr1 = malloc(0x20); char *ptr2 = malloc(0x20); char *ptr3 = malloc(0x20); memset(ptr1, 'A', 0x20); free(ptr1); free(ptr2); free(ptr3); char *ptr4 = malloc(0x20); C 已初始化 main_arena tcache tcache_perthread_struct 0 0x291 tcache_perthread_struct 0x20 Cnt 2 0x40 Cnt 0x50 Cnt …… 8 Bytes 0x20 Entry 0x30 Entry …… 0 0x31 0 0x31 0 0x31 0 tcache AAAAAAAA AAAAAAAA &Chunk1 tcache AAAAAAAA AAAAAAAA
  62. Tcache - 小總結 - 跟 Fastbin 很像 - LIFO -

    fd 指向下一塊 Free Chunk 的 Chunk Data - 不會改鄰近的下一塊 Chunk 的 P bit - 在 free 時, 如果下一塊是 Top Chunk, 並不會被合併進去 - Size range 比 Fastbin 大很多 - 每個 Tcache 最多只能裝 7 個 Chunk - Fastbin 的 fd 是指到 Chunk Header; Tcache 的 fd 是指到 Chunk Data 74
  63. Heap-Based Buffer Overflow 81 0 0x41 AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA

    AAAAAAAA AAAAAAAA Admin\0\0\0 \0\0\0\0\0\0\0\2 AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAAA AAAAAAAA AAAAAAAA
  64. UAF - UAF 全名 Use-After-Free - 把一個指標當作參數傳給 free, 會釋放指標指向的 Chunk

    - 此時指標變成 dangling pointer - 後續程式碼又從此指標寫入/讀取資料, 就是 UAF 84
  65. Double Free - Free 同一塊 Chunk 兩次 - 若成功, 則鏈表上會出現此

    Chunk 兩次 - 再一次 malloc, 得到此 Chunk, 此時這個 Chunk 在鏈表中還是 存在 - 薛丁格的 Chunk: 是 Alllocated Chunk, 也是 Free Chunk 91
  66. Double Free - 觀察一下鏈表 96 0 0x41 &Chunk2 0 0x41

    &Chunk1 Bin Bin Chunk1 Chunk2 Chunk1 Chunk2
  67. Hooks - 在 malloc/free/calloc/realloc 進到主要分配演算法之前, 若有 設定 hook function, 則會先執行

    hook function - 在寫 exploit 時, 是個很好的利用對象 - 朝 hook function 中寫入, 就能控制執行流程 - 寫入 One Gadget 就能得到 shell 99
  68. Fastbin Dup - 用 Double Free 使 Chunk 在 Fastbin

    中兩次 - Malloc 得到 Chunk 後, 將 fd 改寫成任意位址 - 再次 malloc, 取得位在剛剛改寫位址的 Chunk - 效力形同任意寫入 103
  69. Fastbin dup - Free 掉 Chunk2 105 0 0x31 0

    Fastbin 0 0x31 Fastbin Chunk1 NULL
  70. Fastbin dup - 再次 free 掉 Chunk1 106 0 0x31

    0 Fastbin 0 0x31 &Chunk1 Fastbin Chunk2 Chunk1 NULL
  71. Fastbin dup - 觀察一下鏈表 107 0 0x31 &Chunk2 Fastbin 0

    0x31 &Chunk1 Fastbin Chunk1 Chunk2 Chunk1 Chunk2 …
  72. Fastbin dup - Malloc 過後取得 Chunk1, Chunk2 遞補 108 0

    0x31 &Chunk2 Fastbin 0 0x31 &Chunk1 Fastbin Chunk1 Chunk2 Chunk1 Chunk2 …
  73. Fastbin dup - 取得 Chunk1 後, 向其寫入 malice 109 0

    0x31 malice Fastbin 0 0x31 &Chunk1 ???????? 0x31 Fastbin Chunk2 Chunk1 malice ????????
  74. Fastbin dup - 再次 malloc 過後取得 Chunk2, Chunk1 遞補 110

    0 0x31 malice Fastbin 0 0x31 &Chunk1 ???????? 0x31 Fastbin Chunk2 Chunk1 malice ????????
  75. Fastbin dup - 取得 Chunk2 後, 向其寫入什麼無所謂 111 0 0x31

    malice Fastbin 0 0x31 AAAAAAAA AAAAAAAA ???????? 0x31 Fastbin Chunk1 malice ????????
  76. Fastbin dup - 再次 malloc 過後取得 Chunk1, malice 遞補 112

    0 0x31 malice Fastbin 0 0x31 AAAAAAAA AAAAAAAA ???????? 0x31 Fastbin Chunk1 malice ????????
  77. Fastbin dup - 這次取得 Chunk1 後, 向其寫入什麼無所謂 113 0 0x31

    BBBBBBBB BBBBBBBB Fastbin 0 0x31 AAAAAAAA AAAAAAAA ???????? 0x31 Fastbin malice ????????
  78. Fastbin dup - 再次 malloc 過後取得 malice, ???????? 遞補 114

    0 0x31 BBBBBBBB BBBBBBBB Fastbin 0 0x31 AAAAAAAA AAAAAAAA ???????? 0x31 Fastbin malice ????????
  79. Fastbin dup - 這次取得位於 malice 的 Chunk 後, 向其寫入想寫的值 115

    0 0x31 BBBBBBBB BBBBBBBB Fastbin 0 0x31 AAAAAAAA AAAAAAAA ???????? 0x31 PAYLOAD1 PAYLOAD2 PAYLOAD3 PAYLOAD4 Fastbin ????????
  80. Fastbin Dup - 可以將目標地址設定為 Return Address 或 Hook Function 這類

    可控執行流程的位址 - 注意 fastbin fd 是指向 Chunk Header - Malicious Chunk 的 Size 須符合 Fastbin 所屬的 Size - 在 libc 2.26 後, Fast Chunk 會先進 Tcache 116
  81. Fastbin Dup - Libc 2.23 - _int_free - 有幾道安全檢查 -

    檢查鄰近的下一塊 Chunk size 是否合理 - 0x10 < Size < av->system_mem 118
  82. Fastbin Dup - Libc 2.23 - _int_free - 有幾道安全檢查 -

    鏈上的第一個 Free Chunk 若和目前要 free 的 Chunk 一樣, 則為 Double Free - 繞過方式: 再 free 同塊 Chunk 之前, 先 free 其他 Chunk, 這 就是示意圖中 Chunk2 存在的 意義 119
  83. Fastbin Dup - Libc 2.23 - _int_malloc - 檢查拿出的 Chunk

    Size 是否和 Fastbin 所屬的 Size 相同 120
  84. Fastbin Dup - Libc 2.27 - _int_free - Fastbin 的部分基本相同

    - Tcache 裝滿 7 個 Fast Chunk 後, 才會往 Fastbin 放 121
  85. Fastbin Dup - Libc 2.27 - _int_malloc - 一樣會檢查拿出的 Chunk

    是否屬於此 Fastbin - 須注意的是, 若 Tcache 沒 放滿, 則會把 Fastbin 中的 Chunk 放入 Tcache 122
  86. Fastbin Dup - Libc 2.31 則和 Libc 2.27 差不多 -

    統整以上, Fastbin Dup 在以上版本皆能打 - 在支援 Tcache 的 Libc 版本需考慮 Tcache 要放滿 - Q: 在 Tcache 放滿的情況下, malloc 不會拿 fastbin 而是拿 Tcache, 那 Fastbin Dup 怎打? - A: 利用 calloc 不會拿 Tcache 的特性繞過 123
  87. Tcache Dup - 和 Fastbin Dup 很像 - 用 Double

    Free 使 Chunk 在 Tcache 中兩次 - Malloc 得到 Chunk 後, 將 fd 改寫成任意位址 - 再次 malloc, 取得位在剛剛改寫位址的 Chunk - 效力形同任意寫入 - 在 libc 2.26 ~ libc 2.28 中可使用, 之後的版本有加安全檢查 - libc 2.27 中, 並無檢查鏈上第一個 Chunk 是否就是要被 free 的 Chunk, 因此可以直接 Free 自己 - 利用上更簡單 126
  88. Tcache dup - Malloc 取得 Chunk1, 從鏈上取走 Chunk1, Chunk1 遞補

    130 0 0x41 &Chunk1 Tcache Tcache Chunk1 Chunk1 …
  89. Tcache dup - 朝 malloc 回傳的 ptr 寫入 malice 131

    0 0x41 &Chunk1 Tcache Tcache Chunk1 Chunk1 …
  90. Tcache dup - 再次 malloc,從鏈上取走 Chunk1, malice 遞補 133 0

    0x41 &malice Tcache ???????? Tcache Chunk1 malice ????????
  91. Tcache dup - 重點是下次 malloc 回傳的 ptr 就是指向 malice 135

    0 0x41 AAAAAAAA AAAAAAAA Tcache ???????? Tcache malice ????????
  92. Tcache dup - 這次寫入資料就是往 malice 寫入 136 0 0x41 AAAAAAAA

    AAAAAAAA Tcache PAYLOAD1 PAYLOAD2 Tcache malice ????????
  93. Tcache Dup - 在 libc 2.29 後加上了安全檢查 - 相對於 Fastbin

    fd 是指向 Chunk Header, Tcache fd 是直接指 向 Chunk Data, 好用 137
  94. Tcache Dup - Libc 2.31 - _int_free 多了檢查 - 被

    free 過的 Chunk, key 會寫入 tcache - 若要 free 的 Chunk key 為 tcache, 則懷疑他已被 free 過一次, 遍尋 tcache list 檢查是否已被 free - 若能改寫 key 就能繞過 141
  95. Tcache Dup - Libc 2.31 - _int_malloc 基本沒變 - 檢查

    counts 的方式從 != NULL 改為 > 0 142
  96. Unsorted Bin - 被 free 的 Chunk 沒被放到 Tcache 和

    Fastbin 時, 則 - 若鄰近的上一塊 Chunk 為 Free, 則合併到它裡 - 若鄰近的下一塊 Chunk 是 Top Chunk, 則合併到 Top Chunk 裡 - 若下一塊不是, 那再看它是不是 Free, 是則合併進此 Chunk 裡, 並加到 Unsorted Bin 鏈表中 - Unsorted Bin 目的是給被回收的 Chunk 至少一次的機會再被分 配出去 - Unsorted Bin 為 Circular Doubly Linked List 145
  97. Unsorted Bin 146 fd bk unsorted_chunks (main_arena) fd bk size

    prev_size/data data fd bk size prev_size/data data size prev_size/data
  98. Unsorted Bin 147 bins[0] bins[1] unsorted_chunks (main_arena) fd bk size

    prev_size/data data fd bk size prev_size/data data last_remainder top
  99. Consolidate 151 - 大致有三種合併方式 - Backward consolidate - Forward consolidate

    - malloc_consolidate - 有兩塊 Free Chunk 要合併 成一塊, 調整其中一塊的大小, 並將另一塊從鏈表中移除
  100. Consolidate - unlink_chunk - 檢查 p 的 size 跟下一塊 Chunk

    紀錄的 prev_size 是否一樣 - fd = p->fd - bk = p->bk - 檢查 fd->bk 為 p, 且 bk->fd 為 p - fd->bk = bk - bk->fd = fd 152 fd bk fd bk fd bk p fd bk fd bk fd bk fd bk p fd bk
  101. Unsafe Unlink - 越界寫成以下後, free(q) 157 0 ptr 0x431 0

    0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q
  102. Unsafe Unlink - free(q), q 的 size 不進入 fastbin &

    Tcache, 並由於 q 的 Prev_inuse 為 0, 進行往前合併 158 0 ptr 0x431 0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q
  103. Unsafe Unlink - 由於我們設置 prev_size 為 0x420, 所以 p 的指向如圖所示

    159 0 ptr 0x431 0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q
  104. Unsafe Unlink - unlink_chunk(av, p) 160 0 ptr 0x431 0

    0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q
  105. Unsafe Unlink - 檢查 p 的 size 跟下一塊 Chunk 紀錄的

    prev_size 是否一樣 - OK 161 0 ptr 0x431 0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q
  106. Unsafe Unlink - fd = p->fd 162 0 ptr 0x431

    0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q fd
  107. Unsafe Unlink - bk = p->bk 163 0 ptr 0x431

    0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q fd bk
  108. Unsafe Unlink - 檢查 fd->bk 為 p - OK 164

    0 (prev_size/data) (size) (fd) ptr (bk) 0x431 0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q fd bk
  109. Unsafe Unlink - 檢查 bk->fd 為 p - OK 165

    0 (prev_size/data) (size) ptr (fd) 0x431 0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q fd bk
  110. Unsafe Unlink - fd->bk = bk 166 0 (prev_size/data) (size)

    (fd) ptr (bk) 0x431 0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q fd bk
  111. Unsafe Unlink - bk->fd = fd 167 0 (prev_size/data) (size)

    ptr (fd) 0x431 0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q fd bk
  112. Unsafe Unlink - ptr 作為資料 pointer, 通常能再次寫入 - 正常使用時, ptr

    應只指向 heap, 然而攻擊後會指向 &ptr – 0x18 168 0 (prev_size/data) (size) ptr (fd) 0x431 0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q fd bk
  113. Unsafe Unlink - 再次往 ptr 寫入就能再蓋 ptr, 就能製造任意位置寫入 169 0

    (prev_size/data) (size) ptr (fd) 0x431 0 0x421 &ptr-0x18 &ptr-0x10 0x420 0x430 0 0 q fd bk