← Back to SOC feed Coverage →

Exchange Server Suspicious URIs Visited

kql MEDIUM Azure-Sentinel
T1190
W3CIISLog
exploithuntingmicrosoftofficial
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-06-04T23:00:00Z · Confidence: medium

Hunt Hypothesis

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

Analytic Rule Definition

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

Required Data Sources

Sentinel TableNotes
W3CIISLogEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/W3CIISLog/ExchangeServerSuspiciousURIsVisited.yaml