今天來講約莫四年前發生的 bug,CVE-2014-0160,又稱 Heartbleed。
OpenSSL 在之前實作了一個 TLS 的延伸協定,名叫 Heartbeat。其主要用途是在長時間連線時使用,確認一個資源(即一台機器)是否有在線上,避免兩台電腦之間太久沒有通訊,導致斷線,需要重新開啟一次連線的狀況。
除了在 TLS 上使用,也可以在 DTLS (即 UDP 版的 TLS) 上來使用。
Heartbeat 有兩種模式:一種是 HeartbeatRequest
,另一種是 HeartbeatResponse
。依據連線模式的不同,會分成以下兩種:
HeartbeatRequest
,另一邊應該要馬上用 HeartbeatResponse
回覆,所以可以做 Keep-Alive
連線 (參考 HTTP Header)。如果另一方沒有在一定時間內回覆,這條連線應該被斷開。當一方收到 HeartbeatRequest
時,接受方應該要將其內容複製一份,包在 HeartbeatResponse
當中,傳回去。傳送方應該要檢查內容是否和一開始傳送的一樣,如果不是,則重傳。
OpenSSL 有實作上述的 Heartbeat 協定,不過它是這樣實作的:
unsigned char *pl;
unsigned int payload;
...
buffer = OPENSSL.malloc(1 + 2 + payload + padding);
bp = buffer;
...
memcpy(bp, pl, payload);
...
但是,前後並沒有針對 bp
的長度 pl
做檢查,所以 pl
不一定會等於 sizeof(bp)
,而 memcpy
的實作是「從 bp
的開始,讀取 pl
的長度,然後複製到 payload
」,所以這樣會有 leak 的問題,且最多可以 leak 65535 bytes (unsigned int 最大 65535 bytes)。
所以在這個狀況下,只要傳 HeartbeatRequest
時,不要填寫正確的長度,即可以讀取到 payload
開頭之後 64K 的資料。
由於這個漏洞不能控制 payload
在記憶體實際上的位置,所以,不一定會馬上把記憶體中其他敏感資訊給洩漏出去。但因為記憶體裡面可能會有其他敏感資訊(例如:憑證私鑰,別人的 session key,別人的密碼,別人的 log),所以這個 bug 的影響頗大。
這裡有一張圖,可以解釋實際上的影響: