CVE-2024-2961 分析報告

Overview CVE-2024-2961 是一個發生在 GNU C Library(glibc)中 iconv() 函式的記憶體破壞漏洞。該漏洞源於編碼轉換模組 iconvdata/iso-2022-cn-ext.c,在處理特定中文字符(如「劄」「䂚」)並轉換為 ISO-2022-CN-EXT 編碼時,未正確檢查輸出 buffer 大小,導致會有 1–3 bytes 的 Out-of-Bounds (OOB) Write。 由於 iconv 常被用於 PHP,因此該漏洞可以與 php://filter/convert.iconv.* 結合,造成 Overflow,進而實現 leak、甚至達成 RCE。 Affected Versions glibc iconv:支援 ISO-2022-CN-EXT 的版本 PHP:多數版本(含 PHP 7.x, 8.x) 框架與系統:例如某些 Wordpress Plugin、PHP 7.0 的 Symfony 4.x 等,符合下列條件: 有 file_get_contents($_GET[‘file’]) 類似行為 可以透過 php://filter 執行 filter-chain Root Cause Analysis ISO-2022-CN-EXT 漏洞 由多個子字符集組成,專門用於轉換中文字,是 ISO-2022-CN 的擴充,可以做大量中文字元轉換 流程:需要編碼→發出轉義序列 (escape sequence) 告知需切換至哪個字符集 在處理 escape sequence 時僅在部分路徑做 buffer boundary 檢查 source code ...

2025-04-30 · 8 min · 1651 words · YJK

Day30-最後總結

總結 這一次的鐵人賽從頭開始介紹了組合語言、如何使用 GDB、ELF 檔的保護機制與工具,接著介紹了 stack 和 heap 相關的漏洞。這段過程中,我將過去一年在 PWN 領域學到的技巧與知識系統地整理了出來,並透過一些 demo 和 lab 幫助讀者更好地理解內容。這樣的互動方式能讓大家不僅是被動閱讀,還能實際操作,增強參與感,也避免了那種只看理論卻不知道如何實際操作的情況。 雖然這次鐵人賽的重點是介紹 ELF 檔的相關知識,但事實上,「萬物皆可 PWN」。無論是小小的 ELF 執行檔、Windows 的 PE 執行檔,還是每個人天天使用的瀏覽器,甚至到家中的 IoT 智慧家電,都可能成為 PWN 的目標。PWN 的技術也是多種多樣,因而被認為是 CTF 中最難掌握的分類之一。這次的主題正是希望為想要入門 PWN 領域的人提供一個較為簡單的教材,讓大家能夠更容易地踏進這個領域。 心得 這次的主題其實是在參賽前一個月才做的更改,原本計劃是從 reverse 開始講解,並在後面再介紹一些 PWN 的基礎知識與漏洞。然而,經過一個暑假的學習和參加了 AIS3 之後,我發現自己可以分享的 PWN 知識其實足以支撐整個月的文章內容,並且也有很多值得深入探討的主題。同時,我也藉此機會強迫自己開始學習 Heap 相關的知識與漏洞,這讓我的研究領域更加多元化。 雖然這次的內容寫得不算很完美,但搭配著自己設計的 lab,相信讀者會有更高的參與感。在準備這些 lab 的過程中,我自己也學到了許多額外的知識,這是一個額外的收穫。我也希望能在未來繼續精進,持續努力。 最後,我要感謝一路上支持我學習資訊安全的各種資源、課程、文章以及講師、學長姐。大家也可以參考 AngelBoy 過去的文章和影片,是理解 Heap 結構非常好的教材。另外,張元的教材 也是非常推薦的資源。想進一步學習的人也可以考慮參加台灣好厲駭、AIS3 或 SCIST 的課程,這些都是極有幫助的學習途徑。 謝謝大家這一個月來的收看與支持!

2024-10-14 · 1 min · 58 words · YJK

Day29-Shrink the chunk

前言 昨天介紹了 Fastbin Corruption,今天我們將介紹這次鐵人賽的最後一個漏洞:Shrink the chunk。 Shrink the chunk Shrink the chunk 這個漏洞的前提是存在一個 off-by-one 的 null byte 漏洞。這類漏洞其實並不少見,因為如果使用像 strcpy 或 strcat 這類在結尾會補 0 的函數,就有可能導致此類漏洞。這個漏洞主要目的是創造出 overlap chunk,從而修改 chunk 的內容,並利用 unsorted bin 和 smallbin 的 unlink 行為來達成目的。 利用方式 假設 fastbin 是空的,並且通過 malloc 分配三個 chunk 到 heap 中,分別為 A、B、C,大小分別是 0x40、0x170 和 0x100。在 B 的資料的 offset 位置 0xf0 填入 0x100,目的是為了通過 prev_size == size 的檢查。接著我們釋放 B,然後釋放 A,這裡是為了觸發 A 的 off-by-one 漏洞。 接著,我們再分配回 A(需要注意 malloc 的大小),此時就可以觸發 off-by-one 漏洞,並將原本 B 的 0x171 改寫成 0x100。這樣,這一塊會被認為是 0x100 大小。當我們再次 malloc 時,會發現因為這塊記憶體需要從 unsorted bin 中取出,因此會進行 unlink 操作,並檢查 prev_size 和 size。這就是為什麼我們一開始在 B 中放入 0x100 的原因。 ...

2024-10-13 · 1 min · 177 words · YJK

Day28-Fastbin corruption

前言 在完成了兩天的 Heap Overflow 介紹後,我們將進一步探討另一個常見的漏洞:Fastbin Corruption。 Fastbin Corruption Fastbin Corruption 顧名思義,與 fastbin 密切相關。這類漏洞的前提通常是存在 double free 的情況。其基本利用方式是通過修改已經 free 的 fastbin chunk 的 fd 指標,讓下一次 malloc 分配時獲取到惡意控制的位置。不過,為了成功利用 double free 漏洞來修改 free 後的 chunk,我們需要了解 fastbin 的特性並滿足相關的檢查條件。 fastbin 的檢查 free 操作中的檢查 在 free 操作中,系統會進行以下檢查: chunk 的地址必須小於 -size。 地址需要對齊,並且 chunk 的大小至少要滿足最小要求(如 0x20),且需是 0x10 的倍數。 下一個 chunk 的大小應該大於最小值且小於 system_memory 的大小。 最重要的一點是,系統會檢查要 free 的 chunk 是否與當前 fastbin 中對應大小的第一個 chunk 相同,若相同則無法 free。 malloc 操作中的檢查 在 malloc 操作中,系統會根據所需的 byte 數量來獲取對應的 fastbin index,並查找適合的 chunk。系統檢查該 fastbin 中的第一個 chunk 是否符合大小要求。特別的是,實際比對時會使用 fastbin 第一個 chunk 的大小來取得 index 並進行驗證。 ...

2024-10-12 · 1 min · 181 words · YJK

Day27-Heap overflow adv

前言 昨天我們提到了較新版本的 libc 針對 unlink 所引入的一些機制,今天就進一步介紹這些機制的細節。 Corrupted double linked list Corrupted double linked list 機制會檢查雙向鏈表(double linked list)是否被破壞,具體來說是檢查循環雙向鏈表(circular doubly linked list)的完整性。原則上,一個節點的前向指針與後向指針應該形成一個封閉的循環,如果鏈表被破壞,程式就會顯示 Corrupted double linked list,並且中斷運行。具體條件如下: P->bk->fd == P P->fd->bk == P Corrupted size vs. prev_size Corrupted size vs. prev_size 機制主要是用來防止 size 或 prev_size 被篡改。這個檢查的邏輯可以表示為: chunksize(P) == next_chunk(P)->prev_size 此機制是從 glibc 2.26 版本開始新增的。 How to bypass 儘管這些機制增加了攻擊的難度,但仍有可能進行攻擊,只是過程會更複雜。攻擊的核心思路是構造出符合檢查條件的假 chunk,整體流程大致如下: 偽造 chunk 結構。 需要知道指向該 chunk 的 pointer 及該指針的 address。 由於能修改的區域有限,可能需要間接進行讀取或寫入操作。 構造 chunk size 與 next_chunk->prev_size 一起偽造。 假設 r 是第二塊 chunk,且存在溢出問題,q 是第三塊: 先偽造 chunk,注意偽造 r 的 size 時需要扣掉 header 的大小。接著填入 fd 和 bk,並繼續溢出到 q 的 prev_size 和 size。當這些 chunk 被巧妙地佈置好後,r 會指向我們偽造的 chunk。 接著 free(q)。 此時檢查 q 和 r 是否已被 free(可觀察相關的 flag)。 在接下來的操作中,會利用 q 的 prev_size 計算出正確位置,發現 r 被視為一個 chunk,因此進行 unlink 操作。 按照設計進行 unlink(r, FD, BK),具體過程為: FD = r->fd = &r - 0x18 BK = r->bk = &r - 0x10 進行檢查: prev_size2 == fake_size == 0x80 r->fd->bk == r = *(&r - 0x18 + 0x18) = r r->bk->fd == r = *(&r - 0x10 + 0x10) = r 然後更新 pointer: FD->bk = BK: *(&r - 0x18 + 0x18) == &r - 0x10 BK->fd = FD: *(&r - 0x10 + 0x10) == &r - 0x18 最後結束操作: r 通常會成為一個指向 data 的 pointer,因此可以利用 r 修改附近的 pointer,進而造成任意位置讀寫。如果指針附近有 function pointer,甚至可以直接控制執行流程。 現今較新的 libc 版本引入了更多的檢查機制,因此攻擊 glibc 變得更加困難,甚至在某些情況下已經無法達成。 ...

2024-10-11 · 2 min · 223 words · YJK

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