Writing out a custom SSH-authentication with a brute force attack

For checking weak login credentials, we need to perform an authentication brute force attack. The agenda of such tests is not only to test an application against weak credentials, but to ensure proper authorization and access controls as well. These tests ensure that the attackers cannot simply bypass the security paradigm by trying the non-exhaustive brute force attack, and are locked out after a certain number of random guesses.

Designing the next module for authentication testing on the SSH service, we will look at how easy it is to design authentication-based checks in Metasploit, and perform tests that attack authentication. Let's now jump into the coding part and begin designing a module, as follows:

require 'metasploit/framework/credential_collection' 
require 'metasploit/framework/login_scanner/ssh' 
 
class MetasploitModule < Msf::Auxiliary 
 
  include Msf::Auxiliary::Scanner 
  include Msf::Auxiliary::Report 
  include Msf::Auxiliary::AuthBrute 
 
  def initialize 
    super( 
      'Name'        => 'SSH Scanner', 
      'Description' => %q{ 
        My Module. 
      }, 
      'Author'      => 'Nipun Jaswal', 
      'License'     => MSF_LICENSE 
    ) 
 
    register_options( 
      [ 
        Opt::RPORT(22) 
      ]) 
  end 

In the previous examples, we have already seen the importance of using Msf::Auxiliary::Scanner and Msf::Auxiliary::Report. Let's see the other included libraries and understand their usage through the following table:

In the preceding code, we also included two files, which are metasploit/framework/login_scanner/ssh and metasploit/framework/credential_collection. The metasploit/framework/login_scanner/ssh file includes the SSH login scanner library that eliminates all manual operations and provides an underlying API to SSH scanning. The metasploit/framework/credential_collection file helps to create multiple credentials based on user inputs from the datastore. Next, we simply define the type of the module we are building.

In the initialize section, we define the basic information for this module. Let's see the next section:

def run_host(ip) 
    cred_collection = Metasploit::Framework::CredentialCollection.new( 
      blank_passwords: datastore['BLANK_PASSWORDS'], 
      pass_file: datastore['PASS_FILE'], 
      password: datastore['PASSWORD'], 
      user_file: datastore['USER_FILE'], 
      userpass_file: datastore['USERPASS_FILE'], 
      username: datastore['USERNAME'], 
      user_as_pass: datastore['USER_AS_PASS'], 
    ) 
 
    scanner = Metasploit::Framework::LoginScanner::SSH.new( 
      host: ip, 
      port: datastore['RPORT'], 
      cred_details: cred_collection, 
      proxies: datastore['Proxies'], 
      stop_on_success: datastore['STOP_ON_SUCCESS'], 
      bruteforce_speed: datastore['BRUTEFORCE_SPEED'], 
      connection_timeout: datastore['SSH_TIMEOUT'], 
      framework: framework, 
      framework_module: self, 
    ) 

We can see that we have two objects in the preceding code, which are cred_collection and scanner. An important point to make a note of here is that we do not require any manual methods of logging into the SSH service because the login scanner does everything for us. Therefore, cred_collection is doing nothing but yielding sets of credentials based on the datastore options set on a module. The beauty of the CredentialCollection class lies in the fact that it can take a single username/password combination, wordlists, and blank credentials all at once, or one of them at a time.

All login scanner modules require credential objects for their login attempts. The scanner object defined in the preceding code initializes an object for the SSH class. This object stores the address of the target, port, credentials as generated by the CredentialCollection class, and other data-like proxy information, stop_on_success that will stop the scanning on the successful credential match, brute force speed, and the value of the attempted timeout.

Up to this point in the module, we have created two objects; cred_collection, which will generate credentials based on the user input, and the scanner object, which will use those credentials to scan the target. Next, we need to define a mechanism so that all the credentials from a wordlist are defined as single parameters and are tested against the target.

We have already seen the usage of run_host in previous examples. Let's see what other vital functions from various libraries we are going to use in this module:

Let's see how we can achieve that:

   scanner.scan! do |result| 
      credential_data = result.to_h 
      credential_data.merge!( 
          module_fullname: self.fullname, 
          workspace_id: myworkspace_id 
      ) 
         if result.success? 
        credential_core = create_credential(credential_data) 
        credential_data[:core] = credential_core 
        create_credential_login(credential_data) 
        print_good "#{ip} - LOGIN SUCCESSFUL: #{result.credential}" 
         else 
        invalidate_login(credential_data) 
        print_status "#{ip} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})" 
         end 
   end 
end 
end 

It can be observed that we used .scan to initialize the scan, and this will perform all the login attempts by itself, which means we do not need to specify any other mechanism explicitly. The .scan instruction is exactly like an each loop in Ruby.

In the next statement, the results get saved in the result object and are assigned to the credential_data variable using the to_h method, which will convert the data to hash format. In the next line, we merge the module name and workspace ID into the credential_data variable. Next, we run an if-else check on the result object using the .success, variable, which denotes successful login attempts into the target. If the result.success? variable returns true, we mark the credential as a successful login attempt and store it in the database. However, if the condition is not satisfied, we pass the credential_data variable to the invalidate_login method that denotes a failed login.

It is advisable to run all the modules in this chapter and all the later chapters only after performing a consistency check through msftidy. Let's try running the module, as follows:

We can see that we were able to log in with claire and 18101988 as the username and password. Let's see if we were able to log the credentials into the database using the creds command:

We can see that we have the details logged into the database, and they can be used to carry out advanced attacks, or for reporting.