Detection rules › Kusto

Exchange OAB Virtual Directory Attribute Containing Potential Webshell

Status
available
Severity
high
Time window
1h
Source
github.com/Azure/Azure-Sentinel

This query uses Windows Event ID 5136 in order to detect potential webshell deployment by exploitation of CVE-2021-27065. This query looks for changes to the InternalHostName or ExternalHostName properties of Exchange OAB Virtual Directory objects in AD Directory Services where the new objects contain potential webshell objects.

MITRE ATT&CK coverage

TacticTechniques
Initial Access

Telemetry coverage

Rule body

id: faf1a6ff-53b5-4f92-8c55-4b20e9957594
name: Exchange OAB Virtual Directory Attribute Containing Potential Webshell
description: |
  'This query uses Windows Event ID 5136 in order to detect potential webshell deployment by exploitation of CVE-2021-27065.
  This query looks for changes to the InternalHostName or ExternalHostName properties of Exchange OAB Virtual Directory objects in AD Directory Services where the new objects contain potential webshell objects.'
severity: High
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
query: |
  SecurityEvent
  // Look for specific Directory Service Changes and parse data
  | where EventID == 5136
  | extend EventData = parse_xml(EventData).EventData.Data
  | mv-expand bagexpansion = array EventData
  | evaluate bag_unpack(EventData)
  | extend Key = tostring(column_ifexists('@Name', "")), Value = column_ifexists('#text', "")
  | evaluate pivot(Key, any(Value),TimeGenerated, EventID, Computer, Account, AccountType, EventSourceName, Activity, SubjectAccount)
  // Where changes relate to Exchange OAB
  | extend ObjectClass = column_ifexists("ObjectClass", "")
  | where ObjectClass =~ "msExchOABVirtualDirectory"
  // Look for InternalHostName or ExternalHostName properties being changed
  | extend AttributeLDAPDisplayName = column_ifexists("AttributeLDAPDisplayName", "")
  | where AttributeLDAPDisplayName in~ ("msExchExternalHostName", "msExchInternalHostName")
  // Look for suspected webshell activity
  | extend AttributeValue = column_ifexists("AttributeValue", "")
  | where AttributeValue has "script"
  | project-rename LastSeen = TimeGenerated
  | extend ObjectDN = column_ifexists("ObjectDN", "")
  | project-reorder LastSeen, Computer, Account, ObjectDN, AttributeLDAPDisplayName, AttributeValue
  | extend timestamp = LastSeen
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
  | extend AccountName = tostring(split(Account, @'\')[1]), AccountNTDomain = tostring(split(Account, @'\')[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: HostNameDomain
version: 1.0.4
kind: Scheduled

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 5136

Stage 3: extend

| extend EventData = parse_xml(EventData).EventData.Data

Stage 4: mv-expand

| mv-expand bagexpansion = array EventData

Stage 5: evaluate

| evaluate bag_unpack(EventData)

Stage 6: extend

| extend Key = tostring(column_ifexists('@Name', "")), Value = column_ifexists('#text', "")

Stage 7: evaluate

| evaluate pivot(Key, any(Value),TimeGenerated, EventID, Computer, Account, AccountType, EventSourceName, Activity, SubjectAccount)

Stage 8: extend

| extend ObjectClass = column_ifexists("ObjectClass", "")

Stage 9: where

| where ObjectClass =~ "msExchOABVirtualDirectory"

Stage 10: extend

| extend AttributeLDAPDisplayName = column_ifexists("AttributeLDAPDisplayName", "")

Stage 11: where

| where AttributeLDAPDisplayName in~ ("msExchExternalHostName", "msExchInternalHostName")

Stage 12: extend

| extend AttributeValue = column_ifexists("AttributeValue", "")

Stage 13: where

| where AttributeValue has "script"

Stage 14: project-rename

| project-rename LastSeen = TimeGenerated

Stage 15: extend

| extend ObjectDN = column_ifexists("ObjectDN", "")

Stage 16: project-reorder

| project-reorder LastSeen, Computer, Account, ObjectDN, AttributeLDAPDisplayName, AttributeValue

Stage 17: extend (4 consecutive steps)

| extend timestamp = LastSeen
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend AccountName = tostring(split(Account, @'\')[1]), AccountNTDomain = tostring(split(Account, @'\')[0])

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AttributeLDAPDisplayNamein
  • msExchExternalHostName
  • msExchInternalHostName
field:"AttributeLDAPDisplayName" kind:in
AttributeValuematch
  • script transforms: term
field:"AttributeValue" kind:match value:"script"
EventIDeq
  • 5136 corpus 45 (splunk 24, kusto 18, elastic 3)
field:"EventID" kind:eq value:"5136"
ObjectClasseq
  • msExchOABVirtualDirectory
field:"ObjectClass" kind:eq value:"msExchOABVirtualDirectory"

Output fields

These fields are emitted when the rule matches.

FieldSource
EventDataextend
Keyextend
Valueextend
ObjectClassextend
AttributeLDAPDisplayNameextend
AttributeValueextend
LastSeenproject-rename
ObjectDNextend
timestampextend
DomainIndexextend
HostNameextend
HostNameDomainextend
AccountNTDomainextend
AccountNameextend