Detection rules › Kusto

User agent search for log4j exploitation attempt

Status
available
Severity
high
Time window
1d
Group by
Account, AppDisplayName, ClientAppUsed, DstIpAddr, EventName, HttpStatus, HttpUserAgent, Operation, ResourceId, ResourceType, SourceIP, Type, Url, UserAgent, csMethod, ruleName_s, sSiteName
Source
github.com/Azure/Azure-Sentinel

This query uses various log sources having user agent data to look for log4j CVE-2021-44228 exploitation attempt based on user agent pattern. Log4j is an open-source Apache logging library that is used in many Java-based applications. The regex and the string matching look for the most common attacks. This might not be comprehensive to detect every possible user agent variation. Reference: https://msrc-blog.microsoft.com/2021/12/11/microsofts-response-to-cve-2021-44228-apache-log4j2/

MITRE ATT&CK coverage

TacticTechniques
Initial Access

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

id: 29283b22-a1c0-4d16-b0a9-3460b655a46a
name: User agent search for log4j exploitation attempt
description: |
  'This query uses various log sources having user agent data to look for log4j CVE-2021-44228 exploitation attempt based on user agent pattern.
  Log4j is an open-source Apache logging library that is used in many Java-based applications. The regex and the string matching look for the most common attacks. This might not be comprehensive to detect every possible user agent variation.
   Reference: https://msrc-blog.microsoft.com/2021/12/11/microsofts-response-to-cve-2021-44228-apache-log4j2/'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SquidProxy
    dataTypes:
      - SquidProxy_CL
  - connectorId: Zscaler
    dataTypes:
      - CommonSecurityLog
  - connectorId: WAF
    dataTypes:
      - AzureDiagnostics
  - connectorId: Office365
    dataTypes:
      - OfficeActivity
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AADNonInteractiveUserSignInLogs
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
  - connectorId: AzureMonitor(IIS)
    dataTypes:
      - W3CIISLog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
tags:
    - log4j
    - log4shell
    - CVE2021-44228
    - Schema: ASimWebSession
    - SchemaVersion: 0.2.1
    - Schema: ASimNetworkSessions
    - SchemaVersion: 0.2.1
query: |
  let UserAgentString = dynamic (["${jndi:ldap:/", "${jndi:rmi:/", "${jndi:ldaps:/", "${jndi:dns:/", "${jndi:iiop:/","${jndi:","${jndi:nds:/","${jndi:corba/"]);
  let UARegexMinimalString=dynamic(['{','%7b', '%7B']);
  let UARegex = @'(\\$|%24)(\\{|%7B)([^jJ]*[jJ])([^nN]*[nN])([^dD]*[dD])([^iI]*[iI])(:|%3A|\\$|%24|}|%7D)';
  (union isfuzzy=true
  (OfficeActivity
  | where UserAgent has_any (UserAgentString) or UserAgent matches regex UARegex
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = ClientIP, Account = UserId, Type, Operation
  ),
  (AzureDiagnostics
  | where Category in ("FrontdoorWebApplicationFirewallLog", "FrontdoorAccessLog", "ApplicationGatewayFirewallLog", "ApplicationGatewayAccessLog")
  | where userAgent_s has_any (UserAgentString) or userAgent_s matches regex UARegex
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent = userAgent_s, SourceIP = column_ifexists("clientIp_s",clientIP_s), Type, column_ifexists("originalHost_s",host_s), Url = requestUri_s, HttpStatus = column_ifexists("httpStatusDetails_s",httpStatus_d), column_ifexists("trackingReference_s",transactionId_g), ruleName_s, ResourceType, ResourceId
  ),
  (
  W3CIISLog
  | where csUserAgent has_any (UserAgentString) or csUserAgent matches regex UARegex
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent = csUserAgent, SourceIP = cIP, Account = csUserName, Type, sSiteName, csMethod, Url = csUriStem
  ),
  (
  AWSCloudTrail
  | where UserAgent has_any (UserAgentString) or UserAgent matches regex UARegex
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = SourceIpAddress, Account = UserIdentityUserName, Type, EventName
  ),
  (SigninLogs
  | where UserAgent has_any (UserAgentString) or UserAgent matches regex UARegex
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = IPAddress, Account = UserPrincipalName, Type, Operation = OperationName, tostring(LocationDetails), tostring(DeviceDetail),    AppDisplayName, ClientAppUsed
  ),
  (AADNonInteractiveUserSignInLogs 
  | where UserAgent has_any (UserAgentString) or UserAgent matches regex UARegex
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = IPAddress, Account = UserPrincipalName, Type, Operation = OperationName, tostring(LocationDetails), tostring(DeviceDetail), AppDisplayName, ClientAppUsed
  ),
  (_Im_WebSession (httpuseragent_has_any=array_concat(UserAgentString,UARegexMinimalString))
  | where HttpUserAgent has_any (UserAgentString) or HttpUserAgent matches regex UARegex
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by HttpUserAgent, SourceIP = SrcIpAddr, DstIpAddr, Account = SrcUsername, Url, Type
  )
  )
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIP
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Account
version: 1.0.9
kind: Scheduled

Stages and Predicates

Stage 0: let

let UserAgentString = dynamic (["${jndi:ldap:/", "${jndi:rmi:/", "${jndi:ldaps:/", "${jndi:dns:/", "${jndi:iiop:/","${jndi:","${jndi:nds:/","${jndi:corba/"]);
let UARegexMinimalString=dynamic(['{','%7b', '%7B']);
let UARegex = @'(\\$|%24)(\\{|%7B)([^jJ]*[jJ])([^nN]*[nN])([^dD]*[dD])([^iI]*[iI])(:|%3A|\\$|%24|}|%7D)';

Stage 1: union

union of 7 branches

Stage 2: source

OfficeActivity

Stage 3: where

| where UserAgent has_any (UserAgentString) or UserAgent matches regex UARegex

Stage 4: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = ClientIP, Account = UserId, Type, Operation

Stage 5: source

AzureDiagnostics

Stage 6: where

| where Category in ("FrontdoorWebApplicationFirewallLog", "FrontdoorAccessLog", "ApplicationGatewayFirewallLog", "ApplicationGatewayAccessLog")

Stage 7: where

| where userAgent_s has_any (UserAgentString) or userAgent_s matches regex UARegex

Stage 8: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent = userAgent_s, SourceIP = column_ifexists("clientIp_s",clientIP_s), Type, column_ifexists("originalHost_s",host_s), Url = requestUri_s, HttpStatus = column_ifexists("httpStatusDetails_s",httpStatus_d), column_ifexists("trackingReference_s",transactionId_g), ruleName_s, ResourceType, ResourceId

Stage 9: source

W3CIISLog

Stage 10: where

| where csUserAgent has_any (UserAgentString) or csUserAgent matches regex UARegex

Stage 11: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent = csUserAgent, SourceIP = cIP, Account = csUserName, Type, sSiteName, csMethod, Url = csUriStem

Stage 12: source

AWSCloudTrail

Stage 13: where

| where UserAgent has_any (UserAgentString) or UserAgent matches regex UARegex

Stage 14: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = SourceIpAddress, Account = UserIdentityUserName, Type, EventName

Stage 15: source

SigninLogs

Stage 16: where

| where UserAgent has_any (UserAgentString) or UserAgent matches regex UARegex

Stage 17: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = IPAddress, Account = UserPrincipalName, Type, Operation = OperationName, tostring(LocationDetails), tostring(DeviceDetail),    AppDisplayName, ClientAppUsed

Stage 18: source

AADNonInteractiveUserSignInLogs

Stage 19: where

| where UserAgent has_any (UserAgentString) or UserAgent matches regex UARegex

Stage 20: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = IPAddress, Account = UserPrincipalName, Type, Operation = OperationName, tostring(LocationDetails), tostring(DeviceDetail), AppDisplayName, ClientAppUsed

Stage 21: source

_Im_WebSession (httpuseragent_has_any=array_concat(UserAgentString,UARegexMinimalString))

Stage 22: where

| where HttpUserAgent has_any (UserAgentString) or HttpUserAgent matches regex UARegex

