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

Add the DB_SKIP_EXISTING option to the AuthBrute mixin #15630

Merged
merged 9 commits into from Sep 24, 2021

Conversation

zeroSteiner
Copy link
Contributor

@zeroSteiner zeroSteiner commented Aug 31, 2021

This updates the PrivateCredentialCollection class to allow a filter callback to be defined on it. It needs to be a callback, because the CredentialCollection instance is often passed to another object that will blindly iterate over it. The other object (like a login scanner) should be unaware of the filter. The AuthBrute mixin is then updated to create the CredentialCollection using the new build_credential_collection method which will check the datastore options set the filter as appropriate.

Prior to this, all of the modules using the AuthBrute mixin were building their CredentialCollection instances themselves which created alot of redundant copy-pasta code. It also meant that it was up to the options to honor the DB_ALL_* datastore options that they had defined by including the AuthBrute mixin. This lead to quite a few modules that appear to have been ignoring the options. All of the modules have been migrated to use the new mixin method. A few that were only brute-forcing passwords before the PrivateCredentialCollection was created were switched to using that. Modules that are using the PrivateCredentialCollection probably don't make sense to be filtered since there is no username field to filter on in the same way.

The new DB_SKIP_EXISTING option is an enum so it can be extended with other conditions in the future. Right now three are defined:

  • none - Don't perform any filtering. This is the default value and retains the existing behavior.
  • user - Filter out credentials when the username can be found in the database. This ignores the realm field.
  • user&realm - Filter out credentials when the username and realm match a single object in the database.

Verification

  • Review the modules, make sure the username and password options were not changed in the diff
    • The username and password options are the most commonly specified options to the mixin method because modules pull them from different datastore value like USERNAME, SMBUser and HttpUsername. Need to make sure these didn't change.
  • Load up a module to test
  • Some easy ones to setup test environments might be
    • auxiliary/scanner/ssh/ssh_login
    • auxiliary/scanner/smb/smb_login
    • auxiliary/scanner/http/http_login
  • Start with no credentials in the database (delete them by running creds -d)
  • Run the module and get a successful login
  • Run the module again but with DB_SKIP_EXISTING set to user
  • See that the username that was previously successful is skipped entirely. You probably need to set VERBOSE to true to see each individual attempt.
  • Do the same thing with DB_SKIP_EXISTING set to user&realm. See the expected results depending on the module. It will probably filter out the user, but it depends.

Example

In the following example, all three DB_SKIP_EXISTING cases are shown using the auxiliary/scanner/ssh/ssh_login module. The smcintyre user has a realm defined. The ssh_login module does not define a realm so when the filtering is set to user&realm the smcintyre user is included because the realm the module is using is nil.

msf6 auxiliary(scanner/ssh/ssh_login) > run

[*] 192.168.250.8:22 - Starting bruteforce
[-] 192.168.250.8:22 - Failed: 'smcintyre:password'
[-] 192.168.250.8:22 - Failed: 'smcintyre:admin'
[-] 192.168.250.8:22 - Failed: 'root:password'
[-] 192.168.250.8:22 - Failed: 'root:admin'
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/ssh/ssh_login) > creds
Credentials
===========

host  origin  service  public     private    realm      private_type  JtR Format
----  ------  -------  ------     -------    -----      ------------  ----------
                       root       Password1             Password      
                       smcintyre  Password1  WORKGROUP  Password      

msf6 auxiliary(scanner/ssh/ssh_login) > run DB_SKIP_EXISTING=user

[*] 192.168.250.8:22 - Starting bruteforce
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/ssh/ssh_login) > run DB_SKIP_EXISTING=user&realm 

[*] 192.168.250.8:22 - Starting bruteforce
[-] 192.168.250.8:22 - Failed: 'smcintyre:password'
[-] 192.168.250.8:22 - Failed: 'smcintyre:admin'
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/ssh/ssh_login) >

@zeroSteiner
Copy link
Contributor Author

Note to self: the Creating Metasploit Framework LoginScanners and How to write a HTTP LoginScanner pages should be updated to callout the new mixin method is the preferred way to initialize the CredentialCollection instance.

@jheysel-r7
Copy link
Contributor

Awesome job on this @smcintyre-r7. Great refactor, this cleans up a lot of copy and pasted code. I've tested the Auth Brute mixin against the three modules suggested in the description and they seem to be working as expected:

auxiliary/scanner/ssh/ssh_login

msf6 > creds -d
Credentials
===========

host             origin  service       public   private                realm      private_type  JtR Format
----             ------  -------       ------   -------                -----      ------------  ----------
                                       root     Password1              WORKGROUP  Password
                                       jheysel  Password1                         Password
                                       root     not the root password             Password
                                       guest    guest password                    Password
