From 729bdc58fb175599fb51efc454d924023ff4c68d Mon Sep 17 00:00:00 2001 From: Thomas McElroy <62295189+thmcelro@users.noreply.github.com> Date: Mon, 22 Mar 2021 12:36:26 +0000 Subject: [PATCH 1/3] Hunting queries for Exchange activity Hunting queries to detect ProxyLogon and other web exploitation activity. --- .../ExchangeServerSuspiciousURIsVisited.yaml | 44 +++++++++++++++++++ ...changeServersAssociatedSecurityAlerts.yaml | 31 +++++++++++++ .../ExchangeServerProxyLogonURI.yaml | 27 ++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml create mode 100644 Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml create mode 100644 Hunting Queries/W3CIISLog/ExchangeServerProxyLogonURI.yaml diff --git a/Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml b/Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml new file mode 100644 index 0000000000..275a6aad05 --- /dev/null +++ b/Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml @@ -0,0 +1,44 @@ +id: 43701f18-c903-489e-8cc9-a2e85dc3ad23 +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: [] +tactics: + - InitialAccess +relevantTechniques: + - T1190 +tags: + - Exchange +query: | + +let timeRange = 7d; +//Calculate number of suspicious URI stems visited by user +W3CIISLog +| where TimeGenerated > ago(timeRange) +| 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 TimeGenerated > ago(timeRange) + | 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 +entityMappings: +- entityType: NetworkConnection + fieldMappings: + - identifier: SourceAddress + columnName: AttackerIP \ No newline at end of file diff --git a/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml b/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml new file mode 100644 index 0000000000..94c147a336 --- /dev/null +++ b/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml @@ -0,0 +1,31 @@ +id: 43701f18-c903-489e-8cc9-a2e85dc3ad23 +name: Exchange Servers and Associated Security Alerts +description: | + 'This query will dynamically identify Exchnage servers using common web paths used by the application in the csUriStem. The query + will then collect MDE alerts from the SecurityAlert table using the identified Exchange Server hostnames.' +requiredDataConnectors: [] +tactics: + - InitialAccess +relevantTechniques: + - T1190 +tags: + - Exchange +query: | + + W3CIISLog + | where csUriStem has_any("/owa/auth/", "/ecp/healthcheck.htm", "/ews/exchange.asmx") + | summarize by computer=tolower(Computer) + | join kind=leftouter ( + SecurityAlert + | extend alertData = parse_json(Entities) + | mvexpand alertData + | where alertData.Type == "host" + | extend computer = iff(isnotempty(alertData.DnsDomain), tolower(strcat(tostring(alertData.HostName), "." , tostring(alertData.DnsDomain))),tolower(tostring(alertData.HostName))) + | summarize Alerts=dcount(SystemAlertId), AlertTimes=make_list(TimeGenerated), AlertNames=make_list(AlertName) by computer + ) on computer + | project ExchangeServer=computer, Alerts, AlertTimes, AlertNames +entityMappings: +- entityType: Host + fieldMappings: + - identifier: HostName + columnName: ExchangeServer \ No newline at end of file diff --git a/Hunting Queries/W3CIISLog/ExchangeServerProxyLogonURI.yaml b/Hunting Queries/W3CIISLog/ExchangeServerProxyLogonURI.yaml new file mode 100644 index 0000000000..c5305d7dc3 --- /dev/null +++ b/Hunting Queries/W3CIISLog/ExchangeServerProxyLogonURI.yaml @@ -0,0 +1,27 @@ +id: 43701f18-c903-489e-8cc9-a2e85dc3ad23 +name: Exchange Server ProxyLogon URIs +description: | + 'This query will detect paths suspicious associated with ProxyLogon exploitation' +requiredDataConnectors: [] +tactics: + - InitialAccess +relevantTechniques: + - T1190 +tags: + - Exchange +query: | + + W3CIISLog + | where TimeGenerated > ago(3d) + | 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)") + | project TimeGenerated, sSiteName, csMethod, csUriStem, sPort, sIP, cIP, csUserAgent +entityMappings: +- entityType: NetworkConnection + fieldMappings: + - identifier: DestinationAddress + columnName: sIP + - identifier: DestinationPort + columnName: sPort + - identifier: SourceAddress + columnName: cIP \ No newline at end of file From 65ddf104c5cfe3ec10e8e996c8e6aedb9331f203 Mon Sep 17 00:00:00 2001 From: Thomas McElroy <62295189+thmcelro@users.noreply.github.com> Date: Mon, 22 Mar 2021 12:42:17 +0000 Subject: [PATCH 2/3] Typo and bugfix --- .../ExchangeServerSuspiciousURIsVisited.yaml | 48 +++++++++---------- ...changeServersAssociatedSecurityAlerts.yaml | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml b/Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml index 275a6aad05..cc7c149404 100644 --- a/Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml +++ b/Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml @@ -13,30 +13,30 @@ tags: - Exchange query: | -let timeRange = 7d; -//Calculate number of suspicious URI stems visited by user -W3CIISLog -| where TimeGenerated > ago(timeRange) -| 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 TimeGenerated > ago(timeRange) - | 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 + let timeRange = 7d; + //Calculate number of suspicious URI stems visited by user + W3CIISLog + | where TimeGenerated > ago(timeRange) + | 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 TimeGenerated > ago(timeRange) + | 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 entityMappings: - entityType: NetworkConnection fieldMappings: diff --git a/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml b/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml index 94c147a336..7a201588d5 100644 --- a/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml +++ b/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml @@ -1,7 +1,7 @@ id: 43701f18-c903-489e-8cc9-a2e85dc3ad23 name: Exchange Servers and Associated Security Alerts description: | - 'This query will dynamically identify Exchnage servers using common web paths used by the application in the csUriStem. The query + 'This query will dynamically identify Exchange servers using common web paths used by the application in the csUriStem. The query will then collect MDE alerts from the SecurityAlert table using the identified Exchange Server hostnames.' requiredDataConnectors: [] tactics: From 23a552b3c770a1a5a6cbd70efd86153be57632e8 Mon Sep 17 00:00:00 2001 From: Thomas McElroy <62295189+thmcelro@users.noreply.github.com> Date: Thu, 25 Mar 2021 16:42:44 +0000 Subject: [PATCH 3/3] Updates - Move query to correct folder - Adding required connectors --- .../ExchangeServersAssociatedSecurityAlerts.yaml | 8 +++++++- .../W3CIISLog/ExchangeServerProxyLogonURI.yaml | 5 ++++- .../ExchangeServerSuspiciousURIsVisited.yaml | 5 ++++- 3 files changed, 15 insertions(+), 3 deletions(-) rename Hunting Queries/{MultipleDataSources => W3CIISLog}/ExchangeServerSuspiciousURIsVisited.yaml (94%) diff --git a/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml b/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml index 7a201588d5..78b244cf3a 100644 --- a/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml +++ b/Hunting Queries/MultipleDataSources/ExchangeServersAssociatedSecurityAlerts.yaml @@ -3,7 +3,13 @@ name: Exchange Servers and Associated Security Alerts description: | 'This query will dynamically identify Exchange servers using common web paths used by the application in the csUriStem. The query will then collect MDE alerts from the SecurityAlert table using the identified Exchange Server hostnames.' -requiredDataConnectors: [] +requiredDataConnectors: + - connectorId: AzureMonitor(IIS) + dataTypes: + - W3CIISLog + - connectorId: MicrosoftDefenderAdvancedThreatProtection + dataTypes: + - SecurityAlert (MDATP) tactics: - InitialAccess relevantTechniques: diff --git a/Hunting Queries/W3CIISLog/ExchangeServerProxyLogonURI.yaml b/Hunting Queries/W3CIISLog/ExchangeServerProxyLogonURI.yaml index c5305d7dc3..b4b2572feb 100644 --- a/Hunting Queries/W3CIISLog/ExchangeServerProxyLogonURI.yaml +++ b/Hunting Queries/W3CIISLog/ExchangeServerProxyLogonURI.yaml @@ -2,7 +2,10 @@ id: 43701f18-c903-489e-8cc9-a2e85dc3ad23 name: Exchange Server ProxyLogon URIs description: | 'This query will detect paths suspicious associated with ProxyLogon exploitation' -requiredDataConnectors: [] +requiredDataConnectors: + - connectorId: AzureMonitor(IIS) + dataTypes: + - W3CIISLog tactics: - InitialAccess relevantTechniques: diff --git a/Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml b/Hunting Queries/W3CIISLog/ExchangeServerSuspiciousURIsVisited.yaml similarity index 94% rename from Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml rename to Hunting Queries/W3CIISLog/ExchangeServerSuspiciousURIsVisited.yaml index cc7c149404..ee972edfde 100644 --- a/Hunting Queries/MultipleDataSources/ExchangeServerSuspiciousURIsVisited.yaml +++ b/Hunting Queries/W3CIISLog/ExchangeServerSuspiciousURIsVisited.yaml @@ -4,7 +4,10 @@ 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: [] +requiredDataConnectors: + - connectorId: AzureMonitor(IIS) + dataTypes: + - W3CIISLog tactics: - InitialAccess relevantTechniques: