-
Notifications
You must be signed in to change notification settings - Fork 14.1k
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
base: master
Are you sure you want to change the base?
Fixing multiple bugs in credential generation + refactoring #19653
Conversation
64aeccc
to
4e9d771
Compare
…monstrate multiple bugs in the each method
4e9d771
to
7cab903
Compare
end | ||
end | ||
|
||
context "when every possible option is used" do |
There was a problem hiding this comment.
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.
…of password spraying) to make all tests pass
|
||
additional_publics.each do |add_public| | ||
yield Metasploit::Framework::Credential.new(public: add_public, private: add_public, realm: realm, private_type: :password) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can easily see the potential for a refactoring here as many blocks are almost complete c/p of previous blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, just my opinion but after having another look at this I think refactoring these sections may actually reduce readability.
I find this code hard to read anyway due to all of the nuances required for each possible configuration of password_spray
(not something that you have introduced, just the nature of password_spray's requirements itself).
I may be misunderstanding what you had in mind though, so feel free to let me know if that's the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a clear direction in mind, I just see potential because of the duplication of this big block:
if username.present?
yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)
end
if user_fd
user_fd.each_line do |user_from_file|
user_from_file.chomp!
yield Metasploit::Framework::Credential.new(public: user_from_file, private: user_from_file, realm: realm, private_type: private_type(password))
end
user_fd.seek(0)
end
additional_publics.each do |add_public|
yield Metasploit::Framework::Credential.new(public: add_public, private: add_public, realm: realm, private_type: :password)
end
It bothers me as if we have one slight modification of behaviour, we will have to modify the 3 (or 4, or 5?) of them.
A better target is not clear for me, but I had an idea that I didn't investigate yet: it would be to introduce two new classes, that would essentialy be iterator: one to iterate on usernames, and another one on passwords.
The code would then simply look something like this:
usernames_iterator.each do |username|
passwords_iterator.each do |password|
yield Metasploit::Framework::Credential.new...
end
end
And the password spraying branch would simply look like this:
passwords_iterator.each do |username|
usernames_iterator.each do |password|
yield Metasploit::Framework::Credential.new...
end
end
The main problem would be to handle the user_as_pass
parameter in a clean way.
(anyway, maybe we are good enough for now?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's still in draft but I thought I'd give it a once over anyway. Looks great and thanks for following up on the last pull request 🚀
@msjenkins-r7 retest this please |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @Mathiou04. Only getting back to this now as I was off for the holidays.
I took at look at these failing tests and it seem in the last commit some off the test expectations were changed. Mostly and_returns
being changed to and_yields
.
I have tested this locally and left suggestions that got everything passing for me.
I'll also add the diff, incase I missed any suggestions on the code or if you want to use it to patch in the changes 👍
diff --git a/spec/lib/metasploit/framework/credential_collection_spec.rb b/spec/lib/metasploit/framework/credential_collection_spec.rb
index dcb1682796..55b4bc2b12 100644
--- a/spec/lib/metasploit/framework/credential_collection_spec.rb
+++ b/spec/lib/metasploit/framework/credential_collection_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe Metasploit::Framework::CredentialCollection do
let(:pass_file) do
filename = "foo"
stub_file = StringIO.new("asdf\njkl\n")
- allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
+ allow(File).to receive(:open).with(filename,/^r/).and_return stub_file
filename
end
@@ -113,7 +113,7 @@ RSpec.describe Metasploit::Framework::CredentialCollection do
let(:pass_file) do
filename = "pass_file"
stub_file = StringIO.new("asdf\njkl\n")
- allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
+ allow(File).to receive(:open).with(filename,/^r/).and_return stub_file
filename
end
@@ -142,7 +142,7 @@ RSpec.describe Metasploit::Framework::CredentialCollection do
let(:user_file) do
filename = "user_file"
stub_file = StringIO.new("user1\nuser2\nuser3\n")
- allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
+ allow(File).to receive(:open).with(filename,/^r/).and_return stub_file
filename
end
@@ -173,7 +173,7 @@ RSpec.describe Metasploit::Framework::CredentialCollection do
let(:user_file) do
filename = "user_file"
stub_file = StringIO.new("user1\nuser2\nuser3\n")
- allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
+ allow(File).to receive(:open).with(filename,/^r/).and_return stub_file
filename
end
@@ -220,7 +220,7 @@ RSpec.describe Metasploit::Framework::CredentialCollection do
let(:user_file) do
filename = "user_file"
stub_file = StringIO.new("user1\nuser2\nuser3\n")
- allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
+ allow(File).to receive(:open).with(filename,/^r/).and_return stub_file
filename
end
@@ -255,7 +255,7 @@ RSpec.describe Metasploit::Framework::CredentialCollection do
let(:user_file) do
filename = "user_file"
stub_file = StringIO.new("user1\nuser2\nuser3\n")
- allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
+ allow(File).to receive(:open).with(filename,/^r/).and_return stub_file
filename
end
@@ -293,7 +293,7 @@ RSpec.describe Metasploit::Framework::CredentialCollection do
let(:pass_file) do
filename = "pass_file"
stub_file = StringIO.new("asdf\njkl\n")
- allow(File).to receive(:open).with(filename, /^r/).and_yield stub_file
+ allow(File).to receive(:open).with(filename, /^r/).and_return stub_file
filename
end
@@ -473,7 +473,7 @@ RSpec.describe Metasploit::Framework::CredentialCollection do
let(:user_file) do
filename = "foo"
stub_file = StringIO.new("asdf\njkl\n")
- allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
+ allow(File).to receive(:open).with(filename,/^r/).and_return stub_file
filename
end
@@ -502,7 +502,7 @@ RSpec.describe Metasploit::Framework::CredentialCollection do
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
+ allow(File).to receive(:open).with(filename,/^r/).and_return stub_file
filename
end
@@ -554,7 +554,7 @@ RSpec.describe Metasploit::Framework::CredentialCollection do
let(:user_file) do
filename = "user_file"
stub_file = StringIO.new("userfile")
- allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
+ allow(File).to receive(:open).with(filename,/^r/).and_return stub_file
filename
end
d4e1e04
to
2047671
Compare
@Mathiou04 I went ahead and made some changes to get the tests passing and resolved any comments I believed had been covered with the changes. I also marked the PR over to |
Thanks for looking into this, sorry I didn't came back earlier. In fact I just realized I had one more commit, that made the tests pass, and I didn't push it... I introduced a method
And a method
Is it ok if I try to rebase/merge with your work, to show you? |
@Mathiou04 Absolutely, that would be great! |
@cgranleese-r7 Here is he final version I worked on, sorry for the delay! I had other ideas for further refactoring and DRYing (mainly creating custom Iterators class for username and password), but I am not sure it is worth it and am struggling to ind the time to work on it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll handle these changes as I know you mentioned you don't have the cycles 👍
Thx for that. I am not sure I understand all the implications, but those new parameters feels kind of specific to a certain usage (and don't interact well with other params, like password spraying for example). Wouldn't it be cleaner for the CredentialCollection to have an interface with some kind of enum: Note: I can try to do the changes, they are not as time consuming as the ones I mentioned before 😉 |
Fixes #19652
Summary
In this PR, I added various tests on the CredentialCollection class, all of them currently failing and illustrating what I think are bugs.
Issues can be grouped in two categories:
additional_publics
arraypassword_spray
optionI added some comments on each test explaining what it is currently yielding (instead of the result I think should be expected - let me know if I misunderstood anything).
Refactoring opportunity
You will see that there are many bugs linked to the
password_spray
option.It seems that this branch of the code has been copy/pasted from the original branch (that didn't handle password spraying) and adapted.
There was not a lot of tests on this part of the code + the structure is difficult to maintain which probably explains those issues.
The original code branch (without spraying:
#each_unfiltered_username_first
) is also a bit complex with a lot of duplicated code.I think there are some opportunities to simplify and clarify the code a lot with some refactoring.
Non-regression tests
For this, I created 2 additional "non-regression" tests that activate all options and show how the credentials should be yielded.
There is one test with password spraying, one without.
I don't know if we want to keep them after the refactoring, but they will surely help.
Also, even if a part of the order in which the credentials are yielded should be "fixed", there are questions around others.
For example:
user_as_pass
option in case ofpassword_spraying
?Additional question
As a side note, the
nil_passwords
,blank_passwords
anduser_as_pass
options do not apply to the userpass file.Is it expected behaviour or should we extend this behaviour to users yielded in the userpass file?
Next steps