Day26-Heap overflow

前言 前兩天介紹了 Use After Free 的概念和利用方式,今天要介紹另一種 Overflow,稱為 Heap Overflow。 Heap overflow Heap Overflow 簡單來說就是發生在 heap 段的 buffer overflow。與 Stack Overflow 不同,它通常無法直接控制程式的執行流程。取而代之的是,攻擊者可以間接透過覆蓋下一個 chunk 的 header,利用 malloc 或 free 的行為,達成任意位置寫入,進而間接控制執行流程。 如何攻擊 攻擊的方式是透過 Overflow 覆蓋已經 free 掉的 chunk 中的 fd 和 bk,然後利用前面提到的 unlink 機制(即 fd->bk = BK 和 BK->fd = FD)來更改記憶體位置。具體來說,unlink 的程式碼如下: 1unlink(P,BK,FD){ 2 FD = P->fd; 3 BK = P->bk; 4 FD->bk = BK; 5 BK->fd = FD; 6} 在較舊的 libc 版本中,由於檢查機制較少,可以透過一些步驟構造出成功開啟 shell 的 chain,步驟如下: ...

2024-10-10 · 1 min · 161 words · YJK

Day25-Use After Free Adv

前言 昨天的題目是透過直接將執行流程改為我們事先設計好的後門。但如果我們沒有設置後門,其實在早期版本的 libc 中,仍然可以使用其他方式 leak 出 libc,並進一步將執行流程轉向 one_gadget 或是直接使用 system 開啟 shell。 利用方式 在 libc 2.23 版本中,我們可以透過一些技巧來 leak 出 libc base,甚至進而控制執行流程。過程簡單來說就是: 先 malloc 一塊 unsorted bin 大小的記憶體空間。 再申請一塊較小的空間,以防止後續 free 掉 unsorted bin 大小的空間時,該 chunk 與 top chunk 合併。 接著使用 Use After Free 技巧,先 free 掉 unsorted bin 大小的空間,再輸出該空間的內容,如此就能 leak 出 libc 的地址。 之後我們可以減去 offset,拿到 libc base 地址。 接著 malloc 一塊 fastbin 大小的 chunk,並修改它的 fd,將滿足條件的地址放進 fastbin。 最後,malloc 一塊相同大小的 chunk 並使用它。如果將它修改為 one_gadget,即可輕鬆拿到 shell。 之所以會使用到 __malloc_hook,是因為它與這個過程的密切關聯。 ...

2024-10-09 · 1 min · 139 words · YJK

Day24-Use After Free