192.168.123.146          22/tcp (ssh)  msfuser  notpassword                       Password

[*] Deleted 5 creds
msf6 > use scanner/ssh/ssh_login
msf6 auxiliary(scanner/ssh/ssh_login) > set rhosts 192.168.123.146
rhosts => 192.168.123.146
msf6 auxiliary(scanner/ssh/ssh_login) > set username msfuser
username => msfuser
msf6 auxiliary(scanner/ssh/ssh_login) > set password notpassword
password => notpassword
msf6 auxiliary(scanner/ssh/ssh_login) > set verbose true
verbose => true
msf6 auxiliary(scanner/ssh/ssh_login) > run

[*] 192.168.123.146:22 - Starting bruteforce
[+] 192.168.123.146:22 - Success: 'msfuser:notpassword' 'uid=1000(msfuser) gid=1000(msfuser) groups=1000(msfuser),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(lxd),132(sambashare) Linux ubuntu 5.11.0-27-generic #29~20.04.1-Ubuntu SMP Wed Aug 11 15:58:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux '
[*] Command shell session 1 opened (192.168.123.1:61007 -> 192.168.123.146:22) at 2021-09-23 14:00:52 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/ssh/ssh_login) > run DB_SKIP_EXISTING=user

[*] 192.168.123.146:22 - Starting bruteforce
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/ssh/ssh_login) > run DB_SKIP_EXISTING=user&realm

[*] 192.168.123.146:22 - Starting bruteforce
[*] Scanned 1 of 1 hosts (100% complete)

auxiliary/scanner/smb/smb_login

msf6 > creds -d
Credentials
===========

host             origin           service       public   private      realm  private_type  JtR Format
----             ------           -------       ------   -------      -----  ------------  ----------
192.168.123.146  192.168.123.146  22/tcp (ssh)  msfuser  notpassword         Password

[*] Deleted 1 creds
msf6 > use scanner/smb/smb_login
msf6 auxiliary(scanner/smb/smb_login) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf6 auxiliary(scanner/smb/smb_login) > set SMBUser msfuser
SMBUser => msfuser
msf6 auxiliary(scanner/smb/smb_login) > set SMBPass notpassword
SMBPass => notpassword
msf6 auxiliary(scanner/smb/smb_login) > set SMBDomain SuperAwesomeShare
SMBDomain => SuperAwesomeShare
msf6 auxiliary(scanner/smb/smb_login) > options

Module options (auxiliary/scanner/smb/smb_login):

   Name               Current Setting    Required  Description
   ----               ---------------    --------  -----------
   ABORT_ON_LOCKOUT   false              yes       Abort the run when an account lockout is detected
   BLANK_PASSWORDS    false              no        Try blank passwords for all users
   BRUTEFORCE_SPEED   5                  yes       How fast to bruteforce, from 0 to 5
   DB_ALL_CREDS       false              no        Try each user/password couple stored in the current database
   DB_ALL_PASS        false              no        Add all passwords in the current database to the list
   DB_ALL_USERS       false              no        Add all users in the current database to the list
   DB_SKIP_EXISTING   none               no        Skip existing credentials stored in the current database (Accepted: none, user, user&realm)
   DETECT_ANY_AUTH    false              no        Enable detection of systems accepting any authentication
   DETECT_ANY_DOMAIN  false              no        Detect if domain is required for the specified user
   PASS_FILE                             no        File containing passwords, one per line
   PRESERVE_DOMAINS   true               no        Respect a username that contains a domain name.
   Proxies                               no        A proxy chain of format type:host:port[,type:host:port][...]
   RECORD_GUEST       false              no        Record guest-privileged random logins to the database
   RHOSTS             127.0.0.1          yes       The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Using-Metasploit
   RPORT              445                yes       The SMB service port (TCP)
   SMBDomain          SuperAwesomeShare  no        The Windows domain to use for authentication
   SMBPass            notpassword        no        The password for the specified username
   SMBUser            msfuser            no        The username to authenticate as
   STOP_ON_SUCCESS    false              yes       Stop guessing when a credential works for a host
   THREADS            1                  yes       The number of concurrent threads (max one per host)
   USERPASS_FILE                         no        File containing users and passwords separated by space, one pair per line
   USER_AS_PASS       false              no        Try the username as the password for all users
   USER_FILE                             no        File containing usernames, one per line
   VERBOSE            true               yes       Whether to print output for all attempts

msf6 auxiliary(scanner/smb/smb_login) > run

[*] 127.0.0.1:445         - 127.0.0.1:445 - Starting SMB login bruteforce
[+] 127.0.0.1:445         - 127.0.0.1:445 - Success: 'SuperAwesomeShare\msfuser:notpassword'
[*] 127.0.0.1:445         - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/smb/smb_login) > run DB_SKIP_EXISTING=user

