每個應用程式都依賴電腦記憶體儲存與執行程式碼。記憶體漏洞(Memory Vulnerabilities) 利用應用程式記憶體管理上的瑕疵,造成非預期行為——攻擊者最終可能注入並執行自己的指令。

記憶體漏洞主要出現在開發者必須自行管理記憶體的語言(C、C++)。Ruby、Python、PHP、Java 等有自動記憶體管理的語言相對較不易發生,但若它們本身用 C 寫成,仍可能因底層出錯而被影響。

本章只是入門,僅介紹兩類:緩衝區溢位(Buffer Overflow)越界讀取(Read Out of Bounds)。深入研究可參考:

  • Hacking: The Art of Exploitation(Jon Erickson)
  • A Bug Hunter’s Diary(Tobias Klein)

Buffer Overflow(緩衝區溢位)#

原理#

當應用程式寫入超過配置記憶體大小的資料時,就發生緩衝區溢位。輕則行為不可預期,重則攻擊者可控制溢位的位元組執行任意程式碼。

C 中常見發生點:

  • 修改記憶體的函式:strcpy()memcpy()
  • 配置記憶體的函式:malloc()calloc()

範例#

#include <string.h>
int main() {
    char src[16] = "hello world";
    char dest[16];
    strcpy(dest, src);
    printf("src is %s\n", src);
    printf("dest is %s\n", dest);
    return 0;
}

"hello world" 長 11 字元 + null byte = 12 bytes,落在 16 bytes 內,正常輸出兩個變數。

但如果擴充到 16 字元:

char src[17] = "hello world!!!!!";
char dest[16];
strcpy(dest, src);

src 配置 17 bytes(含 null)但 dest 只配 16 bytes。strcpy 把 17 bytes 複製進 dest多出來的 null byte 溢位到 src 第一個位元組——導致 src 印出來變成空字串。

64 位元系統的最小記憶體配置是 16 bytes(32 位元為 8 bytes)。即使你只宣告 char dest[12],編譯器仍會配置 16 bytes,因此 "hello world!"(13 bytes)不會觸發溢位——這個對齊細節常讓 bug 難以重現。

Read Out of Bounds(越界讀取)#

允許攻擊者讀取超出指定記憶體邊界的資料,可能洩漏敏感內容(例如金鑰、session、密碼)。

Heartbleed(CVE-2014-0160)#

最著名的越界讀取案例:OpenSSL 的 Heartbeat 功能。

  • 客戶端送出帶有 length 參數的 heartbeat 請求
  • 伺服器根據 length 配置回應緩衝區而非實際訊息大小
  • 攻擊者可送 100 bytes 訊息但聲稱 1000 bytes → 伺服器回 100 bytes 訊息 + 900 bytes 任意記憶體內容

該記憶體可能包含私鑰、session token、明文密碼——而所有資料都不需登入即可拉到。

案例 1:PHP ftp_genlist() Integer Overflow#

漏洞描述#

PHP 雖管理記憶體,但語言本身用 C 寫成。研究者 Max Spelsberg 在 PHP FTP 擴充模組的 ftp_genlist() 函式找到 buffer overflow:用 unsigned int 追蹤資料大小與行數。

unsigned int 在 32 位元機器上最大值是 2^32 - 1 ≈ 4GB。攻擊者送出超過 2^32 bytes,計數器溢位歸零,後續 buffer 配置就崩盤。

PoC:用 PHP 啟動 FTP 伺服器、Python 用戶端送入 2^32 + 1 bytes,FTP 伺服器當機。

Takeaways#

即便目標主程式不是 C/C++,只要底層用 C 實作(PHP、Python、Ruby 的 native extension),仍可能藏有 buffer overflow。盯住有變數長度檢查省略的地方。

案例 2:Python Hotshot Module#

漏洞描述#

Python 的 hotshot 是 profile 模組的 C 寫成版本。研究者 John Leitch 找到 memcpy() 用法:

memcpy(self->buffer + self->index, s, len);
  • self->buffer 是固定長度
  • s 由攻擊者提供,可任意長度
  • memcpy 不檢查目的地大小

攻擊者傳入長度大於 self->buffer 的字串即可造成寫入越界。

Takeaways#

找 buffer overflow 的固定戰術:搜尋 strcpy()memcpy()strcat() 呼叫,逆向追 source 與 destination 的長度檢查是否存在。

案例 3:Libcurl Read Out of Bounds#

漏洞描述#

libcurl 在 curl_easy_duphandle 函式裡複製 POST 資料時有兩個問題:

  1. 以 C 字串方式處理 POST body:libcurl 預期資料以 null byte 結尾——若 POST body 中沒有 null byte,會繼續讀直到遇到一個,造成讀過頭;若中間有 null byte,又會誤判結束
  2. 複製後未更新讀取指標:在「複製完」與「真正送出」之間,原本記憶體可能被清空或重新利用——攻擊者可能把不該送出的內容塞進 POST

Takeaways#

cURL 是極穩定的元件,但仍會有 bug。任何牽涉複製記憶體的功能都是高優先檢查目標。記憶體 bug 不易找,但鎖定常見易錯函式仍是務實切入點。

章末總結#

  • 記憶體漏洞輕則洩漏資料、重則任意程式執行
  • 自動管理記憶體的語言(Python、Ruby、PHP)相對安全,但底層 C 實作仍可能受影響
  • Buffer overflow 集中在:寫入、複製、配置記憶體的呼叫
  • Read out of bounds 集中在:讀長度由攻擊者控制的場景(如 Heartbeat、HTTP 解析)
  • 找這類漏洞需要記憶體管理知識,建議閱讀專門書籍補強