Ceenix/metasploit-framework — pull request #1 by dmaloney-r7 (2014-08-29 18:27:04+00:00)
Title: Wordpress xmlrpc login scanner

Ceenix, this PR refactors your Wordpress XMLRPC login work to use a LoginScanner class.

your branch was way out of date so unfortunately this looks huge. I suggest rebasing your fork off of framework master before looking at this PR. That should slim the diff back down to just what i changed.

There are 2 new files and changes to your module file

the first new file is in lib/metasploit/framework/login_scanner/wordpress_rpc.rb

This is a simple LoginScanner class for your target. It inherits from HTTP as ssen in the class definition line `class WordpressRPC < HTTP`

We define set_sane_defaults for this class to just freeze the method as POST since that's what we'll always want.

I moved your generate_xml_request method over to this class, and added YARD docs as you can see in
```
# This method generates the XML data for the RPC login request
# @param user [String] the username to authenticate with
# @param pass [String] the password to authenticate with
# @return [String] the generated XML body for the request
def generate_xml_request(user, pass)
xml = ""
xml << ''
xml << 'wp.getUsers'
xml << '1'
xml << "#{user}"
xml << "#{pass}"
xml << ''
xml << ''
xml
end
```

Then we override the inherited attempt_login method. This method takes the credential object, creates our http client, and crafts and sends our request. It then checks the results based on your existing criteria , and returns a result object.

in spec/lib/metasploit/framework/login_scanner/wordpress_rpc_spec.rb we have just the basic specs for this class added.
It uses a set of shared example groups that test the behaviour this LoginScanner shares with all the other http ones.
There's not a lot else to test here actually. We could do some more later, but this is fine for now

Finally we refactor the module you wrote.

We remove the generate_xml_request method first, since it was mvoed to our login_scanner class.
We completely get rid of the do_login method, as it is not needed.
We create a CredentialCollection object based on the datastore options. This will be responsible for assembling the credentials and handing them off inside the LoginScanner one at a time.
```
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'],
)
```
We then create a new WordpressRPC loginscanner object. We initialize that scanner with the CredentialCollection, our target information, and the uri using the same method you had used before.
```
scanner = Metasploit::Framework::LoginScanner::WordpressRPC.new(
host: ip,
port: rport,
uri: wordpress_url_xmlrpc,
proxies: datastore["PROXIES"],
cred_details: cred_collection,
stop_on_success: datastore['STOP_ON_SUCCESS'],
connection_timeout: 5,
)
```

Then we call the scan! method and pass it a block.

In this block we initialize a credential_data hash from the yielded result object. This hash will be sued for reporting results from the scanner.
We merge data from the module into that hash, specifically the workspace we're in, and the module we're currently in.

Then we check what the result of that login attempt was. If it was successful then we record a new credential and login.
If it was not successful we call invalidate_login which will check to see if we already had a login for that credential on that service. If there was then it will update it's status to mark that we were not actually able to use it.

```
scanner.scan! do |result|
credential_data = result.to_h
credential_data.merge!(
module_fullname: self.fullname,
workspace_id: myworkspace_id
)
case result.status
when Metasploit::Model::Login::Status::SUCCESSFUL
print_brute :level => :good, :ip => ip, :msg => "Success: '#{result.credential}'"
credential_core = create_credential(credential_data)
credential_data[:core] = credential_core
create_credential_login(credential_data)
:next_user
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
invalidate_login(credential_data)
:abort
when Metasploit::Model::Login::Status::INCORRECT
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
invalidate_login(credential_data)
end
end
```

and that's it. We're done. You get a nice neat little package in your module, and the main logic is encapsulated in an actual object class that is reusable and testable.

Let me know if you have any questions, concerns, or problems. also let me know if i screwed anything up as I don't have a test box handy.