前言 在前面文章中,我們討論了 Glibc 的 malloc 和 free 流程,並介紹了一些相關的名詞。接下來,我們將探討與 Heap 相關的漏洞,而第一個要介紹的漏洞是 Use After Free。 Use After Free 顧名思義,Use After Free 指的是使用已經被 free 掉的指標(pointer)。問題的根源在於 dangling pointer。當一個指標被 free 之後,如果沒有將其設為 NULL,就會產生 dangling pointer。 Use After Free 的利用方式會隨著使用的情境有所不同,可能導致: 任意位置讀取或寫入 間接影響程式的控制流程 此外,它也可能被用來 leak 記憶體中的殘值。同樣的,另一個常見的 Heap 漏洞——double free,也是因為 dangling pointer 的存在,導致多次 free 相同的記憶體區塊。這些漏洞都可以通過特定技巧加以利用。 Lab 查看以下原始碼: 1#include<stdio.h> 2#include<stdlib.h> 3 4struct Note{ 5 void (*printnote_content)(); 6 char *content; 7}; 8 9struct Note *noteList[10]; 10int noteCount = 0; 11 12void printnote_content(struct Note *this){ 13 printf("%s\n", this->content); 14} 15 16void add_note(){ 17 int i,size; 18 if(noteCount >= 10){ 19 printf("No more space for new note\n"); 20 return; 21 } 22 for(i=0; i < 10; i++){ 23 if(noteList[i] == NULL){ 24 noteList[i] = (struct Note *)malloc(sizeof(struct Note)); 25 if(noteList[i] == NULL){ 26 printf("Memory allocation failed\n"); 27 exit(1); 28 } 29 noteList[i]->printnote_content = printnote_content; 30 printf("Enter the size of the note: "); 31 scanf("%d", &size); 32 noteList[i]->content = (char *)malloc(size); 33 if(noteList[i]->content == NULL){ 34 printf("Memory allocation failed\n"); 35 exit(1); 36 } 37 printf("Enter the content of the note: "); 38 read(0, noteList[i]->content, size); 39 noteCount++; 40 break; 41 } 42 } 43} 44 45void delete_note(){ 46 int index; 47 printf("Enter the index of the note: "); 48 scanf("%d", &index); 49 if(index < 0 || index >= noteCount){ 50 printf("Invalid index\n"); 51 exit(1); 52 } 53 if(noteList[index] != NULL){ 54 free(noteList[index]->content); 55 free(noteList[index]); 56 printf("Note deleted\n"); 57 } 58} 59 60void print_note(){ 61 int index; 62 printf("Enter the index of the note: "); 63 scanf("%d", &index); 64 if(index < 0 || index >= noteCount){ 65 printf("Invalid index\n"); 66 exit(1); 67 } 68 if(noteList[index] != NULL){ 69 noteList[index]->printnote_content(noteList[index]); 70 } 71} 72 73void backdoor(){ 74 system("/bin/sh"); 75} 76 77void menu(){ 78 printf("1. Add note\n"); 79 printf("2. Delete note\n"); 80 printf("3. Print note\n"); 81 printf("4. Exit\n"); 82 printf("Enter your choice: "); 83} 84 85int main(){ 86 setvbuf(stdout, 0, 2, 0); 87 setvbuf(stdin, 0, 2, 0); 88 setvbuf(stderr, 0, 2, 0); 89 while(1){ 90 menu(); 91 int choice; 92 scanf("%d", &choice); 93 switch(choice){ 94 case 1: 95 add_note(); 96 break; 97 case 2: 98 delete_note(); 99 break; 100 case 3: 101 print_note(); 102 break; 103 case 4: 104 exit(0); 105 break; 106 default: 107 printf("Invalid choice\n"); 108 break; 109 } 110 } 111 return 0; 112} 使用以下指令進行編譯: ...

2024-10-08 · 4 min · 812 words · YJK

Day23-malloc & free

前言 昨天介紹了一些基本的名詞,今天我們來簡單說明 malloc 和 free 的運作流程。 malloc 前面提到,malloc 可以更有效率地分配記憶體空間,按需分配以減少浪費。事實上,malloc 的分配邏輯相當複雜,但我們可以簡化來理解其基本運作。 當 malloc 請求一段記憶體空間時,會依據請求的大小,依次查找 fast bin、small bin 和 large bin,以檢查是否有合適大小的 chunk(記憶體區塊)。如果找到合適的 chunk,就會直接返回該區塊,否則會檢查 unsorted bin 中是否有合適的 chunk。 如果符合以下條件之一: chunk 位於 small bin 中 有剩餘區塊(remainder) size 大於等於 nb 獨立的 chunk(lone chunk) 那麼系統會將剩餘的 chunk 放回 unsorted bin。如果不符合條件,則會從 unsorted bin 中切割合適的 chunk,並繼續尋找符合大小的 chunk。如果找到合適大小的 chunk 就返回它;否則會將這些 chunk 按大小存放到對應的 small bin 或 large bin,直到 unsorted bin 被清空。 接著,對於 small bin size 的 chunk,會找到稍大於請求的 chunk 並將其切割,剩餘部分則放入 unsorted bin。對於 large bin size 的 chunk,流程大致相同。如果所有 bin 都無法滿足請求,則會向 top chunk 要求更多空間。如果 top chunk 也沒有足夠空間,系統會透過 brk 或 mmap 向 kernel 請求額外記憶體。 ...

