-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into feature/MSP-10877/login-search-associations
MSP-10877 Conflicts: lib/metasploit/credential/version.rb metasploit-credential.gemspec spec/dummy/db/schema.rb
- Loading branch information
Showing
8 changed files
with
302 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Implements a one-time migration of `Mdm::Cred` objects to | ||
# appropriate objects from {Metasploit::Credential} | ||
class OldCredsToNewCreds < ActiveRecord::Migration | ||
def up | ||
Metasploit::Credential::Migrator.new.migrate! | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
# This class provides migration behavior for converting `Mdm::Cred` objects to their appropriate counterparts in the | ||
# {Metasploit::Credential} namespace. | ||
|
||
class Metasploit::Credential::Migrator | ||
include Metasploit::Credential::Creation | ||
|
||
# @!attribute origin | ||
# An origin for tracking how these credentials made it into the system. | ||
# Treating this as an Import since there is no migration origin. | ||
# | ||
# | ||
# @return [Metasploit::Credential::Origin::Import] | ||
attr_accessor :origin | ||
|
||
# @!attribute workspaces | ||
# Where the migrated creds will originate | ||
# | ||
# | ||
# @return [Array<Mdm::Workspace>] | ||
attr_accessor :workspaces | ||
|
||
|
||
# Sets up a migrator object with either a single workspace or all workspaces | ||
# @param [Mdm::Workspace] the workspace to act on | ||
# @return [void] | ||
def initialize(workspace=nil) | ||
@origin = Metasploit::Credential::Origin::Import.new(filename: 'MIGRATION') | ||
|
||
if workspace.present? | ||
@workspaces = [workspace] | ||
else | ||
@workspaces = Mdm::Workspace.all | ||
end | ||
end | ||
|
||
# Returns true if {#workspaces} contains `Mdm::Cred` objects | ||
# @return [Boolean] | ||
def creds_exist? | ||
workspaces.map(&:creds).flatten.present? | ||
end | ||
|
||
# Perform the migration | ||
# @return [void] | ||
def migrate! | ||
if creds_exist? | ||
Metasploit::Credential::Core.transaction do | ||
origin.save # we are going to use the one we instantiated earlier, since there is work to be done | ||
workspaces.each do |workspace| | ||
convert_creds_in_workspace(workspace) | ||
end | ||
end | ||
end | ||
end | ||
|
||
# Converts the `Mdm::Cred` objects in a single `Mdm::Workspace` | ||
# @param [Mdm::Workspace] the workspace to act on | ||
# @return [void] | ||
def convert_creds_in_workspace(workspace) | ||
workspace.creds.each do |cred| | ||
service = cred.service | ||
core = create_credential( | ||
origin: origin, | ||
private_data: private_data_from_cred(cred), | ||
private_type: cred_type_to_credential_class(cred.ptype), | ||
username: cred.user, | ||
workspace_id: workspace.id | ||
) | ||
|
||
create_credential_login( | ||
address: service.host.address, | ||
core: core, | ||
port: service.port, | ||
protocol: service.proto, | ||
service_name: service.name, | ||
status: Metasploit::Model::Login::Status::UNTRIED, | ||
workspace_id: workspace.id | ||
) | ||
end | ||
end | ||
|
||
|
||
# Converts types in the legacy credentials model to a type in the new one. | ||
# Assumes that anything that isn't 'smb_hash' but contains 'hash' will be a | ||
# NonreplaybleHash. | ||
# @param cred_type [String] the value from the ptype field of `Mdm::Cred` | ||
# @return[Symbol] | ||
def cred_type_to_credential_class(cred_type) | ||
return :ntlm_hash if cred_type == "smb_hash" | ||
return :ssh_key if cred_type == "ssh_key" | ||
return :nonreplayable_hash if cred_type.include? "hash" | ||
return :password | ||
end | ||
|
||
# Returns the text of the SSH key as read from the file | ||
# @param path [String] Path to an SSH key file on disk | ||
# @return [String] | ||
def key_data_from_file(path) | ||
File.open(path) do |file| | ||
file.read | ||
end | ||
end | ||
|
||
# Returns private data given an `Mdm::Cred` | ||
# @param cred [Mdm::Cred] | ||
# @return[String] | ||
def private_data_from_cred(cred) | ||
case cred.ptype | ||
when 'ssh_key' | ||
key_data_from_file(cred.pass) | ||
else | ||
cred.pass | ||
end | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
require 'spec_helper' | ||
|
||
describe Metasploit::Credential::Migrator do | ||
include_context 'Mdm::Workspace' | ||
|
||
let(:workspace){ FactoryGirl.create(:mdm_workspace) } | ||
let(:host){ FactoryGirl.create(:mdm_host, workspace: workspace)} | ||
let(:service){ FactoryGirl.create(:mdm_service, host: host)} | ||
|
||
|
||
subject(:migrator){ Metasploit::Credential::Migrator.new(workspace) } | ||
|
||
describe "#convert_creds_in_workspace" do | ||
describe "when there are no Mdm::Cred objects in the workspace" do | ||
before(:each) do | ||
workspace.creds = [] | ||
end | ||
|
||
it 'should not change the Core count' do | ||
expect{migrator.convert_creds_in_workspace(workspace)}.to_not change(Metasploit::Credential::Core, :count) | ||
end | ||
|
||
it 'should not change the Login count' do | ||
expect{migrator.convert_creds_in_workspace(workspace)}.to_not change(Metasploit::Credential::Login, :count) | ||
end | ||
|
||
it 'should not change the Private count' do | ||
expect{migrator.convert_creds_in_workspace(workspace)}.to_not change(Metasploit::Credential::Private, :count) | ||
end | ||
|
||
it 'should not change the Public count' do | ||
expect{migrator.convert_creds_in_workspace(workspace)}.to_not change(Metasploit::Credential::Public, :count) | ||
end | ||
end | ||
|
||
describe "when there are Mdm::Cred objects present in the workspace" do | ||
|
||
let(:host1){ FactoryGirl.create(:mdm_host, workspace: workspace)} | ||
let(:host2){ FactoryGirl.create(:mdm_host, workspace: workspace)} | ||
let(:host3){ FactoryGirl.create(:mdm_host, workspace: workspace)} | ||
|
||
let(:service1){ FactoryGirl.create(:mdm_service, host: host1)} | ||
let(:service2){ FactoryGirl.create(:mdm_service, host: host2)} | ||
let(:service3){ FactoryGirl.create(:mdm_service, host: host3)} | ||
|
||
let(:cred1){ FactoryGirl.create(:mdm_cred, service: service1)} | ||
let(:cred2){ FactoryGirl.create(:mdm_cred, service: service2)} | ||
let(:cred3){ FactoryGirl.create(:mdm_cred, service: service3)} | ||
|
||
before(:each) do | ||
cred1; cred2; cred3 | ||
end | ||
|
||
it 'should migrate them into Metasploit::Credential::Core objects' do | ||
expect{migrator.convert_creds_in_workspace(workspace)}.to change(Metasploit::Credential::Core, :count).from(0).to(3) | ||
end | ||
|
||
describe "new Publics" do | ||
before(:each) do | ||
migrator.convert_creds_in_workspace(workspace) | ||
end | ||
|
||
it "should be created for each Mdm::Cred" do | ||
Metasploit::Credential::Public.where(username: cred1.user).should_not be_blank | ||
Metasploit::Credential::Public.where(username: cred2.user).should_not be_blank | ||
Metasploit::Credential::Public.where(username: cred3.user).should_not be_blank | ||
end | ||
end | ||
|
||
describe "new Privates" do | ||
before(:each) do | ||
migrator.convert_creds_in_workspace(workspace) | ||
end | ||
|
||
it "should be created for each Mdm::Cred" do | ||
migrator.convert_creds_in_workspace(workspace) | ||
Metasploit::Credential::Password.where(data: cred1.pass).should_not be_blank | ||
Metasploit::Credential::Password.where(data: cred2.pass).should_not be_blank | ||
Metasploit::Credential::Password.where(data: cred3.pass).should_not be_blank | ||
end | ||
end | ||
end | ||
|
||
describe "creating the proper kinds of Private objects" do | ||
describe "when an Mdm::Cred is an SMB hash" do | ||
let(:cred) do | ||
FactoryGirl.create(:mdm_cred, | ||
service: service, | ||
ptype: 'smb_hash', | ||
pass: FactoryGirl.build(:metasploit_credential_ntlm_hash, password_data: 'f00b4rawesomesauc3!').data | ||
) | ||
end | ||
|
||
before(:each) do | ||
migrator.convert_creds_in_workspace(cred.service.host.workspace) | ||
end | ||
|
||
it 'should create a new NTLMHash in the database' do | ||
Metasploit::Credential::NTLMHash.where(data: cred.pass).should_not be_blank | ||
end | ||
end | ||
|
||
describe "when an Mdm::Cred is an SSH key" do | ||
let(:ssh_key_content){ FactoryGirl.build(:metasploit_credential_ssh_key).data } | ||
let(:cred) do | ||
FactoryGirl.create(:mdm_cred, | ||
service: service, | ||
ptype: 'ssh_key', | ||
pass: '/path/to/ssh_key' | ||
) | ||
end | ||
|
||
before(:each) do | ||
migrator.stub(:key_data_from_file).and_return ssh_key_content | ||
migrator.convert_creds_in_workspace(cred.service.host.workspace) | ||
end | ||
|
||
it 'should create a new SSHKey in the database' do | ||
Metasploit::Credential::SSHKey.where(data: ssh_key_content).should_not be_blank | ||
end | ||
end | ||
|
||
describe "when an Mdm::Cred is a password" do | ||
let(:cred) do | ||
FactoryGirl.create(:mdm_cred, | ||
service: service, | ||
ptype: 'password', | ||
pass: FactoryGirl.build(:metasploit_credential_password, data: 'f00b4rawesomesauc3!').data | ||
) | ||
end | ||
|
||
before(:each) do | ||
migrator.convert_creds_in_workspace(cred.service.host.workspace) | ||
end | ||
|
||
it 'should create a new Password in the database' do | ||
Metasploit::Credential::Password.where(data: cred.pass).should_not be_blank | ||
end | ||
end | ||
|
||
describe "when an Mdm::Cred is another kind of hash" do | ||
let(:cred) do | ||
FactoryGirl.create(:mdm_cred, | ||
service: service, | ||
ptype: 'salted_sha1_hash', | ||
pass: FactoryGirl.build(:metasploit_credential_nonreplayable_hash, password_data: 'f00b4rawesomesauc3!').data | ||
) | ||
end | ||
|
||
before(:each) do | ||
migrator.convert_creds_in_workspace(cred.service.host.workspace) | ||
end | ||
|
||
it 'should create a new NonreplayableHash in the database' do | ||
Metasploit::Credential::NonreplayableHash.where(data: cred.pass).should_not be_blank | ||
end | ||
end | ||
end | ||
|
||
|
||
|
||
end | ||
|
||
end |