天天看點

關于 OpenSSL“心髒出血”漏洞的分析高危漏洞預警#昨日有國外黑客爆出OpenSSL存在一處記憶體洩漏漏洞,該漏洞可随機洩漏https伺服器64k記憶體,記憶體中可能會含有程式源碼、使用者http原始請求、使用者cookie甚至明文帳号密碼等,已經有多個白帽給烏雲君提供了漏洞影響證明,涉及大量網際網路企業與電商,緊急!http://t.cn/8so46dR

當我分析gnutls的漏洞的時候,我曾經說過,那不會是我們看到的最後一個tls棧上的嚴重bug。然而我沒想到這次openssl的bug會如此嚴重。

openssl“心髒出血”漏洞是一個非常嚴重的問題。這個漏洞使攻擊者能夠從記憶體中讀取多達64 kb的資料。一些安全研究員表示:

無需任何特權資訊或身份驗證,我們就可以從我們自己的(測試機上)偷來x.509證書的私鑰、使用者名與密碼、聊天工具的消息、電子郵件以及重要的商業文檔和通信等資料。

這一切是如何發生的呢?讓我們一起從代碼中一探究竟吧。

0x01 bug

請看ssl/dl_both.c,漏洞的更新檔從這行語句開始:

一上來我們就拿到了一個指向一條sslv3記錄中資料的指針。結構體ssl3_record的定義如下(譯者注:結構體ssl3_record不是sslv3記錄

每條sslv3記錄中包含一個類型域(type)、一個長度域(length)和一個指向記錄資料的指針(data)。我們回頭去看dtls1_process_heartbeat:

sslv3記錄的第一個位元組标明了心跳包的類型。宏n2s從指針p指向的數組中取出前兩個位元組,并把它們存入變量payload中——這實際上是心跳包載荷的長度域(length)。注意程式并沒有檢查這條sslv3記錄的實際長度。變量pl則指向由通路者提供的心跳包資料。

這個函數的後面進行了以下工作:

是以程式将配置設定一段由通路者指定大小的記憶體區域,這段記憶體區域最大為 (65535 + 1 + 2 + 16) 個位元組。變量bp是用來通路這段記憶體區域的指針。

宏s2n與宏n2s幹的事情正好相反:s2n讀入一個16 bit長的值,然後将它存成雙位元組值,是以s2n會将與請求的心跳包載荷長度相同的長度值存入變量payload。然後程式從pl處開始複制payload個位元組到新配置設定的bp數組中——pl指向了使用者提供的心跳包資料。最後,程式将所有資料發回給使用者。那麼bug在哪裡呢?

0x01a 使用者可以控制變量payload和pl

如果使用者并沒有在心跳包中提供足夠多的資料,會導緻什麼問題?比如pl指向的資料實際上隻有一個位元組,那麼memcpy會把這條sslv3記錄之後的資料——無論那些資料是什麼——都複制出來。

很明顯,sslv3記錄附近有不少東西。

說實話,我對發現了openssl“心髒出血”漏洞的那些人的聲明感到吃驚。當我聽到他們的聲明時,我認為64 kb資料根本不足以推算出像私鑰一類的資料。至少在x86上,堆是向高位址增長的,是以我認為對指針pl的讀取隻能讀到新配置設定的記憶體區域,例如指針bp指向的區域。存儲私鑰和其它資訊的記憶體區域的配置設定早于對指針pl指向的記憶體區域的配置設定,是以攻擊者是無法讀到那些敏感資料的。當然,考慮到現代malloc的各種神奇實作,我的推斷并不總是成立的。

當然,你也沒辦法讀取其它程序的資料,是以“重要的商業文檔”必須位于目前程序的記憶體區域中、小于64 kb,并且剛好位于指針pl指向的記憶體塊附近。

研究者聲稱他們成功恢複了密鑰,我希望能看到poc。如果你找到了poc,請聯系我。

0x01b 漏洞修補

修複代碼中最重要的一部分如下:

這段代碼幹了兩件事情:首先第一行語句抛棄了長度為0的心跳包,然後第二步檢查確定了心跳包足夠長。就這麼簡單。

0x02 前車之鑒

我們能從這個漏洞中學到什麼呢?

我是c的粉絲。這是我最早接觸的程式設計語言,也是我在工作中使用的第一門得心應手的語言。但是和之前相比,現在我更清楚地看到了c語言的局限性。

從gnutls漏洞和這個漏洞出發,我認為我們應當做到下面三條:

花錢請人對像openssl這樣的關鍵安全基礎設施進行安全審計;

為這些庫寫大量的單元測試和綜合測試;

開始在更安全的語言中編寫替代品。

考慮到使用c語言進行安全程式設計的困難性,我不認為還有什麼其他的解決方案。我會試着做這些,你呢?

作者簡介:sean是一位關于如何把事兒幹好的軟體工程師。現在他在squadron工作。squadron是一個專為saas應用程式準備的配置與釋出管理工具。

測試版本的結果以及檢測工具:

繼續閱讀