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

Multi-threaded check command, plus aux scanner support #2922

Merged
merged 20 commits into from Feb 4, 2014

Conversation

wchen-r7
Copy link
Contributor

This PR addresses the following redmine tickets:

It is also the final completion of this ticket:
http://dev.metasploit.com/redmine/issues/8737

Usage:

Basically there are two ways to use this command:

Example 1:

check [an IP or an IP range]

Example 2:

> set [RHOST or RHOSTS] IP
> check

If you wish to have more threads, then do:

set threads [number of threads]

Please note: Having more threads may result in missed opportunities (meaning that even though a host is vulnerable, it may not be flagged).

Verifications

Before you begin, please make sure you have the following for testing:

  • A Windows XP box vulnerable to ms08-067
  • A fake web server: ruby -run -e httpd . -p 8181
  • Have aux_scanner_check_test.rb for testing: https://gist.github.com/wchen-r7/f297c96881f073336208, you should probably place it under: auxiliary/scanner/http because this is a scanner module.

And now let's move on to these scenarios:

Scenario 1: With ms08_067_netapi, set the RHOST and run check

  • Start msfconsole
  • Enter command: use exploit/windows/smb/ms08_067_netapi
  • Enter command: set rhost [IP]
  • Enter command: check
  • You should see the check run and flag your test box as vulnerable

Scenario 2: With ms08_067_netapi, check multiple hosts

  • Start msfconsole
  • Enter command: use exploit/windows/smb/ms08_067_netapi
  • Enter command: check [range] (keep the range small or it will run for a long time)
  • You should see the check run, and eventually flag your test box as vulnerable

Scenario 3: With ms08_067_netapi, set threads, and run multiple hosts

  • Start msfconsole
  • Enter command: use exploit/windows/smb/ms08_067_netapi
  • Enter command: set threads 10
  • Enter command: check [range] (ideally try to keep the range small for faster testing)
  • You should see the check to run faster.
  • You should also see your test box flagged as vulnerable. However, if you set the threads too high, you might not see that.

Scenario 4: With ms08_067_netapi, abort scanning while on a multi-threaded scan

  • Start msfconsole
  • Enter command: use exploit/windows/smb/ms08_067_netapi
  • Enter command: set threads 10
  • Enter command: check [range] (ideally try to keep the range small for faster testing)
  • When the check starts to run, press [CTRL]+[C] to abort
  • You should see "Caught interrupt from the console", and no more scan messages.

Scenario 5: With ms08_067_netapi, run check without any arguments

  • Start msfconsole
  • Enter command: use exploit/windows/smb/ms08_067_netapi
  • Enter command: check
  • You should see an option validation error about "RHOST"

Scenario 6: Run nodejs_pipelining.rb against the fake web server

  • Start msfconsole
  • Enter command: use auxiliary/dos/http/nodejs_pipelining
  • Enter command: set rport [fake web server PORT]
  • Enter command: check [fake web server IP]
  • You should see a check message

Scenario 7: Run aux_scanner_check_test against the web server

  • Start msfconsole
  • Enter command: use [path to aux_scanner_check_test.rb]
  • Enter command: set rport [fake web server rport]
  • Enter command: check [web server IP]
  • You should see a check message

Scenario 8: With aux_scanner_check_test, run check without any arguments

  • Start msfconsole
  • Enter command: use [path to aux_scanner_check_test.rb]
  • Enter command: check
  • You should see an option validation error about "RHOSTS"

Scenario 9: Run a module that does not support check at all

  • Start msfconsole
  • Enter command: use auxiliary/scanner/http/soap_xml
  • Enter command: set rhosts [IP]
  • Enter command: check
  • You should see: "This module does not support check."

@@ -32,6 +32,17 @@ def initialize(info = {})
end


def check
Copy link
Contributor

Choose a reason for hiding this comment

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

The returns are unnecessary as the begin rescue will be treated as a single statement and if there is no error then the check_host's return will be return value and if there is an error then the rescue's value will be the return value. Also remove the unnecessary self when calling replicant.

