Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing multiple bugs in credential generation + refactoring #19653

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
250 changes: 250 additions & 0 deletions spec/lib/metasploit/framework/credential_collection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@
Metasploit::Framework::Credential.new(public: "foo", private: "bar"),
)
end

# REMOVE BEFORE COMMIT: question for the userpass file: do we want to make options work with it or not?
end

context "when given a pass_file and user_file" do
Expand Down Expand Up @@ -311,6 +313,20 @@
Metasploit::Framework::Credential.new(public: username, private: password),
)
end

context "when using password spraying" do
let(:password_spray) { true }

# REMOVE BEFORE COMMIT: yields nothings, fails because of bug in method
context "without password" do
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
let(:password) { nil }
specify do
expect { |b| collection.each(&b) }.to yield_successive_args(
Metasploit::Framework::Credential.new(public: username, private: nil),
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
)
end
end
end
end

context "when :blank_passwords is true" do
Expand All @@ -323,6 +339,240 @@
end
end

context "when given additional_publics" do
let(:username) { nil }
let(:password) { nil }
let(:additional_publics) { [ "test_public" ] }

context "when :user_as_pass is true" do
let(:user_as_pass) { true }

# REMOVE BEFORE COMMIT currently failing
specify do
expect { |b| collection.each(&b) }.to yield_successive_args(
Metasploit::Framework::Credential.new(public: "test_public", private: "test_public"),
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
)
end


context "when using password spraying" do
let(:password_spray) { true }

# REMOVE BEFORE COMMIT currently failing
specify do
expect { |b| collection.each(&b) }.to yield_successive_args(
Metasploit::Framework::Credential.new(public: "test_public", private: "test_public"),
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
)
end
end
end

context "when :nil_passwords is true" do
let(:nil_passwords) { true }

# REMOVE BEFORE COMMIT: this option is ignored currently for additional_publics
specify do
expect { |b| collection.each(&b) }.to yield_successive_args(
Metasploit::Framework::Credential.new(public: "test_public", private: nil),
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
)
end
end

context "when using password spraying" do
let(:password_spray) { true }

context "when :blank_passwords and :nil_password are true" do
let(:blank_passwords) { true }
let(:nil_passwords) { true }

context "with 2 additional_publics" do
let(:additional_publics) { [ "test_public1", "test_public2" ] }

# REMOVE BEFORE COMMIT: fails because no pwd spraying
specify do
expect { |b| collection.each(&b) }.to yield_successive_args(
Metasploit::Framework::Credential.new(public: "test_public1", private: ""),
Metasploit::Framework::Credential.new(public: "test_public2", private: ""),
Metasploit::Framework::Credential.new(public: "test_public1", private: nil),
Metasploit::Framework::Credential.new(public: "test_public2", private: nil),
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
)
end
end
end

context "when given a user file" do
let(:user_file) do
filename = "user_file"
stub_file = StringIO.new("asdf\njkl\n")
allow(File).to receive(:open).with(filename, /^r/).and_return stub_file

filename
end

# REMOVE BEFORE COMMIT: this also yields the usernames as passwords for the additional_public
context "when given a password" do
let(:password) { "password" }

specify do
expect { |b| collection.each(&b) }.to yield_successive_args(
Metasploit::Framework::Credential.new(public: "adsf", private: "password"),
Metasploit::Framework::Credential.new(public: "jkl", private: "password"),
Metasploit::Framework::Credential.new(public: "test_public", private: "password"),
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
)
end
end
end
end
end

context "when using password spraying" do
let(:password_spray) { true }
let(:username) { nil }
let(:password) { nil }

context "when :blank_passwords is true" do
let(:blank_passwords) { true }

context "with password (but no username)" do
let(:password) { "pass" }

# REMOVE BEFORE COMMIT: this yields empty creds (no username, no pass)
specify do
expect { |b| collection.each(&b) }.to yield_successive_args()
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
end
end

# REMOVE BEFORE COMMIT: yields nothings, fails because of bug in method
context "with username (but no password)" do
let(:username) { "user" }

specify do
expect { |b| collection.each(&b) }.to yield_successive_args(
Metasploit::Framework::Credential.new(public: username, private: ''),
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
)
end
end

context "when given a user_file" do
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
let(:user_file) do
filename = "foo"
stub_file = StringIO.new("asdf\njkl\n")
allow(File).to receive(:open).with(filename,/^r/).and_return stub_file

filename
end

# REMOVE BEFORE COMMIT: yields nothing, same for blank passwords option
specify do
expect { |b| collection.each(&b) }.to yield_successive_args(
Metasploit::Framework::Credential.new(public: "asdf", private: ''),
Metasploit::Framework::Credential.new(public: "jkl", private: ''),
)
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
end

context "when every possible option is used" do
Copy link
Contributor Author

