心脏流血(Heartbleed )漏洞详解
?
2014年4月7日,OpenSSL宣布的OpenSSL?1.0.2-beta及1.0.1系列(除1.0.1g)的所有版本中,在其所實現的TLS心跳擴展存在嚴重的內存處理錯誤。它可以被用于讓每個心跳包顯示應用程序至多64K Byte的內存內容。它的CVE號為CVE-2014-0160。
該漏洞是通過發送一個畸形的心跳請求至服務器,以引起服務器內存響應而引發。由于缺乏邊界檢查,OpenSSL不會驗證心跳請求的有效性,從而可以帶給攻擊者不恰當的服務器響應。
自2011年12月31日,漏洞就已經存在,而且隨著OpenSSL版本1.0.1于2012年3月14日釋出,有缺陷的代碼被廣泛使用。通過讀取網絡服務器內存,攻擊者可以訪問敏感數據,從而危及服務器及用戶的安全。潛在的敏感數據,包括服務器的專用主密鑰,可使攻擊者得以通過被動中間人攻擊,解密當前或存儲的傳輸信息(如果服務器和客戶端未使用完全正向保密),或使用了完全正向保密時發動主動中間人攻擊。攻擊者無法控制服務器返回的數據,因為服務器會使用一個隨機內存塊作為響應。
漏洞還可能暴露其他用戶的敏感請求和響應,包括用戶任何形式的POST請求數據,會話cookie和密碼,這能使攻擊者可以劫持其他用戶的服務身份。在其披露時,約有17%或五十萬通過認證機構認證的互聯網安全網絡服務器被認為容易受到攻擊。電子前哨基金會,?Ars Technica,和布魯斯·施奈爾都認為心臟出血漏洞是“災難性的”。
Heartbleed是什么?
說起“心臟出血”要從OpenSSL說起了,實際上Heartbleed 并不是病毒,而是 OpenSSL 的一個漏洞。眾所周知,OpenSSL 是一個安全協議,它可以對用戶與大多數網絡服務所提供的服務器之間的通信進行加密。因為很多網站(例如google等)采用的是 OpenSSL 來保護敏感的用戶信息,這就讓大量的用戶信息處于隨時被竊取的危險境地。
黑客利用這個漏洞,就能夠從大量包含用戶名、密碼和其他敏感信息的數據庫中竊取數據。
剖析 Heartbleed的“所作所為”
既然上文說了,如果要弄清楚Heartbleed 的“所作所為”,筆者先來解釋一下什么是 SSL,從而進一步了解OpenSSL。
SSL 是安全套接層 (Secure Sockets Layer) 的縮寫,是一個安全標準,支持信息在用戶與服務之間安全傳遞,確保信息免遭第三方截獲。OpenSSL 是一個開源項目,由開源愛好者和志愿者使用來自開發社區的信息進行更新和維護。
如果要使 SSL 發揮作用,計算機需要與服務器進行通信。為此,它會發送稱為“heartbeat”(心跳)的信息。heartbeat 所做的就是向服務器發送特定信號以確定服務器是否聯機。如果服務器聯機,它會向計算機發送回該信號,讓用戶可以盡享安全的通信。計算機和服務器會定期發送 heartbeat 以確定用戶和服務器沒有脫機。
Heartbleed 會向服務器發送惡意 heartbeat,以此來“惡搞心跳”。實質上是“誘騙”服務器向發送該惡意 heartbeat 的用戶傳回一個隨機內存塊,其中可能包含一組地址、用戶名和密碼。令人擔憂的是,這些憑據中的一些可能屬于管理該服務器的公司。這就為黑客提供了一種通過互聯網訪問和竊取信息的途徑。
有多嚴重?
這一漏洞的嚴重性不容小覷。節選來自Heartbleed的官方說明:OpenSSL在Web容器如Apache/Nginx中使用,這兩的全球份額超過66%。一些大型的互聯網公司常使用 OpenSSL,而這曾經被認為是最安全的數據傳輸手段之一。不僅如此,眾多網絡路由器廠商包括?Cisco?Systems 與 Juniper Networks等,紛紛發布了緊急公告,列出一系列受此漏洞影響的路由器產品。雖說這些產品由于使用舊版SSL,廠商也表示會盡快更新補洞,但對于網管人員來說,著實是個令人頭大的問題。據悉,有的黑客一年前就已經在利用這個漏洞了,獲取到了不少大網站的敏感信息。
Bug類型:
該漏洞被歸為緩沖過度讀取。緩沖過度讀取錯誤是軟件可以讀取比應該被允許還多的數據。
OpenSSL版本1.0.1g增加了一些邊界檢查,以防止過度讀取緩沖。例如,測試
| ??if?(1?+?2?+?16?>?s->s3->rrec.length)?return?0;?/*?silently?discard?*/? |
?
已添加在行
| hbtype?=?*p++;?? |
?
的前面。 更改的完整列表,請參閱git.openssl.org。
更多信息:請參考知乎上的討論:http://www.zhihu.com/question/23328658?sort=created
下面是一段技術分析:http://blog.existentialize.com/diagnosis-of-the-openssl-heartbleed-bug.html
When I wrote about the GnuTLS bug, I said that this isn't the last severe TLS stack bug we'd see. I didn't expect it to be quite this bad, however.
The Heartbleed bug is a particularly nasty bug. It allows an attacker to read up to 64KB of memory, and the security researchers have said:
Without using any privileged information or credentials we were able steal from ourselves the secret keys used for our X.509 certificates, user names and passwords, instant messages, emails and business critical documents and communication.
How could this happen? Let's read the code and find out.
The bug
The fix starts here, in ssl/d1_both.c:
int dtls1_process_heartbeat(SSL *s){ unsigned char *p = &s->s3->rrec.data[0], *pl;unsigned short hbtype;unsigned int payload;unsigned int padding = 16; /* Use minimum padding */So, first we get a pointer to the data within an SSLv3 record. That looks like this:
typedef struct ssl3_record_st{int type; /* type of record */unsigned int length; /* How many bytes available */unsigned int off; /* read/write offset into 'buf' */unsigned char *data; /* pointer to the record data */unsigned char *input; /* where the decode bytes are */unsigned char *comp; /* only used with decompression - malloc()ed */unsigned long epoch; /* epoch number, needed by DTLS1 */unsigned char seq_num[8]; /* sequence number, needed by DTLS1 */} SSL3_RECORD;Records have a type, a length, and data. Back to dtls1_process_heartbeat:
/* Read type and payload length first */ hbtype = *p++; n2s(p, payload); pl = p;The first byte of the SSLv3 record is the heartbeat type. The macro n2s takes two bytes from p, and puts them in payload. This is actually the length of the payload. Note that the actual length in the SSLv3 record is not checked.
The variable pl is then the resulting heartbeat data, supplied by the requester.
Later in the function, it does this:
unsigned char *buffer, *bp; int r;/* Allocate memory for the response, size is 1 byte * message type, plus 2 bytes payload length, plus * payload, plus padding */ buffer = OPENSSL_malloc(1 + 2 + payload + padding); bp = buffer;So we're allocating as much memory as the requester asked for: up to 65535+1+2+16, to be precise. The variable bp is going to be the pointer used for accessing this memory. Then:
/* Enter response type, length and copy payload */ *bp++ = TLS1_HB_RESPONSE; s2n(payload, bp); memcpy(bp, pl, payload);The macro s2n does the inverse of n2s: it takes a 16-bit value and puts it into two bytes. So it puts the same payload length requested.
Then it copies payload bytes from pl, the user supplied data, to the newly allocated bp array. After this, it sends this all back to the user. So where's the bug?
The user controls payload and pl
What if the requester didn't actually supply payload bytes, like she said she did? What if pl really is only one byte? Then the read from memcpy is going to read whatever memory was near the SSLv3 record and within the same process.
And apparently, there's a lot of stuff nearby.
There are two ways memory is dynamically allocated with malloc (at least on Linux): using sbrk(2) and using mmap(2). If the memory is allocated with sbrk, then it uses the old heap-grows-up rules and limits what can be found with this, although multiple requests (especially simultaneously) could still find some fun stuff1.
The allocations for bp don't matter at all, actually. The allocation for pl, however, matters a great deal. It's almost certainly allocated with sbrk because of the mmap threshold in malloc. However, interesting stuff (like documents or user info), is very likely to be allocated with mmap and might be reachable from pl. Multiple simultaneous requests will also make some interesting data available.
And your secret keys will probably be available:
Just cracked @CloudFlare ’s challenge: https://t.co/8ZPSxyKF4D . I wonder when they’ll update the page.
— Fedor Indutny (@indutny) April 11, 2014The fix
The most important part of the fix was this:
/* Read type and payload length first */ if (1 + 2 + 16 > s->s3->rrec.length)return 0; /* silently discard */ hbtype = *p++; n2s(p, payload); if (1 + 2 + payload + 16 > s->s3->rrec.length)return 0; /* silently discard per RFC 6520 sec. 4 */ pl = p;This does two things: the first check stops zero-length heartbeats. The second check checks to make sure that the actual record length is sufficiently long. That's it.
Lessons
What can we learn from this?
I'm a fan of C. It was my first programming language and it was the first language I felt comfortable using professionally. But I see its limitations more clearly now than I have ever before.
Between this and the GnuTLS bug, I think that we need to do three things:
Given how difficult it is to write safe C, I don't see any other options. I would donate to this effort. Would you?
This section originally contained my skepticism about the feasability of a PoC due to the nature of how the heap works via sbrk. Neel Mehta has validated some of my concerns, but there are many reports of secret key discovery out there.?
總結
以上是生活随笔為你收集整理的心脏流血(Heartbleed )漏洞详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 爬虫智联招聘
- 下一篇: 心脏出血漏洞修复记录