Detection rules › Kusto

Exchange Server Suspicious File Downloads.

Severity
medium
Time window
1d
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

'This query looks for messages related to file downloads of suspicious file types on an Exchange Server. This could indicate attempted deployment of webshells. This query uses the Exchange HttpProxy AOBGeneratorLog, you will need to onboard this log as a custom log under the table http_proxy_oab_CL before using this query. This log is commonly found at C:\Program Files\Microsoft\Exchange Server\V15\Logging\OABGeneratorLog on the Exchange server. Details on collecting custom logs into Sentinel can be found here: https://learn.microsoft.com/en-us/azure/sentinel/connect-custom-logs-ama'

MITRE ATT&CK coverage

TacticTechniques
Initial Access

Rule body

id: 8955c0fb-3408-47b0-a3b9-a1faec41e427
name: Exchange Server Suspicious File Downloads.
description: |
  'This query looks for messages related to file downloads of suspicious file types on an Exchange Server. This could indicate attempted deployment of webshells. 
  This query uses the Exchange HttpProxy AOBGeneratorLog, you will need to onboard this log as a custom log under the table http_proxy_oab_CL before using this query. 
  This log is commonly found at C:\Program Files\Microsoft\Exchange Server\V15\Logging\OABGeneratorLog on the Exchange server. Details on collecting custom logs into Sentinel
  can be found here: https://learn.microsoft.com/en-us/azure/sentinel/connect-custom-logs-ama'
severity: Medium
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
query: |
  let scriptExtensions = dynamic([".php", ".jsp", ".js", ".aspx", ".asmx", ".asax", ".cfm", ".shtml"]);
  http_proxy_oab_CL
  | where RawData contains "Download failed and temporary file"
  | extend File = extract("([^\\\\]*)(\\\\[^']*)",2,RawData)
  | extend Extension = strcat(".",split(File, ".")[-1])
  | extend InteractiveFile = iif(Extension in (scriptExtensions), "Yes", "No")
  // Uncomment the following line to alert only on interactive file download type
  //| where InteractiveFile =~ "Yes"
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: HostNameDomain
version: 1.0.5
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Application" ]

Stages and Predicates

Parameters

let scriptExtensions = dynamic([".php", ".jsp", ".js", ".aspx", ".asmx", ".asax", ".cfm", ".shtml"]);

Stage 1: source

http_proxy_oab_CL

Stage 2: where

| where RawData contains "Download failed and temporary file"

Stage 3: extend (5 consecutive steps)

| extend File = extract("([^\\\\]*)(\\\\[^']*)",2,RawData)
| extend Extension = strcat(".",split(File, ".")[-1])
| extend InteractiveFile = iif(Extension in (scriptExtensions), "Yes", "No")
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
RawDatacontains
  • Download failed and temporary file
field:"RawData" kind:contains value:"Download failed and temporary file"

Output fields

These fields are emitted when the rule matches.

FieldSource
Fileextend
Extensionextend
InteractiveFileextend
DomainIndexextend
HostNameextend
HostNameDomainextend