@Mathiou04 Mathiou04 Nov 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to create some generic non-regression tests to demonstrate how the credentials should be yielded in case of various user/password data source (in fact in case of all them being active).

The idea is mainly to check that all credentials are present, but also to have the proper order.
I am not sure they should be kept after the fixes/refacto have been made (as there may be some question regarding the order, many are probably valid?) but here they will help demonstrate what we expect.

let(:nil_passwords) { true }
let(:blank_passwords) { true }
let(:username) { "user" }
let(:password) { "pass" }
let(:user_file) do
filename = "user_file"
stub_file = StringIO.new("userfile")
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file

filename
end
let(:pass_file) do
filename = "pass_file"
stub_file = StringIO.new("passfile\n")
allow(File).to receive(:open).with(filename,/^r/).and_return stub_file

filename
end
let(:user_as_pass) { false }
let(:userpass_file) do
filename = "userpass_file"
stub_file = StringIO.new("userpass_user userpass_pass\n")
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file

filename
end
let(:prepended_creds) { ['test_prepend'] }
let(:additional_privates) { ['test_private'] }
let(:additional_publics) { ['test_public'] }

# REMOVE BEFORE COMMIT: fails because of the useraspass error, then fails because of the nil value for addiitonal publics and should be ok then
specify do
expect { |b| collection.each(&b) }.to yield_successive_args(
"test_prepend",
Metasploit::Framework::Credential.new(public: "user", private: nil),
Metasploit::Framework::Credential.new(public: "user", private: "pass"),
Metasploit::Framework::Credential.new(public: "user", private: "user"),
Metasploit::Framework::Credential.new(public: "user", private: ""),
Metasploit::Framework::Credential.new(public: "user", private: "passfile"),
Metasploit::Framework::Credential.new(public: "user", private: "test_private"),
Metasploit::Framework::Credential.new(public: "userfile", private: nil),
Metasploit::Framework::Credential.new(public: "userfile", private: "pass"),
Metasploit::Framework::Credential.new(public: "userfile", private: "userfile"),
Metasploit::Framework::Credential.new(public: "userfile", private: ""),
Metasploit::Framework::Credential.new(public: "userfile", private: "passfile"),
Metasploit::Framework::Credential.new(public: "userfile", private: "test_private"),
Metasploit::Framework::Credential.new(public: "userpass_user", private: "userpass_pass"),
Metasploit::Framework::Credential.new(public: "test_public", private: nil), # missing this case
Metasploit::Framework::Credential.new(public: "test_public", private: "pass"),
Metasploit::Framework::Credential.new(public: "test_public", private: "test_public"),
Metasploit::Framework::Credential.new(public: "test_public", private: ""),
Metasploit::Framework::Credential.new(public: "test_public", private: "passfile"),
Metasploit::Framework::Credential.new(public: "test_public", private: "test_private")
)
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
end

context "when using password spraying" do
let(:password_spray) { true }
let(:user_file) do
filename = "user_file"
stub_file = StringIO.new("userfile")
allow(File).to receive(:open).with(filename,/^r/).and_return stub_file

filename
end
let(:pass_file) do
filename = "pass_file"
stub_file = StringIO.new("passfile\n")
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file

filename
end

specify do
expect { |b| collection.each(&b) }.to yield_successive_args(
"test_prepend",
Metasploit::Framework::Credential.new(public: "user", private: nil),
Metasploit::Framework::Credential.new(public: "userfile", private: nil),
Metasploit::Framework::Credential.new(public: "test_public", private: nil),
Metasploit::Framework::Credential.new(public: "user", private: "pass"),
Metasploit::Framework::Credential.new(public: "userfile", private: "pass"),
Metasploit::Framework::Credential.new(public: "test_public", private: "pass"),
Metasploit::Framework::Credential.new(public: "user", private: "user"),
Metasploit::Framework::Credential.new(public: "userfile", private: "userfile"),
Metasploit::Framework::Credential.new(public: "test_public", private: "test_public"),
Metasploit::Framework::Credential.new(public: "user", private: ""),
Metasploit::Framework::Credential.new(public: "userfile", private: ""),
Metasploit::Framework::Credential.new(public: "test_public", private: ""),
Metasploit::Framework::Credential.new(public: "user", private: "passfile"),
Metasploit::Framework::Credential.new(public: "userfile", private: "passfile"),
Metasploit::Framework::Credential.new(public: "test_public", private: "passfile"),
Metasploit::Framework::Credential.new(public: "userpass_user", private: "userpass_pass"),
Metasploit::Framework::Credential.new(public: "user", private: "test_private"),
Metasploit::Framework::Credential.new(public: "userfile", private: "test_private"),
Metasploit::Framework::Credential.new(public: "test_public", private: "test_private"),
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
)
end
end
end
end

describe "#empty?" do
Expand Down
Loading