Heartbleed 實戰:一個影響無數網站的緩衝區溢出漏洞

作者 :  李博傑

昨天 OpenSSL 爆出了名爲 Heartbleed 的重大安全漏洞(CVE-2014-0160),通過 TLS 的 heartbeat 擴展,可以讀取運行 HTTPS 服務的服務器上長達 64 KB 的內存,獲取內存中可能存在的敏感信息。由於這個漏洞已經存在三年,Debian stable (wheezy) 和 Ubuntu 12.04 LTS、13.04、13.10 等流行的發行版都受影響,無數部署 TLS(HTTPS)的網站都暴露在此漏洞之下。

什麼是 SSL heartbeat

https

SSL 全名 Secure Socket Layer,是在 TCP 連接的基礎上的安全層,提供了認證、加密等功能。只要你在上網的時候看到網址旁邊有個小綠鎖,或者網址是 https 開頭的,或者網址欄是綠色背景的,就說明你在使用 SSL。涉及到金錢或敏感信息的網站,一般都會使用 SSL 安全連接。

SSL 連接建立的時候需要進行兩個來回的握手,第一次握手協商所用的加密方式、獲取服務器端的數字證書,第二次握手協商後續數據傳輸所使用的對稱加密密鑰(好比兩個人協商一個公共密碼)。如果服務器在大洋彼岸,這中間的延遲有幾百毫秒。因此我們希望儘量重用已經建立的 SSL 連接。

問題是我們並不是在不停地點擊網頁,服務器怎麼知道連接對面的人還 “活着” 呢?如果對面的人 “死掉” 了,服務器需要儘快釋放資源,不然就會被殭屍連接佔滿。這就需要客戶端隔一段時間就發一個 “心跳”,告訴服務器 “我還活着”。服務器也會返回一個心跳告訴客戶端 “我還沒把你忘掉”。這就是 SSL heartbeat 的由來。

SSL 安全連接層又分爲兩層:底層是記錄層,這一層是不加密的,用 wireshark 就能抓出來;上層是握手信息、密鑰信息、用戶數據、心跳包等,加密後封裝成一個個 “記錄”。SSL 安全連接層之上,就是我們熟悉的 HTTP 等協議了。也就是說,SSL 安全連接層是透明地插在應用層(如 HTTP)和傳輸層(如 TCP)之間的。

IC197149

SSL heartbeat 跟上圖中的 Handshake 等並列。一個 SSL heartbeat 由下述 4 個字段構成:

  • 心跳包類型:1 字節,只有請求和響應兩種可能。
  • 載荷長度:2 字節,按 RFC 6520 規範,不能超過 2^14 = 16384,不過 OpenSSL 的實現只在發送請求的客戶端檢查了,服務器端沒有檢查。
  • 載荷 (payload):載荷長度這麼多字節,可以是任意字節。請求發送什麼載荷,服務器就應該響應同樣的載荷,就像是 “回顯”。
  • 填充 (padding):至少 16 字節,要把一個 SSL 記錄填滿。請求和響應的填充字節都應該是隨機的。

漏洞分析

以下漏洞分析基於 OpenSSL-1.0.1e 源碼。TLS 是 SSL 的一種傳輸方式,是比傳統 SSL 更安全的,因此如果客戶端和服務器都支持,會優先採用它。當服務器收到一個 TLS heartbeat 包時,就會調用 ssl/t1_lib.c 中的 tls1_process_heartbeat 函數(如下圖所示)。

  • 2490 行:p 被初始化爲指向數據區的指針,取出第一個字節(心跳包類型),放進 hbtype 變量。
  • 2491 行:把隨後的兩個字節(載荷長度)放進 payload 變量。n2s 是個宏,會將 p 增加 2。注意,這裏沒有對載荷長度進行任何檢查。
  • 2492 行:後續的字節被認爲是載荷和填充,賦值給 pl,稍後將被髮送出去。

code1

注意,不要與 dtls1_process_heartbeat 這個 “孿生兄弟” 弄混,DTLS 是基於 UDP 的 TLS,我們網站一般都是用 TCP 傳輸,因此調用的是 tls1_process_heartbeat。

  • 2499 行:如果這是一個心跳包請求……
  • 2508 行:爲即將發送的響應分配內存。
  • 2509 行:使用 bp 作爲即將發送的緩衝區指針。
  • 2512 行:響應的第一個字節是心跳包類型。
  • 2513 行:響應的第 2、3 字節是載荷長度。
  • 2514 行:把載荷從心跳包請求複製到響應緩衝區。緩衝區溢出就在這裏:如果 “載荷長度” 字段(payload 變量)被髮送端設置得很大,而實際的載荷長度比較短,就會把本來不屬於載荷區域的內存複製到響應緩衝區。
  • 2515~2517 行:生成 16 個隨機的填充字節並附加到載荷末尾。
  • 然後,bp 指向的緩衝區就被裝入 TLS 記錄,發送給客戶端了。

code2

不說有沒有漏洞,相信有人已經聞到代碼裏的 “壞味道” 了。變量名 p、bp、pl 都是什麼意思?也許能猜出來,但這種命名方式實在是不敢恭維。也許正是因爲這些代碼生澀難懂,才讓 bug 有了藏身之地。

