Detection rules › Kusto

Kusto rules: 1j

AdminSDHolder Modifications

#
Severity
high
Time window
1h
Group by
Computer, ObjectDN, SubjectAccount, SubjectLogonId, SubjectUserSid
Author
Vasileios Paschalidis
Source
github.com/Azure/Azure-Sentinel

This query detects modification in the AdminSDHolder in the Active Directory which could indicate an attempt for persistence. AdminSDHolder Modification is a persistence technique in which an attacker abuses the SDProp process in Active Directory to establish a persistent backdoor to Active Directory. This query searches for the event id 5136 where the Object DN is AdminSDHolder. Ref: https://netwrix.com/en/cybersecurity-glossary/cyber-security-attacks/adminsdholder-attack/

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Telemetry Coverage

Rule Body

id: 52aec824-96c1-4a03-8e44-bb70532e6cea
name: AdminSDHolder Modifications
description: |
   'This query detects modification in the AdminSDHolder  in the Active Directory which could indicate an attempt for persistence. 
   AdminSDHolder Modification is a persistence technique in which an attacker abuses the SDProp process in Active Directory to establish a persistent backdoor to Active Directory.
   This query searches for the event id 5136 where the Object DN is AdminSDHolder.
   Ref: https://netwrix.com/en/cybersecurity-glossary/cyber-security-attacks/adminsdholder-attack/'
severity: High
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
relevantTechniques:
  - T1078
query: |
   SecurityEvent
   | where EventID == 5136 and EventData contains "<Data Name=\"ObjectDN\">CN=AdminSDHolder,CN=System"
   | parse EventData with * 'ObjectDN">' ObjectDN "<" *
   | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by Computer, SubjectAccount, SubjectUserSid, SubjectLogonId, ObjectDN
   | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
   | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
   | extend Name = tostring(split(SubjectAccount, "\\")[1]), NTDomain = tostring(split(SubjectAccount, "\\")[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: SubjectAccount
      - identifier: Name
        columnName: Name
      - identifier: NTDomain
        columnName: NTDomain
  - 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: Vasileios Paschalidis
    support:
        tier: Community
    categories:
        domains: [ "Security - Others" ]

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 5136 and EventData contains "<Data Name=\"ObjectDN\">CN=AdminSDHolder,CN=System"

Stage 3: parse

| parse EventData with * 'ObjectDN">' ObjectDN "<" *

Stage 4: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by Computer, SubjectAccount, SubjectUserSid, SubjectLogonId, ObjectDN

Stage 5: extend (3 consecutive steps)

| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend Name = tostring(split(SubjectAccount, "\\")[1]), NTDomain = tostring(split(SubjectAccount, "\\")[0])

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventDatacontains
  • <Data Name="ObjectDN">CN=AdminSDHolder,CN=System
field:"EventData" kind:contains
EventIDeq
  • 5136 corpus 20 (kusto 18, elastic 2)
field:"EventID" kind:eq value:"5136"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Computersummarize
EndTimesummarize
ObjectDNsummarize
StartTimesummarize
SubjectAccountsummarize
SubjectLogonIdsummarize
SubjectUserSidsummarize
DomainIndexextend
HostNameextend
HostNameDomainextend
NTDomainextend
Nameextend

Audit policy manipulation using auditpol utility

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

This detects attempts to manipulate audit policies using auditpol command. This technique was seen in relation to Solorigate attack but the results can indicate potential malicious activity used in different attacks. The process name in each data source is commented out as an adversary could rename it. It is advisable to keep process name commented but if the results show unrelated false positives, users may want to uncomment it. Refer to auditpol syntax: https://docs.microsoft.com/windows-server/administration/windows-commands/auditpol
Refer to our M365 blog for details on use during the Solorigate attack: https://www.microsoft.com/security/blog/2021/01/20/deep-dive-into-the-solorigate-second-stage-activation-from-sunburst-to-teardrop-and-raindrop/

MITRE ATT&CK Coverage

TacticTechniques
Execution

Telemetry Coverage

Rule Body

id: 66276b14-32c5-4226-88e3-080dacc31ce1
name: Audit policy manipulation using auditpol utility
description: |
   This detects attempts to manipulate audit policies using auditpol command.
   This technique was seen in relation to Solorigate attack but the results can indicate potential malicious activity used in different attacks.
   The process name in each data source is commented out as an adversary could rename it. It is advisable to keep process name commented but if the results show unrelated false positives, users may want to uncomment it.
   Refer to auditpol syntax: https://docs.microsoft.com/windows-server/administration/windows-commands/auditpol  
   Refer to our M365 blog for details on use during the Solorigate attack:
   https://www.microsoft.com/security/blog/2021/01/20/deep-dive-into-the-solorigate-second-stage-activation-from-sunburst-to-teardrop-and-raindrop/
severity: Medium
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
relevantTechniques:
  - T1204
tags:
  - Solorigate
  - NOBELIUM
query: |
  let timeframe = 1d;
  let AccountAllowList = dynamic(['SYSTEM']);
  let SubCategoryList = dynamic(["Logoff", "Account Lockout", "User Account Management", "Authorization Policy Change"]); // Add any Category in the list to be allowed or disallowed
  let tokens = dynamic(["clear", "remove", "success:disable","failure:disable"]); 
  (union isfuzzy=true
  (
  SecurityEvent
  | where TimeGenerated >= ago(timeframe)
  //| where Process =~ "auditpol.exe" 
  | where CommandLine has_any (tokens)
  | where AccountType !~ "Machine" and Account !in~ (AccountAllowList)
  | parse CommandLine with * "/subcategory:" subcategorytoken
  | extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])
  | where SubCategory in~ (SubCategoryList) //use in~ for inclusion or !in~ for exclusion
  | where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable") // use this filter if required to exclude certain toggles
  | project TimeGenerated, Computer, Account, SubjectDomainName, SubjectUserName, Process, ParentProcessName, CommandLine, SubCategory, Toggle
  | extend timestamp = TimeGenerated, AccountName = SubjectUserName, AccountDomain = SubjectDomainName, DeviceName = Computer
  ),
  (
  DeviceProcessEvents
  | where TimeGenerated >= ago(timeframe)
  // | where InitiatingProcessFileName =~ "auditpol.exe" 
  | where InitiatingProcessCommandLine has_any (tokens)
  | where AccountName !in~ (AccountAllowList)
  | parse InitiatingProcessCommandLine with * "/subcategory:" subcategorytoken
  | extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])
  | where SubCategory in~ (SubCategoryList) //use in~ for inclusion or !in~ for exclusion
  | where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable") // use this filter if required to exclude certain toggles
  | project TimeGenerated, DeviceName, AccountName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessParentFileName,  InitiatingProcessCommandLine, SubCategory, Toggle
  | extend timestamp = TimeGenerated, AccountName = InitiatingProcessAccountName, AccountDomain = InitiatingProcessAccountDomain
  ),
  (
  Event
  | where TimeGenerated > ago(timeframe)
  | where Source == "Microsoft-Windows-Sysmon"
  | where EventID == 1
  | extend EventData = parse_xml(EventData).DataItem.EventData.Data
  | mv-expand bagexpansion=array EventData
  | evaluate bag_unpack(EventData)
  | extend Key=tostring(['@Name']), Value=['#text']
  | evaluate pivot(Key, any(Value), TimeGenerated, Source, EventLog, Computer, EventLevel, EventLevelName, EventID, UserName, RenderedDescription, MG, ManagementGroupName, Type, _ResourceId)
  // | where OriginalFileName =~ "auditpol.exe"
  | where CommandLine has_any (tokens)
  | where User !in~ (AccountAllowList)
  | parse CommandLine with * "/subcategory:" subcategorytoken
  | extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])
  | where SubCategory in~ (SubCategoryList) //use in~ for inclusion or !in~ for exclusion
  | where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable") // use this filter if required to exclude certain toggles
  | project TimeGenerated, Computer, User, Process, ParentImage, CommandLine, SubCategory, Toggle
  | extend timestamp = TimeGenerated, AccountName = tostring(split(User, @'\')[1]), AccountUPNSuffix = tostring(split(User, @'\')[0]), DeviceName = Computer
  )
  )
  | extend Account = strcat(AccountDomain, "\\", AccountName)
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountDomain
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DeviceName
version: 1.2.3
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Others" ]

Stages and Predicates

Parameters

let timeframe = 1d;
let AccountAllowList = dynamic(['SYSTEM']);
let SubCategoryList = dynamic(["Logoff", "Account Lockout", "User Account Management", "Authorization Policy Change"]);
let tokens = dynamic(["clear", "remove", "success:disable","failure:disable"]);

Union isfuzzy=true (3 Sources)

Each leg below queries one source; the rule matches if any leg does. Sources: SecurityEvent, DeviceProcessEvents, Event

Leg 1: SecurityEvent

SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where CommandLine has_any (tokens)
| where AccountType !~ "Machine" and Account !in~ (AccountAllowList)
| parse CommandLine with * "/subcategory:" subcategorytoken
| extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])
| where SubCategory in~ (SubCategoryList)
| where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable")
| project TimeGenerated, Computer, Account, SubjectDomainName, SubjectUserName, Process, ParentProcessName, CommandLine, SubCategory, Toggle
| extend timestamp = TimeGenerated, AccountName = SubjectUserName, AccountDomain = SubjectDomainName, DeviceName = Computer

Leg 2: DeviceProcessEvents

DeviceProcessEvents
| where TimeGenerated >= ago(timeframe)
| where InitiatingProcessCommandLine has_any (tokens)
| where AccountName !in~ (AccountAllowList)
| parse InitiatingProcessCommandLine with * "/subcategory:" subcategorytoken
| extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])
| where SubCategory in~ (SubCategoryList)
| where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable")
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessParentFileName,  InitiatingProcessCommandLine, SubCategory, Toggle
| extend timestamp = TimeGenerated, AccountName = InitiatingProcessAccountName, AccountDomain = InitiatingProcessAccountDomain

Leg 3: Event

Event
| where TimeGenerated > ago(timeframe)
| where Source == "Microsoft-Windows-Sysmon"
| where EventID == 1
| extend EventData = parse_xml(EventData).DataItem.EventData.Data
| mv-expand bagexpansion=array EventData
| evaluate bag_unpack(EventData)
| extend Key=tostring(['@Name']), Value=['#text']
| evaluate pivot(Key, any(Value), TimeGenerated, Source, EventLog, Computer, EventLevel, EventLevelName, EventID, UserName, RenderedDescription, MG, ManagementGroupName, Type, _ResourceId)
| where CommandLine has_any (tokens)
| where User !in~ (AccountAllowList)
| parse CommandLine with * "/subcategory:" subcategorytoken
| extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])
| where SubCategory in~ (SubCategoryList)
| where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable")
| project TimeGenerated, Computer, User, Process, ParentImage, CommandLine, SubCategory, Toggle
| extend timestamp = TimeGenerated, AccountName = tostring(split(User, @'\')[1]), AccountUPNSuffix = tostring(split(User, @'\')[0]), DeviceName = Computer

Applied to the Combined Result

| extend Account = strcat(AccountDomain, "\\", AccountName)

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AccountTypene
  • Machine
field:"AccountType" kind:ne value:"Machine"
CommandLinematch
  • clear transforms: term
  • failure:disable transforms: term
  • remove transforms: term
  • success:disable transforms: term
field:"CommandLine" kind:match
EventIDeq
  • 1 corpus 12 (kusto 11, elastic 1)
field:"EventID" kind:eq value:"1"
InitiatingProcessCommandLinematch
  • clear transforms: term
  • failure:disable transforms: term
  • remove transforms: term
  • success:disable transforms: term
field:"ParentCommandLine" kind:match
SubCategoryin
  • Account Lockout
  • Authorization Policy Change
  • Logoff
  • User Account Management
field:"SubCategory" kind:in

Exclusions

The rule actively suppresses these predicates.

Output Fields

These fields are emitted when the rule matches.

FieldSource
CommandLineproject
Computerproject
ParentImageproject
Processproject
SubCategoryproject
TimeGeneratedproject
Toggleproject
Userproject
AccountNameextend
AccountUPNSuffixextend
DeviceNameextend
timestampextend
Accountextend

Cisco SEG - Multiple suspiciuos attachments received

#
Status
available
Severity
high
Time window
10m
Group by
EventMessage
Source
github.com/Azure/Azure-Sentinel

'Detects possibly phishing emails.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: dfdb9a73-4335-4bb4-b29b-eb713bce61a6
name: Cisco SEG - Multiple suspiciuos attachments received
description: |
  'Detects possibly phishing emails.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1566
query: |
  let r_threshold = 5;
  CiscoSEGEvent
  | where NetworkDirection =~ 'Incoming'
  | where isnotempty(EventMessage)
  | where strlen(EventMessage) < 20
  | summarize rec=makeset(DstUserName) by EventMessage, bin(TimeGenerated, 10m)
  | where array_length(rec) > r_threshold
  | extend AccountCustomEntity = rec
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let r_threshold = 5;

Stage 1: source

CiscoSEGEvent

Stage 2: where

| where NetworkDirection =~ 'Incoming'

Stage 3: where

| where isnotempty(EventMessage)

Stage 4: where

| where strlen(EventMessage) < 20

Stage 5: summarize

| summarize rec=makeset(DstUserName) by EventMessage, bin(TimeGenerated, 10m)

Stage 6: where

| where array_length(rec) > r_threshold

Stage 7: extend

| extend AccountCustomEntity = rec

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
EventMessagesummarize
recsummarize
AccountCustomEntityextend

CYFIRMA - High severity File Hash Indicators with Block Action Rule

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

"This query retrieves file hash indicators marked for blocking, with no assigned role, from the CyfirmaIndicators_CL table. It extracts MD5, SHA1, and SHA256 hashes and includes threat metadata for use in preventive security controls such as EDRs, threat intel platforms, or automated blocklists."

MITRE ATT&CK Coverage

Rule Body

id: 7cb829b2-915a-42c2-adb9-725e9ce9bf43
name: CYFIRMA - High severity File Hash Indicators with Block Action Rule
description: |
  "This query retrieves file hash indicators marked for blocking, with no assigned role, from the CyfirmaIndicators_CL table. 
  It extracts MD5, SHA1, and SHA256 hashes and includes threat metadata for use in preventive security controls such as EDRs, threat intel platforms, or automated blocklists."
version: 1.0.1
kind: Scheduled
severity: High
enabled: false
requiredDataConnectors:
  - connectorId: CyfirmaCyberIntelligenceDC
    dataTypes:
      - CyfirmaIndicators_CL
query: |
  // File Hash Indicators with Block Action
  let timeFrame = 5m;
  CyfirmaIndicators_CL 
  | where ConfidenceScore >= 80
      and TimeGenerated between (ago(timeFrame) .. now())
      and pattern contains 'file:hashes' and RecommendedActions has 'Block' and (isempty(Roles) or not(Roles has_any ('Malware', 'Trojan')))
  | extend MD5 = extract(@"file:hashes\.md5\s*=\s*'([a-fA-F0-9]{32})'", 1, pattern)
  | extend SHA1 = extract(@"file:hashes\.'SHA-1'\s*=\s*'([a-fA-F0-9]{40})'", 1, pattern)
  | extend SHA256 = extract(@"file:hashes\.'SHA-256'\s*=\s*'([a-fA-F0-9]{64})'", 1, pattern)
  | extend
      Algo_MD5='MD5',
      Algo_SHA1= 'SHA1',
      Algo_SHA256='SHA256',
      ProviderName = 'CYFIRMA',
      ProductName = 'DeCYFIR/DeTCT'
  | project  
      MD5,
      SHA1,
      SHA256,
      Algo_MD5,
      Algo_SHA1,
      Algo_SHA256,
      ThreatActors,
      Sources,
      RecommendedActions,
      Roles,
      Country,
      name,
      Description,
      ConfidenceScore,
      SecurityVendors,
      IndicatorID,
      created,
      modified,
      valid_from,
      Tags,
      ThreatType,
      TimeGenerated,
      ProductName,
      ProviderName
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: 5m
suppressionEnabled: true
tactics:
  - Execution
  - InitialAccess
  - DefenseEvasion
  - Impact
relevantTechniques:
  - T1204
  - T1566
  - T1027
  - T1486
  - T1566.001
alertDetailsOverride:
  alertDisplayNameFormat: "High-Confidence File Hash Indicators with Block Action - {{name}} "
  alertDescriptionFormat: "{{Description}} - {{name}} "
  alertDynamicProperties:
    - alertProperty: ProductName
      value: ProductName
    - alertProperty: ProviderName
      value: ProviderName
customDetails:
  ThreatActors: ThreatActors
  Sources: Sources
  RecommendedActions: RecommendedActions
  Roles: Roles
  Country: Country
  Description: Description
  ConfidenceScore: ConfidenceScore
  SecurityVendors: SecurityVendors
  IndicatorID: IndicatorID
  Created: created
  Modified: modified
  ValidFrom: valid_from
  Tags: Tags
  ThreatType: ThreatType
  TimeGenerated: TimeGenerated
entityMappings:
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: Algo_MD5
      - identifier: Value
        columnName: MD5
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: Algo_SHA1
      - identifier: Value
        columnName: SHA1
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: Algo_SHA256
      - identifier: Value
        columnName: SHA256
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult

Stages and Predicates

Parameters

let timeFrame = 5m;

Stage 1: source

CyfirmaIndicators_CL

Stage 2: where

| where ConfidenceScore >= 80
    and TimeGenerated between (ago(timeFrame) .. now())
    and pattern contains 'file:hashes' and RecommendedActions has 'Block' and (isempty(Roles) or not(Roles has_any ('Malware', 'Trojan')))

Stage 3: extend (4 consecutive steps)

| extend MD5 = extract(@"file:hashes\.md5\s*=\s*'([a-fA-F0-9]{32})'", 1, pattern)
| extend SHA1 = extract(@"file:hashes\.'SHA-1'\s*=\s*'([a-fA-F0-9]{40})'", 1, pattern)
| extend SHA256 = extract(@"file:hashes\.'SHA-256'\s*=\s*'([a-fA-F0-9]{64})'", 1, pattern)
| extend
    Algo_MD5='MD5',
    Algo_SHA1= 'SHA1',
    Algo_SHA256='SHA256',
    ProviderName = 'CYFIRMA',
    ProductName = 'DeCYFIR/DeTCT'

Stage 4: project

| project  
    MD5,
    SHA1,
    SHA256,
    Algo_MD5,
    Algo_SHA1,
    Algo_SHA256,
    ThreatActors,
    Sources,
    RecommendedActions,
    Roles,
    Country,
    name,
    Description,
    ConfidenceScore,
    SecurityVendors,
    IndicatorID,
    created,
    modified,
    valid_from,
    Tags,
    ThreatType,
    TimeGenerated,
    ProductName,
    ProviderName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Algo_MD5project
Algo_SHA1project
Algo_SHA256project
ConfidenceScoreproject
Countryproject
Descriptionproject
IndicatorIDproject
MD5project
ProductNameproject
ProviderNameproject
RecommendedActionsproject
Rolesproject
SHA1project
SHA256project
SecurityVendorsproject
Sourcesproject
Tagsproject
ThreatActorsproject
ThreatTypeproject
TimeGeneratedproject
createdproject
modifiedproject
nameproject
valid_fromproject

CYFIRMA - High severity Malicious Network Indicators Associated with Malware - Monitor Recommended Rule

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

"This analytics rule detects network-based indicators identified by CYFIRMA threat intelligence as associated with Command & Control (C2) infrastructure. These may represent attacker-controlled endpoints used to maintain persistence, exfiltrate data, or receive commands from malware-infected hosts."

MITRE ATT&CK Coverage

Rule Body

id: c7b0b6da-e9dc-405d-ad71-37661f56d40a
name: CYFIRMA - High severity Malicious Network Indicators Associated with Malware - Monitor Recommended Rule
description: |
  "This analytics rule detects network-based indicators identified by CYFIRMA threat intelligence as associated with Command & Control (C2) infrastructure.
  These may represent attacker-controlled endpoints used to maintain persistence, exfiltrate data, or receive commands from malware-infected hosts."
version: 1.0.1
kind: Scheduled
severity: High
enabled: false
requiredDataConnectors:
  - connectorId: CyfirmaCyberIntelligenceDC
    dataTypes:
      - CyfirmaIndicators_CL
query: |
  //Network Indicators Associated with Malware - Monitor Recommended Rule
  let timeFrame= 5m;
  CyfirmaIndicators_CL 
  | where ConfidenceScore >= 80
      and TimeGenerated between (ago(timeFrame) .. now())
      and pattern !contains 'file:hashes' and RecommendedActions has 'Monitor' and Roles has 'Malware'
  | extend IPv4 = extract(@"ipv4-addr:value\s*=\s*'([^']+)'", 1, pattern)
  | extend IPv6 = extract(@"ipv6-addr:value\s*=\s*'([^']+)'", 1, pattern)
  | extend URL = extract(@"url:value\s*=\s*'([^']+)'", 1, pattern)
  | extend Domain = extract(@"domain-name:value\s*=\s*'([^']+)'", 1, pattern)
  | extend parsed = parse_json(extensions)
  | extend extensionKeys = bag_keys(parsed)
  | mv-expand extensionKeys
  | extend extensionKeyStr = tostring(extensionKeys)
  | extend ext = parsed[extensionKeyStr]
  | extend props = ext.properties
  | extend 
      extension_id = extensionKeyStr,
      ASN_Owner = props.asn_owner,
      ASN = props.asn,
      ProviderName = 'CYFIRMA',
      ProductName = 'DeCYFIR/DeTCT'
  | project
      IPv4,
      IPv6,
      URL,
      Domain,
      ThreatActors,
      RecommendedActions,
      Sources,
      Roles,
      Country,
      IPAbuse,
      name,
      Description,
      ConfidenceScore,
      IndicatorID,
      created,
      modified,
      valid_from,
      Tags,
      ThreatType,
      TimeGenerated,
      SecurityVendors,
      ProductName,
      ProviderName
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: 5m
suppressionEnabled: true
tactics:
  - InitialAccess
  - Execution
  - CommandAndControl
relevantTechniques:
  - T1566
  - T1189
  - T1204
  - T1071
  - T1090
  - T1059
  - T1568
  - T1566.002
  - T1204.001
  - T1071.001
  - T1568.002
alertDetailsOverride:
  alertDisplayNameFormat: "High-Confidence Malicious Network Indicators Associated with Malware - Monitor Recommended - {{name}} "
  alertDescriptionFormat: "{{Description}} - {{name}} "
  alertDynamicProperties:
    - alertProperty: ProductName
      value: ProductName
    - alertProperty: ProviderName
      value: ProviderName
customDetails:
  ThreatActors: ThreatActors
  Sources: Sources
  RecommendedActions: RecommendedActions
  Roles: Roles
  Country: Country
  Description: Description
  ConfidenceScore: ConfidenceScore
  SecurityVendors: SecurityVendors
  IndicatorID: IndicatorID
  Created: created
  Modified: modified
  ValidFrom: valid_from
  Tags: Tags
  ThreatType: ThreatType
  TimeGenerated: TimeGenerated
  IPAbuse: IPAbuse
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPv4
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPv6
  - entityType: DNS
    fieldMappings:
      - identifier: DomainName
        columnName: Domain
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: URL
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult

Stages and Predicates

Parameters

let timeFrame = 5m;

Stage 1: source

CyfirmaIndicators_CL

Stage 2: where

| where ConfidenceScore >= 80
    and TimeGenerated between (ago(timeFrame) .. now())
    and pattern !contains 'file:hashes' and RecommendedActions has 'Monitor' and Roles has 'Malware'

Stage 3: extend (6 consecutive steps)

| extend IPv4 = extract(@"ipv4-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend IPv6 = extract(@"ipv6-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend URL = extract(@"url:value\s*=\s*'([^']+)'", 1, pattern)
| extend Domain = extract(@"domain-name:value\s*=\s*'([^']+)'", 1, pattern)
| extend parsed = parse_json(extensions)
| extend extensionKeys = bag_keys(parsed)

Stage 4: mv-expand

| mv-expand extensionKeys

Stage 5: extend (4 consecutive steps)

| extend extensionKeyStr = tostring(extensionKeys)
| extend ext = parsed[extensionKeyStr]
| extend props = ext.properties
| extend 
    extension_id = extensionKeyStr,
    ASN_Owner = props.asn_owner,
    ASN = props.asn,
    ProviderName = 'CYFIRMA',
    ProductName = 'DeCYFIR/DeTCT'

Stage 6: project

| project
    IPv4,
    IPv6,
    URL,
    Domain,
    ThreatActors,
    RecommendedActions,
    Sources,
    Roles,
    Country,
    IPAbuse,
    name,
    Description,
    ConfidenceScore,
    IndicatorID,
    created,
    modified,
    valid_from,
    Tags,
    ThreatType,
    TimeGenerated,
    SecurityVendors,
    ProductName,
    ProviderName

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
patterncontainsfile:hashesexcludes:pattern field:"pattern" value:"file:hashes"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ConfidenceScoreproject
Countryproject
Descriptionproject
Domainproject
IPAbuseproject
IPv4project
IPv6project
IndicatorIDproject
ProductNameproject
ProviderNameproject
RecommendedActionsproject
Rolesproject
SecurityVendorsproject
Sourcesproject
Tagsproject
ThreatActorsproject
ThreatTypeproject
TimeGeneratedproject
URLproject
createdproject
modifiedproject
nameproject
valid_fromproject

CYFIRMA - Social and Public Exposure - Confidential Files Information Exposure Rule

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
medium
Time window
5m
Source
github.com/Azure/Azure-Sentinel

"This rule detects Medium-severity alerts from CYFIRMA regarding exposure of confidential files or forms linked to internal or client-related information, publicly accessible on platforms. These exposures could lead to data leakage, compliance violations, or targeted attacks."

MITRE ATT&CK Coverage

Rule Body

id: a2984be5-8d69-4139-b98f-e89c9c421c27
name: CYFIRMA - Social and Public Exposure - Confidential Files Information Exposure Rule
description: |
  "This rule detects Medium-severity alerts from CYFIRMA regarding exposure of confidential files or forms linked to internal or client-related information, publicly accessible on platforms. 
  These exposures could lead to data leakage, compliance violations, or targeted attacks."
version: 1.0.1
kind: Scheduled
severity: Medium
requiredDataConnectors:
  - connectorId: CyfirmaDigitalRiskAlertsConnector
    dataTypes:
      - CyfirmaSPEConfidentialFilesAlerts_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - InitialAccess
  - Exfiltration
  - Collection
  - Reconnaissance
relevantTechniques:
  - T1189
  - T1213
  - T1593
  - T1567.002
query: |
  // Medium severity - Social and Public Exposure - Confidential Files Information Exposure
  let timeFrame = 5m;
  CyfirmaSPEConfidentialFilesAlerts_CL
  | where severity == 'High' and TimeGenerated between (ago(timeFrame) .. now())
  | extend
      Description=description,
      FirstSeen=first_seen,
      LastSeen=last_seen,
      RiskScore=risk_score,
      AlertUID=alert_uid,
      UID=uid,
      AssetType=asset_type,
      AssetValue=signature,
      Impact=impact,
      Recommendation=recommendation,
      ProviderName='CYFIRMA',
      ProductName='DeCYFIR/DeTCT',
      AlertTitle=Alert_title
  | project
      TimeGenerated,
      Description,
      RiskScore,
      FirstSeen,
      LastSeen,
      AlertUID,
      UID,
      AssetType,
      AssetValue,
      Impact,
      Recommendation,
      ProductName,
      ProviderName,
      AlertTitle
customDetails:
  AssetType: AssetType
  AssetValue: AssetValue
  RiskScore: RiskScore
  Impact: Impact
  Description: Description
  Recommendation: Recommendation
  FirstSeen: FirstSeen
  LastSeen: LastSeen
  AlertUID: AlertUID
  UID: UID
  TimeGenerated: TimeGenerated
alertDetailsOverride:
  alertDisplayNameFormat: "Cyfirma - Medium Severity Alert: Confidential Files Information Exposure on Public Platforms - {{AlertTitle}}  "
  alertDescriptionFormat: "{{Description}} "
  alertDynamicProperties:
    - alertProperty: ProductName
      value: ProductName
    - alertProperty: ProviderName
      value: ProviderName
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult

Stages and Predicates

Parameters

let timeFrame = 5m;

Stage 1: source

CyfirmaSPEConfidentialFilesAlerts_CL

Stage 2: where

| where severity == 'High' and TimeGenerated between (ago(timeFrame) .. now())

Stage 3: extend

| extend
    Description=description,
    FirstSeen=first_seen,
    LastSeen=last_seen,
    RiskScore=risk_score,
    AlertUID=alert_uid,
    UID=uid,
    AssetType=asset_type,
    AssetValue=signature,
    Impact=impact,
    Recommendation=recommendation,
    ProviderName='CYFIRMA',
    ProductName='DeCYFIR/DeTCT',
    AlertTitle=Alert_title

Stage 4: project

| project
    TimeGenerated,
    Description,
    RiskScore,
    FirstSeen,
    LastSeen,
    AlertUID,
    UID,
    AssetType,
    AssetValue,
    Impact,
    Recommendation,
    ProductName,
    ProviderName,
    AlertTitle

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AlertTitleproject
AlertUIDproject
AssetTypeproject
AssetValueproject
Descriptionproject
FirstSeenproject
Impactproject
LastSeenproject
ProductNameproject
ProviderNameproject
Recommendationproject
RiskScoreproject
TimeGeneratedproject
UIDproject

Dataverse - Anomalous application user activity

#
Status
available
Severity
medium
Time window
14d
Group by
AnomalyTimeGenerated, InstanceUrl, OriginalObjectId, UserId
Source
github.com/Azure/Azure-Sentinel

Identifies anomalies in activity patterns of Dataverse application (non-interactive) users, based on activity falling outside the normal pattern of use.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: 0820da12-e895-417f-9175-7c256fcfb33e
kind: Scheduled
name: Dataverse - Anomalous application user activity
description: Identifies anomalies in activity patterns of Dataverse application (non-interactive)
  users, based on activity falling outside the normal pattern of use.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Dataverse
    dataTypes:
      - DataverseActivity
queryFrequency: 5h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - Execution
  - Persistence
relevantTechniques:
  - T1528
  - T1569
  - T0871
  - T0834
  - T0859
query: |
  let query_lookback = 14d;
  let query_frequency = 5h;
  let anomaly_threshold = 2.5;
  let seasonality = -1;
  let trend = 'linefit';
  let step_duration = 5h;
  let app_user_regex = "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\.com$";
  let guid_regex = "([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12})";
  let application_users = DataverseActivity
      | where TimeGenerated >= ago(query_frequency)
      | where UserId !endswith "@onmicrosoft.com" and UserId != "Unknown"
      | summarize by UserId
      | where split(UserId, "@")[1] matches regex app_user_regex;
  DataverseActivity
  | where TimeGenerated >= startofday(ago(query_lookback))
  | where UserId in (application_users)
  | where isnotempty(OriginalObjectId)
  | make-series TotalEvents = count() default=0 on TimeGenerated from startofday(ago(query_lookback)) to now() step step_duration by UserId, InstanceUrl, OriginalObjectId
  | extend (Anomalies, Score, Baseline) = series_decompose_anomalies(TotalEvents, anomaly_threshold, seasonality, trend)
  | mv-expand
      TotalEvents to typeof(double),
      AnomalyTimeGenerated = TimeGenerated to typeof(datetime),
      Anomalies to typeof(double),
      Score to typeof(double),
      Baseline to typeof(long)
  | where Anomalies > 0
  | extend Details = bag_pack(
                         "TotalEvents",
                         TotalEvents,
                         "Anomalies",
                         Anomalies,
                         "Baseline",
                         Baseline,
                         "Score",
                         Score,
                         "OriginalObjectId",
                         OriginalObjectId
                     )
  | summarize Details = make_set(Details, 100) by UserId, InstanceUrl, AnomalyTimeGenerated
  | extend
      CloudAppId = int(32780),
      AadUserId = extract(guid_regex, 1, tostring(split(UserId, "@")[0]))
  | project
      AnomalyTimeGenerated,
      UserId,
      AadUserId,
      InstanceUrl,
      Details,
      CloudAppId
eventGroupingSettings:
  aggregationKind: AlertPerResult
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: AadUserId
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: CloudAppId
      - identifier: InstanceName
        columnName: InstanceUrl
alertDetailsOverride:
  alertDisplayNameFormat: 'Dataverse - Non-interactive account anomaly detected in
    {{InstanceUrl}} '
  alertDescriptionFormat: 'Anomaly detected on {{UserId}} in {{InstanceUrl}}.  Details:
    {{Details}}'
customDetails:
  InstranceUrl: InstanceUrl
version: 3.2.0

Stages and Predicates

Parameters

let query_lookback = 14d;
let query_frequency = 5h;
let anomaly_threshold = 2.5;
let seasonality = -1;
let trend = 'linefit';
let step_duration = 5h;
let app_user_regex = "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\.com$";
let guid_regex = "([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12})";

Let Binding: application_users used in Stage 3

let application_users = DataverseActivity
    | where TimeGenerated >= ago(query_frequency)
    | where UserId !endswith "@onmicrosoft.com" and UserId != "Unknown"
    | summarize by UserId
    | where split(UserId, "@")[1] matches regex app_user_regex;

Stage 1: source

DataverseActivity

Stage 2: where

| where TimeGenerated >= startofday(ago(query_lookback))

Stage 3: where

| where UserId in (application_users)

Stage 4: where

| where isnotempty(OriginalObjectId)

The stages below score time-series anomalies (make-series, series_decompose_anomalies).

Stage 5: make-series

| make-series TotalEvents = count() default=0 on TimeGenerated from startofday(ago(query_lookback)) to now() step step_duration by UserId, InstanceUrl, OriginalObjectId

Stage 6: extend

| extend (Anomalies, Score, Baseline) = series_decompose_anomalies(TotalEvents, anomaly_threshold, seasonality, trend)

Stage 7: mv-expand

| mv-expand
    TotalEvents to typeof(double),
    AnomalyTimeGenerated = TimeGenerated to typeof(datetime),
    Anomalies to typeof(double),
    Score to typeof(double),
    Baseline to typeof(long)

Stage 8: where

| where Anomalies > 0

Stage 9: extend

| extend Details = bag_pack(
                       "TotalEvents",
                       TotalEvents,
                       "Anomalies",
                       Anomalies,
                       "Baseline",
                       Baseline,
                       "Score",
                       Score,
                       "OriginalObjectId",
                       OriginalObjectId
                   )

Stage 10: summarize

| summarize Details = make_set(Details, 100) by UserId, InstanceUrl, AnomalyTimeGenerated

Stage 11: extend

| extend
    CloudAppId = int(32780),
    AadUserId = extract(guid_regex, 1, tostring(split(UserId, "@")[0]))

Stage 12: project

| project
    AnomalyTimeGenerated,
    UserId,
    AadUserId,
    InstanceUrl,
    Details,
    CloudAppId

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AadUserIdproject
AnomalyTimeGeneratedproject
CloudAppIdproject
Detailsproject
InstanceUrlproject
UserIdproject

Detect known risky user agents (ASIM Web Session)

#
Status
available
Severity
medium
Time window
5m
Group by
DstIpAddr, DstPortNumber, HttpUserAgent, SrcHostname, SrcIpAddr, SrcUsername, Url, UserAgentCategory, joiner
Source
github.com/Azure/Azure-Sentinel

'This rule is designed to flag web requests that contain a user agent header that is recognized as malicious. It relies on a predefined list of known user agents, which is referenced from a specific CSV file'

MITRE ATT&CK Coverage

Rule Body

id: 6a4dbcf8-f5e2-4b33-b34f-2db6487613f0
name: Detect known risky user agents (ASIM Web Session)
description: |
  'This rule is designed to flag web requests that contain a user agent header that is recognized as malicious. It relies on a predefined list of known user agents, which is referenced from a specific CSV file'
severity: Medium
status: Available 
tags:
  - Schema: WebSession
    SchemaVersion: 0.2.6
requiredDataConnectors: []
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - CommandAndControl
relevantTechniques:
  - T1190
  - T1133
  - T1071
query: |
  let lookback = 5m;
  let knownUserAgentsIndicators = materialize(externaldata(UserAgent: string, UserAgentCategory: string)
    [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/UnusualUserAgents.csv"] 
    with(format="csv", ignoreFirstRecord=True)
    | extend joiner = 1);
  let CustomUserAgentIndicators = (_ASIM_GetWatchlistRaw("UnusualUserAgents") // Create new Watchlist and add your custom indicators(Optional)
    | extend
        UserAgent = tostring(WatchlistItem["UserAgent"]),
        UserAgentCategory = tostring(WatchlistItem["UserAgentCategory"])
    | project UserAgent, UserAgentCategory, joiner = 1
    | where isnotempty(UserAgent) and isnotempty(UserAgentCategory));
  let CombinedUAList = union knownUserAgentsIndicators, CustomUserAgentIndicators;
  let knownUserAgents=toscalar(CombinedUAList
    | where isnotempty(UserAgent)
    | summarize make_list(UserAgent, 1000));
  _Im_WebSession (starttime=ago(lookback), httpuseragent_has_any=knownUserAgents)
  | project SrcIpAddr, SrcUsername, SrcHostname, HttpUserAgent, Url, DstIpAddr, DstPortNumber, TimeGenerated
  | extend joiner = 1
  | join kind=inner CombinedUAList on joiner
  | where HttpUserAgent has UserAgent
  | summarize
    EventCount=count(),
    EventStartTime = min(TimeGenerated),
    EventEndTime=max(TimeGenerated)
    by SrcIpAddr, SrcUsername, SrcHostname, Url, HttpUserAgent, DstIpAddr, DstPortNumber, UserAgentCategory
  | extend Name = iif(SrcUsername contains "@", tostring(split(SrcUsername,'@',0)[0]),SrcUsername), UPNSuffix = iif(SrcUsername contains "@",tostring(split(SrcUsername,'@',1)[0]),"")
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: DstIpAddr
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: SrcHostname
eventGroupingSettings:
  aggregationKind: AlertPerResult
customDetails:
  EventCount: EventCount
  EventStartTime: EventStartTime
  EventEndTime: EventEndTime
  HttpUserAgent: HttpUserAgent
  UserAgentCategory: UserAgentCategory
alertDetailsOverride:
  alertDisplayNameFormat: "User '{{SrcUsername}}' with IP '{{SrcIpAddr}}' has been observed using User Agent categorized as '{{UserAgentCategory}}'"
  alertDescriptionFormat: "User accessed this URL '{{Url}}' using User Agent '{{HttpUserAgent}}'. Perform a thorough analysis of the requests associated with this user agent header"
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let lookback = 5m;
let CombinedUAList = union knownUserAgentsIndicators, CustomUserAgentIndicators;

Let Binding: knownUserAgentsIndicators

let knownUserAgentsIndicators = materialize(externaldata(UserAgent: string, UserAgentCategory: string)
  [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/UnusualUserAgents.csv"] 
  with(format="csv", ignoreFirstRecord=True)
  | extend joiner = 1);

Let Binding: CustomUserAgentIndicators

let CustomUserAgentIndicators = (_ASIM_GetWatchlistRaw("UnusualUserAgents")
  | extend
      UserAgent = tostring(WatchlistItem["UserAgent"]),
      UserAgentCategory = tostring(WatchlistItem["UserAgentCategory"])
  | project UserAgent, UserAgentCategory, joiner = 1
  | where isnotempty(UserAgent) and isnotempty(UserAgentCategory));

Let Binding: knownUserAgents used in Stage 1

let knownUserAgents = toscalar(CombinedUAList
  | where isnotempty(UserAgent)
  | summarize make_list(UserAgent, 1000));

Stage 1: source

_Im_WebSession (starttime=ago(lookback), httpuseragent_has_any=knownUserAgents)

Stage 2: project

| project SrcIpAddr, SrcUsername, SrcHostname, HttpUserAgent, Url, DstIpAddr, DstPortNumber, TimeGenerated

Stage 3: extend

| extend joiner = 1

Stage 4: join

join kind=inner (CombinedUAList) on joiner

Stage 5: where

where HttpUserAgent =~ UserAgent

Stage 6: summarize

summarize EventCount, EventEndTime, EventStartTime by SrcIpAddr, SrcUsername, SrcHostname, Url, HttpUserAgent, DstIpAddr, DstPortNumber, UserAgentCategory

Stage 7: extend

extend Name, UPNSuffix

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
UserAgentis_not_null
  • (no value, null check)
field:"UserAgent" kind:is_not_null
UserAgentCategoryis_not_null
  • (no value, null check)
field:"UserAgentCategory" kind:is_not_null

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstIpAddrsummarize
DstPortNumbersummarize
EventCountsummarize
EventEndTimesummarize
EventStartTimesummarize
HttpUserAgentsummarize
SrcHostnamesummarize
SrcIpAddrsummarize
SrcUsernamesummarize
Urlsummarize
UserAgentCategorysummarize
Nameextend
UPNSuffixextend

Device Alert Surge

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
high
Time window
1h
Group by
hostname, id
Source
github.com/Azure/Azure-Sentinel

'Triggers an incident when a device generates 5 or more Medium or High severity alerts, indicating potential compromise.'

MITRE ATT&CK Coverage

Rule Body

id: 5c8e1f2e-9d6b-4f4a-8f3e-123456789abc
name: Device Alert Surge
description: |
  'Triggers an incident when a device generates 5 or more Medium or High severity alerts, indicating potential compromise.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: MorphisecCCF
    dataTypes:
      - Morphisec
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
suppressionDuration: 5h
suppressionEnabled: false
tactics:
  - Execution
  - DefenseEvasion
relevantTechniques:
  - T1059
  - T1204
query: |
  MorphisecAlerts_CL
  | where threatMessageArrivalTime >= ago(1h)
  | where attackSeverity in ("HIGH", "MEDIUM")
  | summarize arg_max(threatMessageArrivalTime, *) by id
  | summarize AlertCount = dcount(id) by hostname
  | where AlertCount >= 5
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: hostname
eventGroupingSettings:
  aggregationKind: SingleAlert
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
    groupByEntities:
      - Host
    groupByAlertDetails:
      - DisplayName
alertDetailsOverride:
  alertDisplayNameFormat: "Device alert surge detected: {{hostname}}"
  alertDescriptionFormat: "Device {{hostname}} triggered {{AlertCount}} distinct High/Medium alerts within the last hour."
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

MorphisecAlerts_CL

Stage 2: where

| where threatMessageArrivalTime >= ago(1h)

Stage 3: where

| where attackSeverity in ("HIGH", "MEDIUM")

Stage 4: summarize

| summarize arg_max(threatMessageArrivalTime, *) by id

Stage 5: summarize

| summarize AlertCount = dcount(id) by hostname

Stage 6: where

| where AlertCount >= 5

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AlertCountsummarize
hostnamesummarize

GitLab - Repository visibility to Public

#
Status
available
Severity
medium
Time window
1d
Source
github.com/Azure/Azure-Sentinel

'This query leverages GitLab Audit Logs. A repository in GitLab changed visibility from Private or Internal to Public which could indicate compromise, error or misconfiguration leading to exposing the repository to the public.'

MITRE ATT&CK Coverage

Rule Body

id: 8b291c3d-90ba-4ebf-af2c-0283192d430e
name: GitLab - Repository visibility to Public
description: |
  'This query leverages GitLab Audit Logs. A repository in GitLab changed visibility from Private or Internal to Public which could indicate compromise, error or misconfiguration leading to exposing the repository to the public.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    dataTypes: 
      - Syslog
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
  - DefenseEvasion
  - CredentialAccess
relevantTechniques:
  - T1556
query: |
  GitLabAudit
  | where SourceVisibility != "Public" and ChangeType == "visibility" and TargetVisibility == "Public"
  | project TimeGenerated, EventTime, IPAddress, AuthorName, ChangeType, TargetType, SourceVisibility,  TargetVisibility, EntityName
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AuthorName
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: EntityName        
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

GitLabAudit

Stage 2: where

| where SourceVisibility != "Public" and ChangeType == "visibility" and TargetVisibility == "Public"

Stage 3: project

| project TimeGenerated, EventTime, IPAddress, AuthorName, ChangeType, TargetType, SourceVisibility,  TargetVisibility, EntityName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AuthorNameproject
ChangeTypeproject
EntityNameproject
EventTimeproject
IPAddressproject
SourceVisibilityproject
TargetTypeproject
TargetVisibilityproject
TimeGeneratedproject

IaaS shadow admin detected

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
high
Time window
30m
Source
github.com/Azure/Azure-Sentinel

The policy detects shadow admin users in AWS or Azure.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 31f43e9d-1839-4baf-a668-54c28b98af3e
name: IaaS shadow admin detected
kind: Scheduled
description: The policy detects shadow admin users in AWS or Azure.
severity: High
status: Available
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |-
  Authomize_v2_CL
  | where ingestion_time() >= ago(30m)
  | extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
  | where Policy has "IaaS shadow admin detected"
  | project  EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: URL
requiredDataConnectors:
- connectorId: Authomize
  dataTypes: [ "Authomize_v2_CL" ]
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AnyAlert
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertnameFormat: Alert from Authomize - IaaS shadow admin detected
  alertDescriptionFormat: |
    IaaS shadow admin detected. The policy detects shadow admin users in AWS or Azure.
  alertSeverity: Severity
  alertTactics: Tactics
  alertDynamicProperties:
  - alertProperty: AlertLink
    value: URL
customDetails:
  AuthomizeEventID: EventID
  EventName: Policy
  EventDescription: Description
  EventRecommendation: Recommendation
  ReferencedURL: URL
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.2

Stages and Predicates

Stage 1: source

Authomize_v2_CL

Stage 2: where

| where ingestion_time() >= ago(30m)

Stage 3: extend

| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s

Stage 4: where

| where Policy has "IaaS shadow admin detected"

Stage 5: project

| project  EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Policymatch
  • IaaS shadow admin detected transforms: term
field:"policy_name_s" kind:match value:"IaaS shadow admin detected"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Categoryproject
Descriptionproject
EventIDproject
Policyproject
Recommendationproject
Severityproject
Tacticsproject
URLproject

Malware Activity Detected

#
Status
available
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

Detects when restore points marked as suspicious. This might indicate potential compromise of backup data.

Rule Body

id: 9a7c80ef-8dc2-4b07-834d-b9ca18d603f7
name: Malware Activity Detected
description: Detects when restore points marked as suspicious. This might indicate
  potential compromise of backup data.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: |
  Veeam_GetSecurityEvents
  | where instanceId == 41600
  | extend ActivityType = extract("ActivityType=\"([^\"]*)\"", 1, SyslogMessage)
  | extend MachineDisplayName = extract("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\s*\\(([^)]+)\\)", 1, Description)
  | extend MachineUuid = extract("([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})", 1, Description)
  | project
      Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
      DataSource = original_host,
      EventId = instanceId,
      UserName = user,
      ["Malware Detection Method"] = ActivityType,
      MessageDetails = Description,
      Severity = SeverityDescription,
      MachineDisplayName,
      MachineUuid
version: 1.0.2
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity
  MachineDisplayName: MachineDisplayName
  MachineUuid: MachineUuid

Stages and Predicates

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 41600

Stage 3: extend (3 consecutive steps)

| extend ActivityType = extract("ActivityType=\"([^\"]*)\"", 1, SyslogMessage)
| extend MachineDisplayName = extract("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\s*\\(([^)]+)\\)", 1, Description)
| extend MachineUuid = extract("([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})", 1, Description)

Stage 4: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = original_host,
    EventId = instanceId,
    UserName = user,
    ["Malware Detection Method"] = ActivityType,
    MessageDetails = Description,
    Severity = SeverityDescription,
    MachineDisplayName,
    MachineUuid

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MachineDisplayNameproject
MachineUuidproject
Malware Detection Methodproject
MessageDetailsproject
Severityproject
UserNameproject

Pathlock TDnR - SAP Public Cloud Security Audit Events

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

Detects security audit log events from SAP Public Cloud systems, forwarded by Pathlock Threat Detection and Response. Public cloud security audit events capture authentication, authorization, and configuration activity in SAP S/4HANA Cloud and other SAP Public Cloud environments.

MITRE ATT&CK Coverage

TacticTechniques
Discovery

Rule Body

id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c57
name: Pathlock TDnR - SAP Public Cloud Security Audit Events
kind: Scheduled
description: >-
  Detects security audit log events from SAP Public Cloud systems, forwarded by Pathlock Threat
  Detection and Response. Public cloud security audit events capture authentication, authorization,
  and configuration activity in SAP S/4HANA Cloud and other SAP Public Cloud environments.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Pathlock_TDnR
    dataTypes:
      - Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
relevantTechniques:
  - T1082
query: |
  Pathlock_TDnR_CL
  | where DataSource == "SEC_AUDIT_LOG_CLOUD"
  | project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
            Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
            MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Bname
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Hostname
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIp
eventGroupingSettings:
  aggregationKind: SingleAlert
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AnyAlert
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.0

Stages and Predicates

Stage 1: source

Pathlock_TDnR_CL

Stage 2: where

| where DataSource == "SEC_AUDIT_LOG_CLOUD"

Stage 3: project

| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
          Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
          MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AffectedUserproject
Areaproject
Bnameproject
CentralTsproject
DataSourceproject
DestIpproject
Eventidproject
Hostnameproject
Instanceproject
LogLineproject
MessageV1project
MessageV2project
MessageV3project
MessageV4project
MsgIdproject
MsgNoproject
MsgTypeproject
Reportproject
SrcIpproject
Subidproject
Sysidproject
Tcodeproject
TimeGeneratedproject

PCI App with Open Findings Before Audit Window

#
Status
available
Severity
high
Time window
1455m
Group by
$left.ApplicationId, $right.WL_ApplicationId, $right.WL_Cal_ApplicationId, ApplicationId, Framework, StraTenantId, UpcomingAudit, WL_AppName, WL_Owner
Source
github.com/Azure/Azure-Sentinel

Detects PCI-scoped applications with open Critical/High findings when an upcoming PCI-DSS audit is within 30 days. Correlates PCIApplications and AuditCalendar watchlists to surface compliance risk before audit deadlines.

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: 0457b577-3864-42c9-9779-1c9441d86429
name: PCI App with Open Findings Before Audit Window
description: |
  Detects PCI-scoped applications with open Critical/High findings when an
  upcoming PCI-DSS audit is within 30 days. Correlates PCIApplications and
  AuditCalendar watchlists to surface compliance risk before audit deadlines.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: StratoSecurePush
    dataTypes:
      - StratoSecure_Findings_CL
queryFrequency: 1d
queryPeriod: 1455m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1485
query: |
  let auditWindowDays = 30;
  let PCIApps = _GetWatchlist("PCIApplications")
      | project WL_ApplicationId = column_ifexists("ApplicationId", ""),
          WL_AppName = column_ifexists("AppName", ""),
          WL_AuditDate = column_ifexists("AuditDate", ""),
          WL_Owner = column_ifexists("Owner", "");
  let AuditCalendar = _GetWatchlist("AuditCalendar")
      | where column_ifexists("Framework", "") =~ "PCI-DSS"
      | extend UpcomingAudit = todatetime(column_ifexists("AuditDate", ""))
      | where UpcomingAudit between (now() .. now() + auditWindowDays * 1d)
      | project WL_Cal_ApplicationId = column_ifexists("ApplicationId", ""),
          Framework = column_ifexists("Framework", ""), UpcomingAudit;
  StratoSecure_Findings_CL
      | where FixStatus =~ "open"
      | where Severity in ("Critical", "High")
      | join kind=inner PCIApps on $left.ApplicationId == $right.WL_ApplicationId
      | join kind=inner AuditCalendar on $left.ApplicationId == $right.WL_Cal_ApplicationId
      | summarize TotalOpenFindings = count(), MaxSeverityInt = max(SeverityInt),
          RuleIds = make_set(RuleId, 20), EndTime = max(TimeGenerated)
          by ApplicationId, WL_AppName, WL_Owner, Framework, UpcomingAudit, StraTenantId
      | extend MaxSeverity = case(
          MaxSeverityInt >= 9, "Critical",
          MaxSeverityInt >= 7, "High",
          MaxSeverityInt >= 4, "Medium",
          "Low")
      | project TimeGenerated = EndTime, ApplicationId, AppName = WL_AppName, Owner = WL_Owner,
          StraTenantId, Framework, UpcomingAudit, TotalOpenFindings, MaxSeverity, RuleIds
entityMappings:
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: ApplicationId
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let auditWindowDays = 30;

Let Binding: PCIApps used in Stage 4

let PCIApps = _GetWatchlist("PCIApplications")
    | project WL_ApplicationId = column_ifexists("ApplicationId", ""),
        WL_AppName = column_ifexists("AppName", ""),
        WL_AuditDate = column_ifexists("AuditDate", ""),
        WL_Owner = column_ifexists("Owner", "");

Let Binding: AuditCalendar used in Stage 5

let AuditCalendar = _GetWatchlist("AuditCalendar")
    | where column_ifexists("Framework", "") =~ "PCI-DSS"
    | extend UpcomingAudit = todatetime(column_ifexists("AuditDate", ""))
    | where UpcomingAudit between (now() .. now() + auditWindowDays * 1d)
    | project WL_Cal_ApplicationId = column_ifexists("ApplicationId", ""),
        Framework = column_ifexists("Framework", ""), UpcomingAudit;

Stage 1: source

StratoSecure_Findings_CL

Stage 2: where

| where FixStatus =~ "open"

Stage 3: where

| where Severity in ("Critical", "High")

Stage 4: join

| join kind=inner PCIApps on $left.ApplicationId == $right.WL_ApplicationId

Stage 5: join

| join kind=inner AuditCalendar on $left.ApplicationId == $right.WL_Cal_ApplicationId

Stage 6: summarize

summarize EndTime, MaxSeverityInt, RuleIds, TotalOpenFindings by ApplicationId, WL_AppName, WL_Owner, Framework, UpcomingAudit, StraTenantId

Stage 7: extend

extend MaxSeverity
MaxSeverity =
ifMaxSeverityInt >= 9"Critical"
elifMaxSeverityInt >= 7"High"
elifMaxSeverityInt >= 4"Medium"
else"Low"

Stage 8: project

project AppName, ApplicationId, Framework, MaxSeverity, Owner, RuleIds, StraTenantId, TimeGenerated, TotalOpenFindings, UpcomingAudit

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AppNameproject
ApplicationIdproject
Frameworkproject
MaxSeverityproject
Ownerproject
RuleIdsproject
StraTenantIdproject
TimeGeneratedproject
TotalOpenFindingsproject
UpcomingAuditproject

Restore Point Marked as Infected

#
Status
available
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

Detects when a restore point is marked as infected.

Rule Body

id: bad9d4b8-41cb-41ba-82c8-e20e771b3440
name: Restore Point Marked as Infected
description: Detects when a restore point is marked as infected.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 42220\n| project\n    Date\
  \ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n\
  \    EventId = instanceId,\n    UserName = user,\n    MessageDetails = Description,\n\
  \    Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity

Stages and Predicates

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 42220

Stage 3: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = original_host,
    EventId = instanceId,
    UserName = user,
    MessageDetails = Description,
    Severity = SeverityDescription

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Severityproject
UserNameproject

Server Oriented Cmdlet And User Oriented Cmdlet used

#
Status
available
Severity
high
Time window
1d
Group by
Caller, Computer
Source
github.com/Azure/Azure-Sentinel

'Detect if a server oriented cmdlet and a user oriented cmdlet that are monitored are launched by the same user in the same server within a 10 minutes timeframe'

MITRE ATT&CK Coverage

Rule Body

id: 7bce901b-9bc8-4948-8dfc-8f68878092d5
name: Server Oriented Cmdlet And User Oriented Cmdlet used
description: |
  'Detect if a server oriented cmdlet and a user oriented cmdlet that are monitored are launched by the same user in the same server within a 10 minutes timeframe'
requiredDataConnectors:
  - connectorId: ESI-ExchangeAdminAuditLogEvents
    dataTypes:
      - Event
severity: High
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - Exfiltration
  - Persistence
  - Collection
relevantTechniques:
  - T1020
  - T1098
  - T1114
query: |
  let timeframe = 1d;
  let spanoftime = 10m;
  let threshold = 0;
  ExchangeAdminAuditLogs 
    | where TimeGenerated > ago(2 * timeframe)
    | where isempty(UserOriented)
    | project serverExecutedTime = TimeGenerated,
      ServerCmdlet = CmdletName,
      ServerCmdletParams = CmdletParameters,
      Computer,
      Caller,
      ServerCmdletTargetObject = TargetObject
    | join kind= inner (
        ExchangeAdminAuditLogs
        | where TimeGenerated > ago(timeframe)
        | where UserOriented =~ 'Yes'
        | project userExecutedTime = TimeGenerated,
          UserCmdlet = CmdletName,
          UserCmdletParams = CmdletParameters,
          Computer,
          Caller,
          UserCmdletTargetObject = TargetObject,
          userPrincipalName,
          objectGUID,
          sAMAccountName,
          IsVIP)
      on Computer, Caller
    | where userExecutedTime - serverExecutedTime < spanoftime
    | extend TimeDelta = userExecutedTime - serverExecutedTime
    | extend TimeDeltaInverse = serverExecutedTime - userExecutedTime
    | where tolong(TimeDelta) >= threshold or tolong(TimeDeltaInverse) >= threshold
entityMappings:
- entityType: Mailbox
  fieldMappings:
    - identifier: MailboxPrimaryAddress
      columnName: userPrincipalName
    - identifier: Upn
      columnName: userPrincipalName
- entityType: Host
  fieldMappings:
    - identifier: FullName
      columnName: Computer
- entityType: Host
  fieldMappings:
    - identifier: HostName
      columnName: ServerCmdletTargetObject
- entityType: Account
  fieldMappings:
    - identifier: Name
      columnName: Caller
    - identifier: ObjectGuid
      columnName: objectGUID
version: 1.2.0
kind: Scheduled

Stages and Predicates

Parameters

let timeframe = 1d;
let spanoftime = 10m;
let threshold = 0;

Stage 1: source

ExchangeAdminAuditLogs

Stage 2: where

| where TimeGenerated > ago(2 * timeframe)

Stage 3: where

| where isempty(UserOriented)

Stage 4: project

| project serverExecutedTime = TimeGenerated,
    ServerCmdlet = CmdletName,
    ServerCmdletParams = CmdletParameters,
    Computer,
    Caller,
    ServerCmdletTargetObject = TargetObject

Stage 5: join

| join kind= inner (
      ExchangeAdminAuditLogs
      | where TimeGenerated > ago(timeframe)
      | where UserOriented =~ 'Yes'
      | project userExecutedTime = TimeGenerated,
        UserCmdlet = CmdletName,
        UserCmdletParams = CmdletParameters,
        Computer,
        Caller,
        UserCmdletTargetObject = TargetObject,
        userPrincipalName,
        objectGUID,
        sAMAccountName,
        IsVIP)
    on Computer, Caller

Stage 6: where userExecutedTime - serverExecutedTime < 10m

where userExecutedTime - serverExecutedTime < 10m

Stage 7: extend

extend TimeDelta

Stage 8: extend

extend TimeDeltaInverse

Stage 9: where

where (tolong(TimeDelta) >= 0 or tolong(TimeDeltaInverse) >= 0)

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Callerproject
Computerproject
ServerCmdletproject
ServerCmdletParamsproject
ServerCmdletTargetObjectproject
serverExecutedTimeproject
TimeDeltaextend
TimeDeltaInverseextend

SlackAudit - Multiple failed logins for user

#
Status
available
Severity
medium
Time window
5m
Group by
SrcUserName, bucket
Source
github.com/Azure/Azure-Sentinel

'Identifies multiple failed Slack logins for a user account within a short time window, which may indicate password guessing or brute-force activity.'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Rule Body

id: 93a91c37-032c-4380-847c-957c001957ad
name: SlackAudit - Multiple failed logins for user
description: |
  'Identifies multiple failed Slack logins for a user account within a short time window, which may indicate password
  guessing or brute-force activity.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SlackAuditAPI
    dataTypes:
      - SlackAudit_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: 
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
  let threshold = 10;
  SlackAudit
  | where DvcAction in~ ('user_login_failed')
  | summarize FailedLogins = count() by SrcUserName, bucket = bin(TimeGenerated, 5m)
  | where FailedLogins > threshold
  | extend AccountCustomEntity = SrcUserName
  | project SrcUserName, bucket, FailedLogins, AccountCustomEntity
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
customDetails:
  FailedLogins: FailedLogins
  UserAccount: SrcUserName
  TimeBucket: bucket
version: 1.0.1
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 10;

Stage 1: source

SlackAudit

Stage 2: where

| where DvcAction in~ ('user_login_failed')

Stage 3: summarize

| summarize FailedLogins = count() by SrcUserName, bucket = bin(TimeGenerated, 5m)

Stage 4: where

| where FailedLogins > threshold

Stage 5: extend

| extend AccountCustomEntity = SrcUserName

Stage 6: project

| project SrcUserName, bucket, FailedLogins, AccountCustomEntity

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityproject
FailedLoginsproject
SrcUserNameproject
bucketproject

SlackAudit - User role changed to admin or owner

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

'This query detects Slack audit events where a user role is changed to admin or owner, indicating potential privilege escalation or persistence activity. It monitors role change actions in Slack audit logs and maps the affected user as the primary account entity for investigation.'

MITRE ATT&CK Coverage

Rule Body

id: be6c5fc9-2ac3-43e6-8fb0-cb139e04e43e
name: SlackAudit - User role changed to admin or owner
description: |
  'This query detects Slack audit events where a user role is changed to admin or owner, indicating potential privilege
  escalation or persistence activity. It monitors role change actions in Slack audit logs and maps the affected user as the
  primary account entity for investigation.'
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: SlackAuditAPI
    dataTypes:
      - SlackAudit_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1098
  - T1078
query: |
  SlackAudit
  | where DvcAction in~ ('role_change_to_admin', 'role_change_to_owner')
  | extend AccountCustomEntity = SrcUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
alertDetailsOverride:
  alertDisplayNameFormat: Slack user role changed to {{AccountCustomEntity}}
  alertDescriptionFormat: Slack role change action {{DvcAction}} affected user {{AccountCustomEntity}}
customDetails:
  AffectedUser: AccountCustomEntity
  Action: DvcAction
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

SlackAudit

Stage 2: where

| where DvcAction in~ ('role_change_to_admin', 'role_change_to_owner')

Stage 3: extend

| extend AccountCustomEntity = SrcUserName

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DvcActionin
  • role_change_to_admin
  • role_change_to_owner
field:"DvcAction" kind:in

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend

Snowflake - Multiple login failures from single IP

#
Status
available
Severity
high
Time window
5m
Group by
SrcDvcIpAddr
Source
github.com/Azure/Azure-Sentinel

'Detects Mmltiple login failures from single IP.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: b7d22407-1391-4256-b09a-414a9719443c
name: Snowflake - Multiple login failures from single IP
description: |
  'Detects Mmltiple login failures from single IP.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: Snowflake
    dataTypes:
      - Snowflake
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |
  let threshold = 10;
  Snowflake
  | where EventType =~ 'LOGIN'
  | where LoginIsSuccess =~ 'No'
  | summarize count() by SrcDvcIpAddr, bin(TimeGenerated, 5m)
  | where count_ > threshold
  | extend IPCustomEntity = SrcDvcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 10;

Stage 1: source

Snowflake

Stage 2: where

| where EventType =~ 'LOGIN'

Stage 3: where

| where LoginIsSuccess =~ 'No'

Stage 4: summarize

| summarize count() by SrcDvcIpAddr, bin(TimeGenerated, 5m)

Stage 5: where

| where count_ > threshold

Stage 6: extend

| extend IPCustomEntity = SrcDvcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
SrcDvcIpAddrsummarize
IPCustomEntityextend

TI Map URL Entity to Syslog Data

#
Severity
medium
Time window
14d
Group by
IndicatorId, Url
Source
github.com/Azure/Azure-Sentinel

'This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in Syslog data.'

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: b31037ea-6f68-4fbd-bab2-d0d0f44c2fcf
name: TI Map URL Entity to Syslog Data
description: |
  'This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in Syslog data.'
severity: Medium
requiredDataConnectors:
  - connectorId: Syslog
    dataTypes:
      - Syslog
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let dt_lookBack = 1h;
  let ioc_lookBack = 14d;
  ThreatIntelligenceIndicator
  // Picking up only IOC's that contain the entities we want
  | where isnotempty(Url)
  | where TimeGenerated >= ago(ioc_lookBack)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
  | where Active == true and ExpirationDateTime > now()
  // using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
  | join kind=innerunique (
    Syslog
    | where TimeGenerated >= ago(dt_lookBack)
    // Extract URL from the Syslog message but only take messages that include URLs
    | extend Url = extract("(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)", 1,SyslogMessage)
    | where isnotempty(Url)
    | extend Syslog_TimeGenerated = TimeGenerated
  ) on Url
  | where Syslog_TimeGenerated < ExpirationDateTime
  | summarize Syslog_TimeGenerated  = arg_max(Syslog_TimeGenerated , *) by IndicatorId, Url
  | project timestamp = Syslog_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, SyslogMessage, Computer, ProcessName, Url, HostIP
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Computer
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: HostIP
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
version: 1.2.6
kind: Scheduled

Stages and Predicates

Parameters

let dt_lookBack = 1h;
let ioc_lookBack = 14d;

Stage 1: source

ThreatIntelligenceIndicator

Stage 2: where

| where isnotempty(Url)

Stage 3: where

| where TimeGenerated >= ago(ioc_lookBack)

Stage 4: summarize

| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId

Stage 5: where

| where Active == true and ExpirationDateTime > now()

Stage 6: join

| join kind=innerunique (
  Syslog
  | where TimeGenerated >= ago(dt_lookBack)
  | extend Url = extract("(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)", 1,SyslogMessage)
  | where isnotempty(Url)
  | extend Syslog_TimeGenerated = TimeGenerated
) on Url

Stage 7: where

where Syslog_TimeGenerated < ExpirationDateTime

Stage 8: summarize

summarize Syslog_TimeGenerated by IndicatorId, Url

Stage 9: project

project ActivityGroupNames, Computer, ConfidenceScore, Description, ExpirationDateTime, HostIP, IndicatorId, ProcessName, SyslogMessage, ThreatType, Url, timestamp

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Activeeq
  • true
field:"Active" kind:eq value:"true"
Urlis_not_null
  • (no value, null check)
field:"Url" kind:is_not_null

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActivityGroupNamesproject
Computerproject
ConfidenceScoreproject
Descriptionproject
ExpirationDateTimeproject
HostIPproject
IndicatorIdproject
ProcessNameproject
SyslogMessageproject
ThreatTypeproject
Urlproject
timestampproject

Back to all Kusto rules