[*] 127.0.0.1:445         - 127.0.0.1:445 - Starting SMB login bruteforce
[*] 127.0.0.1:445         - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/smb/smb_login) > creds
Credentials
===========

host       origin     service        public   private      realm  private_type  JtR Format
----       ------     -------        ------   -------      -----  ------------  ----------
127.0.0.1  127.0.0.1  445/tcp (smb)  msfuser  notpassword         Password

msf6 auxiliary(scanner/smb/smb_login) > run DB_SKIP_EXISTING=user&realm

[*] 127.0.0.1:445         - 127.0.0.1:445 - Starting SMB login bruteforce
[+] 127.0.0.1:445         - 127.0.0.1:445 - Success: 'SuperAwesomeShare\msfuser:notpassword'
[*] 127.0.0.1:445         - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

auxiliary/scanner/http/http_login

msf6 auxiliary(scanner/http/http_login) > creds -d
Credentials
===========

host       origin     service          public  private      realm  private_type  JtR Format
----       ------     -------          ------  -------      -----  ------------  ----------
127.0.0.1  127.0.0.1  8080/tcp (http)  tomcat  notpassword         Password

[*] Deleted 1 creds
msf6 auxiliary(scanner/http/http_login) > options

Module options (auxiliary/scanner/http/http_login):

   Name              Current Setting                                                                      Required  Description
   ----              ---------------                                                                      --------  -----------
   AUTH_URI          /manager/html                                                                        no        The URI to authenticate against (default:auto)
   BLANK_PASSWORDS   false                                                                                no        Try blank passwords for all users
   BRUTEFORCE_SPEED  5                                                                                    yes       How fast to bruteforce, from 0 to 5
   DB_ALL_CREDS      false                                                                                no        Try each user/password couple stored in the current database
   DB_ALL_PASS       false                                                                                no        Add all passwords in the current database to the list
   DB_ALL_USERS      false                                                                                no        Add all users in the current database to the list
   DB_SKIP_EXISTING  none                                                                                 no        Skip existing credentials stored in the current database (Accepted: none, user, user&realm)
   PASS_FILE         /Users/jheysel/rapid7/metasploit-framework/data/wordlists/http_default_pass.txt      no        File containing passwords, one per line
   Proxies                                                                                                no        A proxy chain of format type:host:port[,type:host:port][...]
   REQUESTTYPE       GET                                                                                  no        Use HTTP-GET or HTTP-PUT for Digest-Auth, PROPFIND for WebDAV (default:GET)
   RHOSTS            127.0.0.1                                                                            yes       The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Using-Metasploit
   RPORT             8080                                                                                 yes       The target port (TCP)
   SSL               false                                                                                no        Negotiate SSL/TLS for outgoing connections
   STOP_ON_SUCCESS   false                                                                                yes       Stop guessing when a credential works for a host
   THREADS           1                                                                                    yes       The number of concurrent threads (max one per host)
   USERPASS_FILE     /Users/jheysel/rapid7/metasploit-framework/data/wordlists/http_default_userpass.txt  no        File containing users and passwords separated by space, one pair per line
   USER_AS_PASS      false                                                                                no        Try the username as the password for all users
   USER_FILE         /Users/jheysel/rapid7/metasploit-framework/data/wordlists/http_default_users.txt     no        File containing users, one per line
   VERBOSE           true                                                                                 yes       Whether to print output for all attempts
   VHOST                                                                                                  no        HTTP server virtual host
msf6 auxiliary(scanner/http/http_login) > run

[*] Attempting to login to http://127.0.0.1:8080/manager/html
[+] 127.0.0.1:8080 - Success: 'tomcat:notpassword'
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/http_login) > run DB_SKIP_EXISTING=user

[*] Attempting to login to http://127.0.0.1:8080/manager/html
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/http_login) > run DB_SKIP_EXISTING=user&realm

[*] Attempting to login to http://127.0.0.1:8080/manager/html
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

@smcintyre-r7 Gentle reminder about this comment:
#15630 (comment)
do you want to address this before landing?

@zeroSteiner
Copy link
Contributor Author

I'll update the docs as described in that comment after PR has been landed and everything's finalized. That way if there are any changes that need to be made I can just write the docs once 😄 .

@jheysel-r7 jheysel-r7 merged commit 7c7f8b8 into rapid7:master Sep 24, 2021
@jheysel-r7
Copy link
Contributor

Release Notes

This adds the option DB_SKIP_EXISTING to the AuthBrute mixin to give users the option to skip credentials already in the database when preforming brute force attacks.

@erran-r7 erran-r7 added the rn-enhancement release notes enhancement label Oct 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rn-enhancement release notes enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants