漏洞分析---关于OpenSSL“心脏出血”漏洞的分析
關于OpenSSL“心臟出血”漏洞的分析
關于出處
?
- 原文作者:Sean Cassidy?【Twitter:@ex509 】
- 原作博客:http://blog.existentialize.com?
- 原文鏈接:http://blog.existentialize.com/diagnosis-of-the-openssl-heartbleed-bug.html
- 譯文出處:http://drops.wooyun.org/papers/1381
- 本文轉自:http://netsecurity.51cto.com/art/201404/435075.htm
?
前言
? ? ? ?當我分析Gnu TLS的漏洞的時候,我曾經說過,那不會是我們看到的最后一個TLS棧上的嚴重bug。然而我沒想到這次OpenSSL的bug會如此嚴重。
?
?
?
? ? ? ?OpenSSL“心臟出血”漏洞是一個非常嚴重的問題。這個漏洞使攻擊者能夠從內存中讀取最多64 KB的數據。一些安全研究員表示:
無需任何特權信息或身份驗證,我們就可以從我們自己的(測試機上)偷來X.509證書的私鑰、用戶名與密碼、聊天工具的消息、電子郵件以及重要的商業文檔和通信等數據。? ? ? ?這一切是如何發生的呢?讓我們一起從代碼中一探究竟吧。
?
?
0x01 Bug
? ? ? ?請看ssl/dl_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 */? ? ? ?一上來我們就拿到了一個指向一條SSLv3記錄中數據的指針。結構體SSL3_RECORD的定義如下(譯者注:結構體SSL3_RECORD不是SSLv3記錄的實際存儲格式。一條SSLv3記錄所遵循的存儲格式請參見下文分析):
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;? ? ? ?每條SSLv3記錄中包含一個類型域(type)、一個長度域(length)和一個指向記錄數據的指針(data)。我們回頭去看dtls1_process_heartbeat:
/* Read type and payload length first */ hbtype = *p++; n2s(p, payload); pl = p;? ? ? ?SSLv3記錄的第一個字節標明了心跳包的類型。宏n2s從指針p指向的數組中取出前兩個字節,并把它們存入變量payload中——這實際上是心跳包載荷的長度域(length)。注意程序并沒有檢查這條SSLv3記錄的實際長度。變量pl則指向由訪問者提供的心跳包數據。
? ? ? ?這個函數的后面進行了以下工作:
? ? ? ?所以程序將分配一段由訪問者指定大小的內存區域,這段內存區域最大為 (65535 + 1 + 2 + 16) 個字節。變量bp是用來訪問這段內存區域的指針。
/* Enter response type, length and copy payload */ *bp++ = TLS1_HB_RESPONSE; s2n(payload, bp); memcpy(bp, pl, payload);? ? ? ?宏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 漏洞修補
? ? ? ?修復代碼中最重要的一部分如下:
/* 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;? ? ? ?這段代碼干了兩件事情:首先第一行語句拋棄了長度為0的心跳包,然后第二步檢查確保了心跳包足夠長。就這么簡單。
?
0x02 前車之鑒
? ? ? ?我們能從這個漏洞中學到什么呢?
? ? ? ?我是C的粉絲。這是我最早接觸的編程語言,也是我在工作中使用的第一門得心應手的語言。但是和之前相比,現在我更清楚地看到了C語言的局限性。
? ? ? ?從GnuTLS漏洞和這個漏洞出發,我認為我們應當做到下面三條:
- 花錢請人對像OpenSSL這樣的關鍵安全基礎設施進行安全審計;
- 為這些庫寫大量的單元測試和綜合測試;
- 開始在更安全的語言中編寫替代品。
? ? ? ?考慮到使用C語言進行安全編程的困難性,我不認為還有什么其他的解決方案。我會試著做這些,你呢?
作者簡介:
? ? ? ?Sean是一位關于如何把事兒干好的軟件工程師?,F在他在Squadron工作。Squadron是一個專為SaaS應用程序準備的配置與發布管理工具。
測試版本的結果以:
- OpenSSL 1.0.1 through 1.0.1f (inclusive) are vulnerable
- OpenSSL 1.0.1g is NOT vulnerable
- OpenSSL 1.0.0 branch is NOT vulnerable
- OpenSSL 0.9.8 branch is NOT vulnerable
檢測工具:
?
- http://filippo.io/Heartbleed/
- http://www.anquan.org/openssl/
?
相關文章
?
?
總結
以上是生活随笔為你收集整理的漏洞分析---关于OpenSSL“心脏出血”漏洞的分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 起点中文网爬虫
- 下一篇: 【计算机网络基础概念】3广域网、网络互联