Merge branch 'staging/electro-release' into bug/MSP-10715/import-security-issues

Update deps

Conflicts:
	Gemfile
	Gemfile.lock
bug/bundler_fix
Trevor Rosen 2014-07-30 12:49:15 -05:00
commit 3e915e5059
No known key found for this signature in database
GPG Key ID: 255ADB7A642D3928
9 changed files with 325 additions and 155 deletions

View File

@ -5,6 +5,7 @@ PATH
activesupport (>= 3.0.0, < 4.0.0)
bcrypt
json
metasploit-model (~> 0.26.1)
meterpreter_bins (= 0.0.6)
msgpack
nokogiri
@ -82,7 +83,7 @@ GEM
msgpack (0.5.8)
multi_json (1.0.4)
network_interface (0.0.1)
nokogiri (1.6.2.1)
nokogiri (1.6.3.1)
mini_portile (= 0.6.0)
packetfu (1.1.9)
pcaprub (0.11.3)
@ -118,9 +119,9 @@ GEM
rspec-collection_matchers (1.0.0)
rspec-expectations (>= 2.99.0.beta1)
rspec-core (2.99.1)
rspec-expectations (2.99.1)
rspec-expectations (2.99.2)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.99.1)
rspec-mocks (2.99.2)
rspec-rails (2.99.0)
actionpack (>= 3.0)
activemodel (>= 3.0)
@ -132,13 +133,13 @@ GEM
rspec-mocks (~> 2.99.0)
rubyntlm (0.4.0)
rubyzip (1.1.6)
shoulda-matchers (2.6.1)
shoulda-matchers (2.6.2)
activesupport (>= 3.0.0)
simplecov (0.5.4)
multi_json (~> 1.0.3)
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
slop (3.5.0)
slop (3.6.0)
sprockets (2.2.2)
hike (~> 1.2)
multi_json (~> 1.0)

View File

@ -9,6 +9,7 @@ require 'active_support'
require 'bcrypt'
require 'json'
require 'msgpack'
require 'metasploit/model'
require 'nokogiri'
require 'packetfu'
# railties has not autorequire defined

View File

@ -12,6 +12,9 @@ module Metasploit
# @return [Boolean] Whether BOTH a public and private are required
# (defaults to `true`)
attr_accessor :paired
# @!attribute parent
# @return [Object] the parent object that had .to_credential called on it to create this object
attr_accessor :parent
# @!attribute private
# The private credential component (e.g. username)
#

View File

@ -83,6 +83,7 @@ module Metasploit
# This could be a Credential object, or a Credential Core, or an Attempt object
# so make sure that whatever it is, we end up with a Credential.
credential = raw_cred.to_credential
credential.parent = raw_cred
if credential.realm.present? && self.class::REALM_KEY.present?
credential.realm_key = self.class::REALM_KEY
@ -129,7 +130,14 @@ module Metasploit
successful_users = Set.new
each_credential do |credential|
next if successful_users.include?(credential.public)
# For Pro bruteforce Reuse and Guess we need to note that we skipped an attempt.
if successful_users.include?(credential.public)
if credential.parent.respond_to?(:skipped)
credential.parent.skipped = true
credential.parent.save!
end
next
end
result = attempt_login(credential)
result.freeze

View File

@ -4,6 +4,9 @@ module RPC
class RPC_Db < RPC_Base
private
include Metasploit::Credential::Creation
def db
self.framework.db.active
end
@ -15,6 +18,21 @@ private
self.framework.db.workspace
end
def fix_cred_options(opts)
new_opts = fix_options(opts)
# Convert some of are data back to symbols
if new_opts[:origin_type]
new_opts[:origin_type] = new_opts[:origin_type].to_sym
end
if new_opts[:private_type]
new_opts[:private_type] = new_opts[:private_type].to_sym
end
new_opts
end
def fix_options(opts)
newopts = {}
opts.each do |k,v|
@ -88,6 +106,40 @@ private
public
def rpc_create_cracked_credential(xopts)
opts = fix_cred_options(xopts)
create_credential(opts)
end
def rpc_create_credential(xopts)
opts = fix_cred_options(xopts)
core = create_credential(opts)
ret = {
username: core.public.try(:username),
private: core.private.try(:data),
private_type: core.private.try(:type),
realm_value: core.realm.try(:value),
realm_key: core.realm.try(:key)
}
if opts[:last_attempted_at] && opts[:status]
opts[:core] = core
opts[:last_attempted_at] = opts[:last_attempted_at].to_datetime
login = create_credential_login(opts)
ret[:host] = login.service.host.address,
ret[:sname] = login.service.name
ret[:status] = login.status
end
ret
end
def rpc_invalidate_login(xopts)
opts = fix_cred_options(xopts)
invalidate_login(opts)
end
def rpc_hosts(xopts)
::ActiveRecord::Base.connection_pool.with_connection {
opts, wspace = init_db_opts_workspace(xopts)
@ -490,33 +542,6 @@ public
}
end
def rpc_report_auth_info(xopts)
::ActiveRecord::Base.connection_pool.with_connection {
opts, wspace = init_db_opts_workspace(xopts)
res = self.framework.db.report_auth_info(opts)
return { :result => 'success' } if(res)
{ :result => 'failed' }
}
end
def rpc_get_auth_info(xopts)
::ActiveRecord::Base.connection_pool.with_connection {
opts, wspace = init_db_opts_workspace(xopts)
ret = {}
ret[:auth_info] = []
# XXX: This method doesn't exist...
ai = self.framework.db.get_auth_info(opts)
ai.each do |i|
info = {}
i.each do |k,v|
info[k.to_sym] = v
end
ret[:auth_info] << info
end
ret
}
end
def rpc_get_ref(name)
::ActiveRecord::Base.connection_pool.with_connection {
db_check
@ -828,42 +853,6 @@ public
}
end
# requires host, port, user, pass, ptype, and active
def rpc_report_cred(xopts)
::ActiveRecord::Base.connection_pool.with_connection {
opts, wspace = init_db_opts_workspace(xopts)
res = framework.db.find_or_create_cred(opts)
return { :result => 'success' } if res
{ :result => 'failed' }
}
end
#right now workspace is the only option supported
def rpc_creds(xopts)
::ActiveRecord::Base.connection_pool.with_connection {
opts, wspace = init_db_opts_workspace(xopts)
limit = opts.delete(:limit) || 100
offset = opts.delete(:offset) || 0
ret = {}
ret[:creds] = []
::Mdm::Cred.find(:all, :include => {:service => :host}, :conditions => ["hosts.workspace_id = ?",
framework.db.workspace.id ], :limit => limit, :offset => offset).each do |c|
cred = {}
cred[:host] = c.service.host.address if(c.service.host)
cred[:updated_at] = c.updated_at.to_i
cred[:port] = c.service.port
cred[:proto] = c.service.proto
cred[:sname] = c.service.name
cred[:type] = c.ptype
cred[:user] = c.user
cred[:pass] = c.pass
cred[:active] = c.active
ret[:creds] << cred
end
ret
}
end
def rpc_import_data(xopts)
::ActiveRecord::Base.connection_pool.with_connection {

View File

@ -18,6 +18,8 @@ class Db
# TODO: Not thrilled about including this entire module for just store_local.
include Msf::Auxiliary::Report
include Metasploit::Credential::Creation
#
# The dispatcher's name.
#
@ -217,7 +219,6 @@ class Db
return unless active?
::ActiveRecord::Base.connection_pool.with_connection {
onlyup = false
host_search = nil
set_rhosts = false
mode = :search
delete_count = 0
@ -580,7 +581,6 @@ class Db
return
end
mode = :search
while (arg = args.shift)
case arg
#when "-a","--add"
@ -648,53 +648,123 @@ class Db
end
def cmd_creds_help
print_line "Usage: creds [addr range]"
print_line "List credentials. If an address range is given, show only credentials with"
print_line "logins on hosts within that range."
print_line
print_line "With no sub-command, list credentials. If an address range is"
print_line "given, show only credentials with logins on hosts within that"
print_line "range."
print_line
print_line "Usage - Listing credentials:"
print_line " creds [filter options] [address range]"
print_line
print_line "Usage - Adding credentials:"
print_line " creds add-ntlm <user> <ntlm hash> [domain]"
print_line " creds add-password <user> <password> [realm] [realm-type]"
print_line " creds add-ssh-key <user> </path/to/id_rsa> [realm-type]"
print_line "Where [realm type] can be one of:"
Metasploit::Model::Realm::Key::SHORT_NAMES.each do |short, description|
print_line " #{short} - #{description}"
end
print_line
print_line "General options"
print_line " -h,--help Show this help information"
print_line " -c,--columns Columns of interest"
print_line
print_line "Filter options for listing"
print_line " -P,--password <regex> List passwords that match this regex"
print_line " -p,--port <portspec> List creds with logins on services matching this port spec"
print_line " -s <svc names> List creds matching comma-separated service names"
print_line " -u,--user <regex> List users that match this regex"
print_line
print_line "Examples:"
print_line "Examples, listing:"
print_line " creds # Default, returns all credentials"
print_line " creds 1.2.3.4/24 # nmap host specification"
print_line " creds -p 22-25,445 # nmap port specification"
print_line " creds -s ssh,smb # All creds associated with a login on SSH or SMB services"
print_line
print_line
print_line "Examples, adding:"
print_line " # Add a user with an NTLMHash"
print_line " creds add-ntlm alice 5cfe4c82d9ab8c66590f5b47cd6690f1:978a2e2e1dec9804c6b936f254727f9a"
print_line " # Add a user with a blank password and a domain"
print_line " creds add-password bob '' contosso"
print_line
end
#
# Can return return active or all, on a certain host or range, on a
# certain port or range, and/or on a service name.
#
def cmd_creds(*args)
return unless active?
::ActiveRecord::Base.connection_pool.with_connection {
# @param private_type [Symbol] See `Metasploit::Credential::Creation#create_credential`
# @param username [String]
# @param password [String]
# @param realm [String]
# @param realm_type [String] A key in `Metasploit::Model::Realm::Key::SHORT_NAMES`
def creds_add(private_type, username, password=nil, realm=nil, realm_type=nil)
cred_data = {
username: username,
private_data: password,
private_type: private_type,
workspace_id: framework.db.workspace,
origin_type: :import,
filename: "msfconsole"
}
if realm.present?
if realm_type.present?
realm_key = Metasploit::Model::Realm::Key::SHORT_NAMES[realm_type]
if realm_key.nil?
valid = Metasploit::Model::Realm::Key::SHORT_NAMES.keys.map{|n|"'#{n}'"}.join(", ")
print_error("Invalid realm type: #{realm_type}. Valid values: #{valid}")
return
end
end
realm_key ||= Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
cred_data.merge!(
realm_value: realm,
realm_key: realm_key
)
end
begin
create_credential(cred_data)
rescue ActiveRecord::RecordInvalid => e
print_error("Failed to add #{private_type}: #{e}")
end
end
def creds_add_non_replayable_hash(*args)
creds_add(:non_replayable_hash, *args)
end
def creds_add_ntlm_hash(*args)
creds_add(:ntlm_hash, *args)
end
def creds_add_password(*args)
creds_add(:password, *args)
end
def creds_add_ssh_key(username, *args)
key_file, realm = args
begin
key_data = File.read(key_file)
rescue ::Errno::EACCES, ::Errno::ENOENT => e
print_error("Failed to add ssh key: #{e}")
else
creds_add(:ssh_key, username, key_data, realm)
end
end
def creds_search(*args)
host_ranges = []
port_ranges = []
svcs = []
#cred_table_columns = [ 'host', 'port', 'user', 'pass', 'type', 'proof', 'active?' ]
cred_table_columns = [ 'host', 'port', 'public', 'private', 'realm', 'private_type' ]
cred_table_columns = [ 'host', 'service', 'public', 'private', 'realm', 'private_type' ]
user = nil
# Short-circuit help
if args.delete "-h"
cmd_creds_help
return
end
delete_count = 0
while (arg = args.shift)
case arg
when "-h"
cmd_creds_help
return
when '-o'
output_file = args.shift
if (!output_file)
@ -731,6 +801,8 @@ class Db
print_error("Argument required for -u")
return
end
when "-d"
mode = :delete
else
# Anything that wasn't an option is a host to search for
unless (arg_host_range(arg, host_ranges))
@ -757,6 +829,7 @@ class Db
tbl = Rex::Ui::Text::Table.new(tbl_opts)
::ActiveRecord::Base.connection_pool.with_connection {
query = Metasploit::Credential::Core.where(
workspace_id: framework.db.workspace,
)
@ -781,9 +854,9 @@ class Db
tbl << [
"", # host
"", # port
core.public ? core.public.username : "",
core.private ? core.private.data : "",
core.realm ? core.realm.value : "",
core.public,
core.private,
core.realm,
core.private ? core.private.class.model_name.human : "",
]
else
@ -803,23 +876,83 @@ class Db
if host_ranges.present? && !host_ranges.any? { |range| range.include?(login.service.host.address) }
next
end
row = [ login.service.host.address ]
if login.service.name.present?
row << "#{login.service.port}/#{login.service.proto} (#{login.service.name})"
else
row << "#{login.service.port}/#{login.service.proto}"
end
tbl << [
login.service.host.address,
login.service.port,
core.public ? core.public.username : "",
core.private ? core.private.data : "",
core.realm ? core.realm.value : "",
row += [
core.public,
core.private,
core.realm,
core.private ? core.private.class.model_name.human : "",
]
tbl << row
end
end
if mode == :delete
core.destroy
delete_count += 1
end
end
print_line(tbl.to_s)
print_status("Deleted #{delete_count} creds") if delete_count > 0
}
end
#
# Can return return active or all, on a certain host or range, on a
# certain port or range, and/or on a service name.
#
def cmd_creds(*args)
return unless active?
# Short-circuit help
if args.delete "-h"
cmd_creds_help
return
end
subcommand = args.shift
case subcommand
when "add-ntlm"
creds_add_ntlm_hash(*args)
when "add-password"
creds_add_password(*args)
when "add-hash"
creds_add_non_replayable_hash(*args)
when "add-ssh-key"
creds_add_ssh_key(*args)
else
# then it's not actually a subcommand
args.unshift(subcommand) if subcommand
creds_search(*args)
end
end
def cmd_creds_tabs(str, words)
case words.length
when 1
# subcommands
tabs = [ 'add-ntlm', 'add-password', 'add-hash', 'add-ssh-key', ]
when 2
tabs = if words[1] == 'add-ssh-key'
tab_complete_filenames(str, words)
else
[]
end
#when 5
# tabs = Metasploit::Model::Realm::Key::SHORT_NAMES.keys
else
tabs = []
end
return tabs
end
def cmd_notes_help
print_line "Usage: notes [-h] [-t <type1,type2>] [-n <data string>] [-a] [addr range]"
print_line
@ -1106,8 +1239,8 @@ class Db
range.each do |host|
file = File.open(filename, "rb")
contents = file.read
lootfile = framework.db.find_or_create_loot(:type => type, :host => host,:info => info, :data => contents,:path => filename,:name => name)
print_status("Added loot #{host}")
lootfile = framework.db.find_or_create_loot(:type => type, :host => host, :info => info, :data => contents, :path => filename, :name => name)
print_status("Added loot for #{host} (#{lootfile})")
end
end
return
@ -1263,7 +1396,7 @@ class Db
def cmd_db_import(*args)
return unless active?
::ActiveRecord::Base.connection_pool.with_connection {
if (args.include?("-h") or not (args and args.length > 0))
if args.include?("-h") || ! (args && args.length > 0)
cmd_db_import_help
return
end
@ -1326,8 +1459,8 @@ class Db
next
rescue REXML::ParseException => e
print_error("Failed to import #{filename} due to malformed XML:")
print_error("#{$!.class}: #{$!}")
elog("Failed to import #{filename}: #{$!.class}: #{$!}")
print_error("#{e.class}: #{e}")
elog("Failed to import #{filename}: #{e.class}: #{e}")
dlog("Call stack: #{$@.join("\n")}", LEV_3)
next
end
@ -1629,7 +1762,6 @@ class Db
end
def db_find_tools(tools)
found = true
missed = []
tools.each do |name|
if(! Rex::FileUtils.find_full_path(name))

View File

@ -305,7 +305,7 @@ protected
last_cell = nil
last_idx = nil
row.each_with_index { |cell, idx|
if (last_cell)
if (idx != 0)
line << pad(' ', last_cell.to_s, last_idx)
end
# line << pad(' ', cell.to_s, idx)

View File

@ -55,6 +55,9 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'bcrypt'
# Needed for some admin modules (scrutinizer_add_user.rb)
spec.add_runtime_dependency 'json'
# Things that would normally be part of the database model, but which
# are needed when there's no database
spec.add_runtime_dependency 'metasploit-model', '~> 0.26.1'
# Needed for Meterpreter on Windows, soon others.
spec.add_runtime_dependency 'meterpreter_bins', '0.0.6'
# Needed by msfgui and other rpc components

View File

@ -11,6 +11,37 @@ describe Msf::Ui::Console::CommandDispatcher::Db do
described_class.new(driver)
end
describe "#cmd_creds" do
describe "add-password" do
let(:username) { "username" }
let(:password) { "password" }
context "when no core exists" do
it "should add a Core" do
expect {
subject.cmd_creds("add-password", username, password)
}.to change{ Metasploit::Credential::Core.count }.by 1
end
end
context "when a core already exists" do
before(:each) do
priv = FactoryGirl.create(:metasploit_credential_password, data: password)
pub = FactoryGirl.create(:metasploit_credential_public, username: username)
core = FactoryGirl.create(:metasploit_credential_core,
origin: FactoryGirl.create(:metasploit_credential_origin_import),
private: priv,
public: pub,
realm: nil,
workspace: framework.db.workspace)
end
it "should not add a Core" do
expect {
subject.cmd_creds("add-password", username, password)
}.to_not change{ Metasploit::Credential::Core.count }
end
end
end
end
describe "#cmd_workspace" do
describe "-h" do
it "should show a help message" do
@ -183,6 +214,7 @@ describe Msf::Ui::Console::CommandDispatcher::Db do
end
=begin
describe "#cmd_creds" do
describe "-h" do
it "should show a help message" do
@ -206,6 +238,7 @@ describe Msf::Ui::Console::CommandDispatcher::Db do
end
end
end
=end
describe "#cmd_db_import" do
describe "-h" do