漏洞利用

從前面的分析可以看出,這個漏洞利用方式簡單,除 OpenSSL 版本外不需要額外條件,不需要考慮服務器上裝的是何種 Web 服務器軟件、有沒有特殊路徑的文件等。效果也非常明顯:讀出最多 64 KB 的服務器內存,且不會在服務器日誌裏留下痕跡。64 KB 是由於記錄層最大記錄長度的限制,事實上一個記錄最大長度是 65535,根據心跳包格式掐頭去尾,載荷長度最大是 65535 – 1 – 2 – 16 = 65516 個字節。

我寫了一段 POC(Proof Of Concept)代碼來驗證。由於我比較懶,就不去研究 SSL 記錄層格式和 SSL 握手那些麻煩事了,借用 OpenSSL 客戶端庫,把發送 heartbeat 請求的 payload 字段改大。如下圖所示,修改 ssl/t1_lib.c 的 2599 行,把 payload 換成不超過 65519 字節的數值。這樣真正的載荷只有 18 字節,也就是返回的響應裏只有前 18 字節是與發送的載荷一致,後面的接近 64 KB 全都是不該泄露的服務器內存。

code3

剩下的部分就是用客戶端庫完成 SSL 握手、發送和接收 heartbeat 了。由於修改的 tls1_heartbeat 函數是未導出的私有函數,需要直接鏈接上 .o 文件。(具體的編譯命令參見 POC 代碼開頭的註釋)。我把代碼放到 gist 上了:POC 代碼

這段 POC 代碼可以發送正常的 HTTPS 請求來做測試,也可以在 SSL 握手完成後發送 heartbeat 請求並等待響應。如果服務器不支持 TLS heartbeat 擴展或者已經修補了這個漏洞,就不會收到任何響應,此時需要按 Ctrl+C 退出。如果服務器中招了,就會以類似 hexdump -C 的格式輸出接近 64 KB 的服務器內存。

哪些信息會被泄露

首先拿 CSDN 熱熱身,SSL 證書赫然可見,下圖是 X.509 Subject 部分,其中的那些 \x 是 UTF-8 表示的中文字符,翻譯過來就是那家公司的中文名字。當然,似乎這裏面沒有 private key,因此影響不是很大。

csdn

我們可愛的 12306 怎麼樣呢?看,這位哥正買 2014 年 4 月 8 日從呼和浩特(電報碼 HHC)到熊嶽城(電報碼 XYT)的成人票呢!

12306-1

某公司內部網站,泄露了 nginx 配置。

corpweb

就連雅虎這麼大的網站也未能倖免,用戶發送的 HTTP 請求中的 Cookie 清晰可見,據此可以僞裝成其他用戶登錄。

yahoo

某個人博客上剛發表的評論(垃圾評論,呵呵)。

Capture

更有趣的是,由於服務器內存的內容是在不停變動的,每次 SSL heartbeat,泄露出來的 64 KB 內容都可能是不同的。

USTC LUG 所有服務器對 OpenSSL 進行了緊急升級 以應對這個漏洞。

結語

RFC 6520 中明確規定 heartbeat 包的長度不能超過 2^14(16384),如果載荷過長,這個 heartbeat 請求應該被丟棄:

The total length of a HeartbeatMessage MUST NOT exceed 2^14 or max_fragment_length when negotiated as defined in [RFC6066].If the payload_length of a received HeartbeatMessage is too large, the received HeartbeatMessage MUST be discarded silently.

但 OpenSSL 的實現者們似乎把 RFC 中關於最大載荷長度的限制當成了耳旁風(不然最多泄露 16 KB 而非 64 KB 內存),而且完全信任客戶端發來的載荷長度。信任用戶的輸入在網絡編程中是非常危險的,但就是這樣拙劣的 bug,在數以百萬計的服務器上藏身長達三年之久(這段代碼是 2011 年引入的)。

微軟漏洞應急響應中心的人曾說,軟件的安全問題要靠專業的人去排查和測試,只是眼球足夠多,bug 並不一定就能被發現。OpenSSL 作爲網絡安全的底層支持,應該有足夠多的眼球關注了,但仍然不止一次爆出安全漏洞,這是值得我們深思的。

值得稱道的是此次漏洞的發現和處理過程:漏洞的發現者沒有急於公之於衆,而是通過 CVE 平臺向大型互聯網公司和主要 Linux 發行版的維護團隊報告,讓關係互聯網 “生死存亡” 的大型網站先補上漏洞,Linux 發行版也準備好了補丁。在預定的時間,漏洞向全世界公開,一時間網絡上關於此漏洞的報道鋪天蓋地,此時普通用戶只要應用 Linux 發行版準備好的補丁,就能有效防禦漏洞,這使得漏洞對整個互聯網的危害降到最低。

參考文獻

  1. http://heartbleed.com/
  2. http://tools.ietf.org/html/rfc6520
  3. http://technet.microsoft.com/en-us/library/cc781476(v=ws.10).aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章