今天的文章將介紹令一種攻擊手法 — Shrinking Free Chunks,此攻擊利用一個 Byte 的 Heap Overflow(off-by-one),來讓 malloc 回傳一個與 Allocated Chunk 重疊的 Chunk,導致記憶體覆蓋。
Chunk 的結構如下:
+-------------------+
| prev_size | <-- 儲存前一個 `chunk` 的大小(如果前一個 `chunk` 被釋放)
+-------------------+
| size | <-- 儲存當前 `chunk` 的大小(包括標記位元)
+-------------------+
| fd | <-- 指向下一個 `chunk`(如果在 free list 中)
+-------------------+
| bk | <-- 指向上一個 `chunk`(如果在 free list 中)
+-------------------+
| data (user area) | <-- 使用者可使用的資料區塊
+-------------------+
首先,假設攻擊者會分配三個連續的記憶體區塊,稱為 Chunk a, Chunk b 和 Chunk c。這三個 chunk 在記憶體中依次排列
chunk a - chunk b - chunk c
在 Shrinking Free Chunks 攻擊中,攻擊者的目標是溢位到 chunk b 的 size 欄位,並修改其低位。具體來說,攻擊者會將 chunk b 的最小有效位(LSB)設為 0x00,來縮小這個 chunk 的大小。
首先,攻擊者會釋放 Chunk b,讓會進入 unsorted bin。
接下來,利用 off-by-one 修改 Chunk a,將它溢出至 Chunk b 位址的 size 欄位,將 Chunk b 的 size 欄位最小有效位設為 0x00,這樣就縮小了 Chunk b 的大小。這樣 malloc 分配時就會分配到錯誤大小的記憶體區塊。
因此當攻擊者再次分配一個新 Chunk 時,Chunk b(已被修改為更小的區塊)會被用來分配新的 Chunk b1。此時,因為 Chunk b 的 size 已被縮小,因此這個新分配的 Chunk b1 只佔用了 chunk b 的一部分。接著,分配另一個 Chunk b2,並將資料寫入其中。這時,chunk b2 位於 Chunk b1 之後,且尚未被影響。
問題出在 Chunk c 的 prev_size 欄位,由於 Chunk b 的 size 已經被修改縮小,Heap 管理器無法正確更新 Chunk c 的 prev_size 欄位。這導致當 Chunk b1 和 Chunk c 被釋放時,Heap 管理器錯誤地認為 Chunk b 仍然是空閒的。因為 Chunk c 的 prev_size 沒有被正確更新,此時,Chunk c 會嘗試與 Chunk b 合併,形成一個大的空閒區塊。
最後,當攻擊者再次請求分配一個較大的 Chunk 時,這個新的大區塊會包含原本屬於 Chunk b2 的記憶體。代表攻擊者現在可以控制這個新的 Chunk。