def check
  nmod = replicant

  begin
    nmod.check_host(datastore['RHOST])
  rescue  NoMethodError
    Exploit::CheckCode::Unsupported  
  end
end

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh, ok.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Applied in eec01e7

break unless ip

@tl << framework.threads.spawn("CheckHost-#{ip}", false, ip.dup) { |tip|
mod.datastore['RHOST'] = tip
Copy link
Contributor

Choose a reason for hiding this comment

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

This is still not thread-safe, but github's hiding our discussion because it was on an outdated diff. To fix this you need to make the threads not share mutable data or only share read-only data. The easiest way I can think of to do this is to create a replicant on line 85 in the parent thread and then use that replicant in the child thread block of 87 to 88. This will be inefficient since check_simple itself calls replicant internally, but modifying the datastore has to happen on a separate module instance for each thread:

instance = mod.replicant
instance.datastore['RHOST'] = ip.dup

thread = framework.threads.spawn("CheckHost-#{ip}", false) {
  instance.check_simple
}
@tl << thread

I moved setting the datastore outside the thread since it doesn't take that long and doesn't really need to be threaded.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm... actually calling replicant should not affect the original mod, but could take some time since it's duping a lot of variables, so it's probably better to move it into the thread.

thread = framework.threads.spawn("CheckHost-#{ip}", false) {
  instance = mod.replicant
  instance.datastore['RHOST'] = ip.dup
  instance.check_simple
}
@tl << thread

The ip.dup is still necessary in case any of the code that uses 'RHOST' uses mutators like strip! that would modify the String object instead of just replacing it in the data store.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So this solution doesn't actually work because "instance" doesn't have check_simple(). Luke, what was the other solution you were talking about at the airport? Did you say extend a module in there? So check_simple() exists in both exploit and auxiliary, not sure how you're supposed to extend that in a more universal way.... could you please repeat what you said? Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

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

You need to emulate what happens when you call Msf::ModuleSet#create: The instance is initialized from the class and then the creation event is sent to the framework:

instance = mod.replicant
instance.datastore['RHOST'] = ip.dup
framework.events.on_module_created(instance)
instance.check_simple

The framework.events.on_module_created is implemented as Msf::Simple::Framework#on_module_created:

  def on_module_created(instance)
    Msf::Simple::Framework.simplify_module(instance)
  end

Msf::Simple::Framework.simplify_module extends the instance with a module_type specific Simple module:

 instance.extend(ModuleSimplifiers[instance.type])

It is this extend that finally gives your instance the check_simple method.

Copy link
Contributor

Choose a reason for hiding this comment

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

And, no, it wasn't immediately obvious that the event system would add the simple methods to me either. I only figured it out when I left out the framework.events.on_module_created call on my module caching branches. The Simple namespace in lib/msf/base should probably be merged into lib/msf/core's normal modules and classes since nothing really works with non-simplified modules and frameworks, but that's for later for me at least after rounds 1 and 2 of the module cache are complete.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes, that works. Thank you!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Applied in 9f669a8

@wchen-r7
Copy link
Contributor Author

So if I'm not mistaken, all concerns are addressed at this point. I'm gonna go ahead and retest everything now. And Luke, if you see any more (or things that still aren't properly addressed), please let me know. And thanks again.

@wchen-r7
Copy link
Contributor Author

I finished testing and only had to fix ::Interrupt exception handling. Also passed Travis again. This PR is back on track.

@wvu
Copy link
Contributor

wvu commented Feb 3, 2014

Testing...

@wvu
Copy link
Contributor

wvu commented Feb 3, 2014

Checking boxes, rather. :)

@wvu
Copy link
Contributor

wvu commented Feb 3, 2014

I checked all the boxes. Phew.

@wvu
Copy link
Contributor

wvu commented Feb 3, 2014

Scenario 1:

msf > use exploit/windows/smb/ms08_067_netapi 
msf exploit(ms08_067_netapi) > set RHOST 172.16.126.128
RHOST => 172.16.126.128
msf exploit(ms08_067_netapi) > check
[+] 172.16.126.128:445 - The target is vulnerable.
msf exploit(ms08_067_netapi) > 

Scenario 2:

msf exploit(ms08_067_netapi) > unset RHOST 
Unsetting RHOST...
msf exploit(ms08_067_netapi) > check 172.16.126.127-130
[*] 172.16.126.127:445 - Cannot reliably check exploitability.
[+] 172.16.126.128:445 - The target is vulnerable.
[*] 172.16.126.129:445 - The target is not exploitable.
[*] 172.16.126.130:445 - Cannot reliably check exploitability.
msf exploit(ms08_067_netapi) > 

Scenario 3:

msf exploit(ms08_067_netapi) > set THREADS 10
THREADS => 10
msf exploit(ms08_067_netapi) > check 172.16.126.127-130
[*] 172.16.126.129:445 - The target is not exploitable.
[+] 172.16.126.128:445 - The target is vulnerable.
[*] 172.16.126.130:445 - Cannot reliably check exploitability.
[*] 172.16.126.127:445 - Cannot reliably check exploitability.
msf exploit(ms08_067_netapi) > 

Scenario 4:

msf exploit(ms08_067_netapi) > check 172.16.126.127-130
[*] 172.16.126.129:445 - The target is not exploitable.
[+] 172.16.126.128:445 - The target is vulnerable.
^C[*] Caught interrupt from the console...
msf exploit(ms08_067_netapi) > 

Scenario 5:

msf exploit(ms08_067_netapi) > check
[-] Check failed: The following options failed to validate: RHOST.
msf exploit(ms08_067_netapi) > 

Scenario 6:

msf exploit(ms08_067_netapi) > use auxiliary/dos/http/nodejs_pipelining 
msf auxiliary(nodejs_pipelining) > set RPORT 8181
RPORT => 8181
msf auxiliary(nodejs_pipelining) > check 127.0.0.2
[*] 127.0.0.2:8181 - The target appears to be vulnerable.
msf auxiliary(nodejs_pipelining) > 

Scenario 7:

msf auxiliary(nodejs_pipelining) > use auxiliary/scanner/http/aux_scanner_check_test 
msf auxiliary(aux_scanner_check_test) > set RPORT 8181
RPORT => 8181
msf auxiliary(aux_scanner_check_test) > check 127.0.0.2
[+] 127.0.0.2:8181 - The target is vulnerable.
[*] Checked 1 of 1 hosts (100% complete)
msf auxiliary(aux_scanner_check_test) > 

Scenario 8:

msf auxiliary(aux_scanner_check_test) > check
[-] Check failed: The following options failed to validate: RHOSTS.
msf auxiliary(aux_scanner_check_test) > 

Scenario 9:

msf auxiliary(aux_scanner_check_test) > use auxiliary/scanner/http/soap_xml 
msf auxiliary(soap_xml) > set RHOSTS 127.0.0.2
RHOSTS => 127.0.0.2
msf auxiliary(soap_xml) > check
[*] 127.0.0.2:80 - This module does not support check.
[*] Checked 1 of 1 hosts (100% complete)
msf auxiliary(soap_xml) > 

@wchen-r7
Copy link
Contributor Author

wchen-r7 commented Feb 3, 2014

well done sir.

@wchen-r7
Copy link
Contributor Author

wchen-r7 commented Feb 3, 2014

Looks like testing is complete. Please merge. Thanks.

@wchen-r7
Copy link
Contributor Author

wchen-r7 commented Feb 3, 2014

Talked to Tod. Since there's an issue with other things in framework, we're holding this one to make sure we have a safe upcoming release. Should probably go in the same time as #2930, since they're two non-small effort features.

@wvu
Copy link
Contributor

wvu commented Feb 3, 2014

Roger.

wvu added a commit that referenced this pull request Feb 4, 2014
@wvu wvu merged commit 4d008ca into rapid7:master Feb 4, 2014
@wvu
Copy link
Contributor

wvu commented Feb 4, 2014

Thanks!!

@wchen-r7 wchen-r7 deleted the check branch August 22, 2016 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants