Users visiting a high percentage of suspicious URIs associated with ProxyLogon exploitation may indicate compromise of Exchange Server, and proactive hunting for this behavior in Azure Sentinel can help identify potential insider threats or advanced persistent threats leveraging known exploit vectors.
KQL Query
//Calculate number of suspicious URI stems visited by user
W3CIISLog
| where not(ipv4_is_private(cIP))
| where (csUriStem matches regex @"\/owa\/auth\/[A-Za-z0-9]{1,30}\.js") or (csUriStem matches regex @"\/ecp\/[A-Za-z0-9]{1,30}\.(js|flt|css)") or (csUriStem =~ "/ews/exchange.asmx")
| extend userHash = hash_md5(strcat(cIP, csUserAgent))
| summarize susCount=dcount(csUriStem), make_list(csUriStem), min(TimeGenerated), max(TimeGenerated) by userHash, cIP, csUserAgent
| join kind=leftouter (
//Calculate unique URI stems visited by each user
W3CIISLog
| where not(ipv4_is_private(cIP))
| extend userHash = hash_md5(strcat(cIP, csUserAgent))
| summarize allCount=dcount(csUriStem) by userHash
) on userHash
//Find instances where only a common endpoint was seen
| extend containsDefault = iff(list_csUriStem contains "/ews/exchange.asmx", 1, 0)
//If we only see the common endpoint and nothing else dump it
| extend result = iff(containsDefault == 1, containsDefault+susCount, 0)
| where result != 2
| extend susPercentage = susCount / allCount * 100
| where susPercentage > 90
| project StartTime=min_TimeGenerated, EndTime=max_TimeGenerated, AttackerIP=cIP, AttackerUA=csUserAgent, URIsVisited=list_csUriStem, suspiciousPercentage=susPercentage, allUriCount=allCount, suspiciousUriCount=susCount
| extend timestamp = StartTime, IPCustomEntity = AttackerIP
id: 3122423d-6c33-43c8-bc10-6d27b4350176
name: Exchange Server Suspicious URIs Visited
description: |
'This query will detect paths suspicious associated with ProxyLogon exploitation, it will then calculate the percentage of suspicious URIs
the user had visited in relation to the total number of URIs the user has visited. This query will assist in the detection of automated
ProxyLogon exploitation.'
requiredDataConnectors:
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
tactics:
- InitialAccess
relevantTechniques:
- T1190
tags:
- Exchange
query: |
//Calculate number of suspicious URI stems visited by user
W3CIISLog
| where not(ipv4_is_private(cIP))
| where (csUriStem matches regex @"\/owa\/auth\/[A-Za-z0-9]{1,30}\.js") or (csUriStem matches regex @"\/ecp\/[A-Za-z0-9]{1,30}\.(js|flt|css)") or (csUriStem =~ "/ews/exchange.asmx")
| extend userHash = hash_md5(strcat(cIP, csUserAgent))
| summarize susCount=dcount(csUriStem), make_list(csUriStem), min(TimeGenerated), max(TimeGenerated) by userHash, cIP, csUserAgent
| join kind=leftouter (
//Calculate unique URI stems visited by each user
W3CIISLog
| where not(ipv4_is_private(cIP))
| extend userHash = hash_md5(strcat(cIP, csUserAgent))
| summarize allCount=dcount(csUriStem) by userHash
) on userHash
//Find instances where only a common endpoint was seen
| extend containsDefault = iff(list_csUriStem contains "/ews/exchange.asmx", 1, 0)
//If we only see the common endpoint and nothing else dump it
| extend result = iff(containsDefault == 1, containsDefault+susCount, 0)
| where result != 2
| extend susPercentage = susCount / allCount * 100
| where susPercentage > 90
| project StartTime=min_TimeGenerated, EndTime=max_TimeGenerated, AttackerIP=cIP, AttackerUA=csUserAgent, URIsVisited=list_csUriStem, suspiciousPercentage=susPercentage, allUriCount=allCount, suspiciousUriCount=susCount
| extend timestamp = StartTime, IPCustomEntity = AttackerIP
entityMappings:
- entityType: NetworkConnection
fieldMappings:
- identifier: SourceAddress
columnName: AttackerIP
| Sentinel Table | Notes |
|---|---|
W3CIISLog | Ensure this data connector is enabled |
Scenario: A system administrator is performing a scheduled backup of Exchange Server using Veeam Backup & Replication and the backup process accesses a URI associated with the ProxyLogon exploit pattern.
Filter/Exclusion: Exclude URIs related to known backup tools like veeam.com or backupserver.com using a domain-based filter.
Scenario: A user is accessing a Microsoft Update URI that matches the ProxyLogon pattern as part of a routine patching process.
Filter/Exclusion: Exclude URIs containing update.microsoft.com or windowsupdate.com to avoid false positives from legitimate system updates.
Scenario: A PowerShell script running as part of a scheduled job (e.g., using Task Scheduler) is accessing a URI that matches the ProxyLogon pattern for testing or debugging purposes.
Filter/Exclusion: Exclude URIs accessed by processes with the PowerShell.exe executable or by scheduled tasks using a process name filter.
Scenario: A Microsoft Defender for Office 365 (MDO) or Microsoft Defender ATP (MDATP) scan is accessing a URI that matches the ProxyLogon pattern during a security scan.
Filter/Exclusion: Exclude URIs accessed by processes with the MsDefender.exe or MsMpEng.exe executable names.
Scenario: A Microsoft Exchange Management Shell (EMS) script is executed by an admin to check mailbox configurations, and it accesses a URI that matches the ProxyLogon pattern due to a misconfigured proxy or internal redirect.
Filter/Exclusion: Exclude URIs accessed by processes with the Exchange Management Shell or Exchange Server service names.