2024-10-07 · 1 min · 183 words · YJK

Day22-Heap 名詞介紹

前言 在昨天的文章中,我們提到 Heap 涉及到許多關鍵名詞與概念,如 chunk 和 bin。由於這些名詞在後續討論 malloc 流程時會高度關聯,因此今天我們先對這些重要名詞做一些簡單介紹,為後續的學習打好基礎。 chunk chunk 是 glibc 在進行記憶體管理時使用的資料結構。例如,當你呼叫 malloc 分配記憶體時,實際上得到的是一塊 chunk。chunk 的最小大小是 SIZE_T (unsigned long int,8 byte) 的四倍。chunk 包含兩部分:chunk header(由 prev_size 和 size 組成)以及 user data,malloc 返回的指標實際上是指向 user data 的位址。 當一塊 chunk 被 free 掉後,它會被加入一個名為 bin 的 linked list 中。根據大小,chunk 可以分為三類:allocated chunk、free chunk 和 top chunk。 allocated chunk allocated chunk 的特殊之處在於當前一塊 chunk 是 free 狀態時,allocated chunk 的 prev_size 欄位會存儲上一塊 chunk(包含 header)的大小。此外,allocated chunk 的 size 欄位還包含了三個 flag: ...

2024-10-06 · 2 min · 320 words · YJK

Day21-Heap 簡介

前言 昨天我們已經結束了 stack 相關的內容,今天要開始介紹 PWN 中另一個非常重要的部分:Heap。相較於 stack,heap 涉及更多基礎知識,是一個需要深入了解的重要環節。 What is Heap? 在程式執行過程中,為了更有效率地分配記憶體空間,會使用動態記憶體配置(Dynamic Memory Allocation)。不同的使用場景會使用不同的記憶體分配器,例如:glibc 使用的 ptmalloc、firefox 的 jemalloc 以及 chrome 的 tcmalloc。而我們所說的 heap,就是這些分配器取得的一塊連續的虛擬記憶體空間。我們接下來的討論主要集中在 glibc 所使用的記憶體分配器上。 malloc malloc 是用來分配記憶體的函數,基本原則是需要多少分配多少。這樣可以提升程式的記憶體分配效率,避免不必要的空間浪費。其實,malloc 的運作過程相當複雜,但若簡單整理,可以理解為:如果分配的 size $<$ 128KB,系統會呼叫 brk 來進行配置;相反,若 size $\ge$ 128KB,則會使用 mmap 進行分配。 main arena 雖然如果分配的 size 小於 128KB 會通過 brk 來向 kernel 申請空間,但實際上並不只分配請求的空間,系統會直接給予 132KB 的 heap 段,這段記憶體被稱為 main arena。 以下是 GLIBC 2.35 關於 struct malloc_state 的程式碼: 1struct malloc_state 2{ 3 /* Serialize access. */ 4 __libc_lock_define (, mutex); 5 6 /* Flags (formerly in max_fast). */ 7 int flags; 8 9 /* Set if the fastbin chunks contain recently inserted free blocks. */ 10 /* Note this is a bool but not all targets support atomics on booleans. */ 11 int have_fastchunks; 12 13 /* Fastbins */ 14 mfastbinptr fastbinsY[NFASTBINS]; 15 16 /* Base of the topmost chunk -- not otherwise kept in a bin */ 17 mchunkptr top; 18 19 /* The remainder from the most recent split of a small request */ 20 mchunkptr last_remainder; 21 22 /* Normal bins packed as described above */ 23 mchunkptr [NBINS * 2 - 2]; 24 25 /* Bitmap of bins */ 26 unsigned int binmap[BINMAPSIZE]; 27 28 /* Linked list */ 29 struct malloc_state *next; 30 31 /* Linked list for free arenas. Access to this field is serialized 32 by free_list_lock in arena.c. */ 33 struct malloc_state *next_free; 34 35 /* Number of threads attached to this arena. 0 if the arena is on 36 the free list. Access to this field is serialized by 37 free_list_lock in arena.c. */ 38 INTERNAL_SIZE_T attached_threads; 39 40 /* Memory allocated from the system in this arena. */ 41 INTERNAL_SIZE_T system_mem; 42 INTERNAL_SIZE_T max_system_mem; 43}; 可以看到這裡有 flag、last_remainder、fastbin 和 bins 等欄位,這些名詞將在後續內容中詳細說明。 ...