Stage 23: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by HttpUserAgent, SourceIP = SrcIpAddr, DstIpAddr, Account = SrcUsername, Url, Type

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Categoryin
  • ApplicationGatewayAccessLog
  • ApplicationGatewayFirewallLog
  • FrontdoorAccessLog
  • FrontdoorWebApplicationFirewallLog
field:"Category" kind:in
HttpUserAgentmatch
  • ${jndi: transforms: term
  • ${jndi:corba/ transforms: term
  • ${jndi:dns:/ transforms: term
  • ${jndi:iiop:/ transforms: term
  • ${jndi:ldap:/ transforms: term
  • ${jndi:ldaps:/ transforms: term
  • ${jndi:nds:/ transforms: term
  • ${jndi:rmi:/ transforms: term
field:"HttpUserAgent" kind:match
HttpUserAgentregex_match
  • (\$|%24)(\{|%7B)([^jJ]*[jJ])([^nN]*[nN])([^dD]*[dD])([^iI]*[iI])(:|%3A|\$|%24|}|%7D)
field:"HttpUserAgent" kind:regex_match value:"(\$|%24)(\{|%7B)([^jJ]*[jJ])([^nN]*[nN])([^dD]*[dD])([^iI]*[iI])(:|%3A|\$|%24|}|%7D)"
UserAgentmatch
  • ${jndi: transforms: term
  • ${jndi:corba/ transforms: term
  • ${jndi:dns:/ transforms: term
  • ${jndi:iiop:/ transforms: term
  • ${jndi:ldap:/ transforms: term
  • ${jndi:ldaps:/ transforms: term
  • ${jndi:nds:/ transforms: term
  • ${jndi:rmi:/ transforms: term
field:"aws::userAgent" kind:match
UserAgentregex_match
  • (\$|%24)(\{|%7B)([^jJ]*[jJ])([^nN]*[nN])([^dD]*[dD])([^iI]*[iI])(:|%3A|\$|%24|}|%7D)
field:"aws::userAgent" kind:regex_match value:"(\$|%24)(\{|%7B)([^jJ]*[jJ])([^nN]*[nN])([^dD]*[dD])([^iI]*[iI])(:|%3A|\$|%24|}|%7D)"
csUserAgentmatch
  • ${jndi: transforms: term
  • ${jndi:corba/ transforms: term
  • ${jndi:dns:/ transforms: term
  • ${jndi:iiop:/ transforms: term
  • ${jndi:ldap:/ transforms: term
  • ${jndi:ldaps:/ transforms: term
  • ${jndi:nds:/ transforms: term
  • ${jndi:rmi:/ transforms: term
field:"csUserAgent" kind:match
csUserAgentregex_match
  • (\$|%24)(\{|%7B)([^jJ]*[jJ])([^nN]*[nN])([^dD]*[dD])([^iI]*[iI])(:|%3A|\$|%24|}|%7D)
field:"csUserAgent" kind:regex_match value:"(\$|%24)(\{|%7B)([^jJ]*[jJ])([^nN]*[nN])([^dD]*[dD])([^iI]*[iI])(:|%3A|\$|%24|}|%7D)"
userAgent_smatch
  • ${jndi: transforms: term
  • ${jndi:corba/ transforms: term
  • ${jndi:dns:/ transforms: term
  • ${jndi:iiop:/ transforms: term
  • ${jndi:ldap:/ transforms: term
  • ${jndi:ldaps:/ transforms: term
  • ${jndi:nds:/ transforms: term
  • ${jndi:rmi:/ transforms: term
field:"userAgent_s" kind:match
userAgent_sregex_match
  • (\$|%24)(\{|%7B)([^jJ]*[jJ])([^nN]*[nN])([^dD]*[dD])([^iI]*[iI])(:|%3A|\$|%24|}|%7D)
field:"userAgent_s" kind:regex_match value:"(\$|%24)(\{|%7B)([^jJ]*[jJ])([^nN]*[nN])([^dD]*[dD])([^iI]*[iI])(:|%3A|\$|%24|}|%7D)"

Output fields

These fields are emitted when the rule matches.

FieldSource
Accountsummarize
DstIpAddrsummarize
EndTimesummarize
HttpUserAgentsummarize
SourceIPsummarize
StartTimesummarize
Typesummarize
Urlsummarize