2024-10-05 · 2 min · 321 words · YJK

Day20-Stack 題目練習

Lab 網址 前言 今天大致來到 Stack 部分的尾聲,前面的內容我們討論了幾個經典的 Stack 漏洞,講解了如何進行攻擊並編寫 exploit,也介紹了不少實用的工具。今天,我們將實際練習一道程式碼量較大的題目,這是我在 HITCON 社團攤位時設計的練習題目。 Lab 查看以下原始碼: 1#include<stdio.h> 2#include<stdlib.h> 3#include<unistd.h> 4int choice,num; 5char *product[3] = { 6 "shirt\n", 7 "sticker\n", 8 "tissue\n" 9}; 10 11void buy(){ 12 printf("We have 3 products:\n"); 13 for(int i=0; i<3; i++){ 14 printf("%d. %s", i+1, product[i]); 15 } 16 printf("Which one do you want to buy? "); 17 scanf("%d", &choice); 18 printf("You have bought %s\n", product[choice-1]); 19 printf("How many do you want to buy? "); 20 scanf("%d", &num); 21 return; 22} 23 24char address[0x20]; 25int main(){ 26 setvbuf(stdout, 0, _IONBF, 0); 27 setvbuf(stdin, 0, _IONBF, 0); 28 printf("This is HackerSir!\n"); 29 printf("Welcome to the shop!\n"); 30 printf("1. Buy\n"); 31 printf("2. Exit\n"); 32 printf("Your choice: "); 33 scanf("%d", &choice); 34 if(choice==1){ 35 buy(); 36 printf("Please leave your address: "); 37 scanf("%s", address); 38 printf(address); 39 }else{ 40 printf("Goodbye!\n"); 41 exit(0); 42 } 43 char last[0x10]; 44 printf("\nleave your last message to our club: "); 45 read(0, last, 0x30); 46 return 0; 47} 使用以下指令進行編譯: ...

2024-10-04 · 3 min · 464 words · YJK

Day19-Shellcode Bonus-ORW

Lab 網址 前言 今天我們將介紹一些簡單的知識,並在最後提供一個練習 Lab。看完前面的內容,大家可能會想:「如果我們可以調用 execve,那是否就能拿到 shell 呢?」這確實沒錯,但還有一種方法可以防止使用者調用 execve 系統呼叫 (syscall),這就是 seccomp 的機制。 Seccomp (secure computing mode) seccomp 是 Linux 核心用來禁用特定系統呼叫的機制,透過 Seccomp BPF 可以設定對某些 syscall 的過濾規則。例如,可以限制 execve,甚至 open、read、write 等等。 但在實作這些規則後,逆向工程的過程可能變得不直觀且困難理解。為了簡化分析,one gadget 工具的作者還開發了一個工具,叫做 seccomp-tools,非常適合這類情況。 seccomp-tools 筆者提供的環境中已經安裝了該工具 這個工具能分析程式的 seccomp 規則,並將結果轉換成直觀的 if-else 形式的 pseudo code。這讓我們能輕鬆看出程式允許或限制了哪些 syscall,非常適合處理複雜的 seccomp 規則。 使用方式如下: 1seccomp-tools dump ./[binary] Lab 查看以下原始碼: 1#include <stdio.h> 2#include <sys/mman.h> 3#include <unistd.h> 4#include "seccomp-bpf.h" 5 6void apply_seccomp() { 7 struct sock_filter filter[] = { 8 VALIDATE_ARCHITECTURE, 9 EXAMINE_SYSCALL, 10 ALLOW_SYSCALL(read), 11 ALLOW_SYSCALL(write), 12 ALLOW_SYSCALL(open), 13 KILL_PROCESS, 14 }; 15 16 struct sock_fprog prog = { 17 .len = (unsigned short)(sizeof(filter) / sizeof(struct sock_filter)), 18 .filter = filter, 19 }; 20 21 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { 22 perror("Seccomp Error"); 23 exit(1); 24 }; 25 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { 26 perror("Seccomp Error"); 27 exit(1); 28 }; 29} 30char shellcode[0x100]; 31int main() { 32 setvbuf(stdin, 0, 2, 0); 33 setvbuf(stdout, 0, 2, 0); 34 setvbuf(stderr, 0, 2, 0); 35 unsigned long addr = (unsigned long)&shellcode & ~0xfff; 36 mprotect((void *)addr, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE); 37 apply_seccomp(); 38 printf("I add new rule to prevent you from using system().\n"); 39 printf("Give me shellcode: "); 40 read(0, shellcode, 0x100); 41 printf("Overflow me: "); 42 char buffer[0x10]; 43 gets(buffer); 44 printf("Bye!\n"); 45 return 0; 46} 使用以下指令進行編譯: ...

2024-10-03 · 3 min · 446 words · YJK

Day18-Stack Pivoting

Lab 網址 前言 如果我們沒有辦法有足夠的長度或方法 leak libc 或是構建 ROP chain,還有什麼攻擊方式嗎?其實有的,就是將 stack 直接移到其他區塊並執行事先寫在指定區域的 chain。將 stack 遷移到其他地方的方式就叫做 Stack Pivoting 或 Stack Migration。 Stack Pivoting Stack Pivoting 是將 ROP chain 分次寫在指定區域,最後將 stack 遷移過去執行。Stack 由 rsp 控制,因此我們需要控制 rsp,主要利用以下的關鍵指令: leave ; ret 實際執行了以下兩個動作: leave -> mov rsp , rbp ; pop rbp ; ret -> pop rip; 由於在每次的 buffer overflow 中,rbp 是可以控制的,所以我們可以利用 rbp 加上這個指令來控制 rsp。這些 gadgets 在程式中幾乎都可以找到,因為它們通常用於 function 結束時恢復上一個 function 的 stack frame。 範例 以下範例展示如何將 rsp 遷移到另一個區塊: ...

2024-10-02 · 2 min · 364 words · YJK

Day17-ret2csu

前言 經過了這麼多天的 Lab,今天的內容就輕鬆一下,只講原理,不提供練習 Lab 囉! 如果大家有注意看前兩天的程式碼,應該會發現我都寫了一個 pop rdi 指令進去。這是因為現在較新的編譯器版本不再包含 __libc_csu_init,因此傳遞參數變得相對困難。不過,在有這個函式的情況下,會有更多傳遞參數的 gadgets 可以使用,甚至可以直接用一大段 __libc_csu_init 來設定參數並取得 shell。這種技巧被稱為 ret2csu。 __libc_csu_init __libc_csu_init 是編譯器在編譯時會自動加入的函式,用來初始化 libc 函式庫。由於大部分程式都會使用 libc 函式,這個函式原則上一定會存在(不過現今新版編譯器已不再編入這個函式)。 這是 __libc_csu_init 的部分內容: 仔細觀察可以發現,最底下的這段程式碼非常適合用來放參數以及控制流程。 使用方式 我們可以控制 rbp、rbx、r12、r13、r14、r15,並跳至 gadgets 開頭。對應一下就會發現,r13 和 r14 可以用來控制 rsi、rdx 等暫存器。這樣就能解決很多程式中找不到相關 gadgets 來控制參數的問題。 此外,我們還可以透過 r15 和 rbx 指定任意記憶體位置。接著將 rbx 和 rbp 分別指定為 0 和 1,在呼叫完後確保 rbx == rbp == 1,此時 jne 不會生效,我們就可以繼續使用後面的一連串 pop 指令,最終達成任意 ROP 攻擊。

2024-10-01 · 1 min · 62 words · YJK