Detection rules › Kusto
Kusto rules: 22
AWSCloudTrail - Suspicious AWS CLI Command Execution
#'This detection focuses on identifying potentially suspicious activities involving the execution of AWS Command Line Interface (CLI) commands, particularly focusing on reconnaissance operations.'
MITRE ATT&CK Coverage
Rule Body
id: 8c2dc344-9352-4ca1-8863-b1b7a5e09e59
name: AWSCloudTrail - Suspicious AWS CLI Command Execution
description: |
'This detection focuses on identifying potentially suspicious activities involving the execution of AWS Command Line Interface (CLI) commands, particularly focusing on reconnaissance operations.'
severity: Medium
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Reconnaissance
relevantTechniques:
- T1595
- T1592.004
- T1589.002
- T1589.003
- T1590
- T1591
- T1596
query: |
let SuspiciousCommands= pack_array('iam.list-users', 'iam.list-groups', 'ec2.describe-vpcs', 'ec2.describe-subnets', 'route53.list-hosted-zones', 'kms.list-keys', 'kms.list-aliases', 'ecs.list-clusters', 'ecs.list-services', 'iam.list-roles', 'iam.get-user''iam.list-access-keys', 'ec2.describe-security-groups', 'ec2.describe-network-acls', 'ec2.describe-network-interfaces', 'ec2.describe-route-tables', 'ec2.describe-internet-gateways', 'ec2.describe-vpc-peering-connections', 'ec2.describe-network-interfaces', 'ec2.describe-network-interfaces', 'ec2.describe-transit-gateway-vpc-attachment', 'ec2.describe-vpc');
// Retrieve AWS CloudTrail events
AWSCloudTrail
// Filter events with UserAgent starting with "aws-cli"
| where UserAgent startswith "aws-cli"
// Extract the command from the UserAgent using string splitting
| extend command = tostring(split(UserAgent, "off command/", 1)[0])
// Filter events based on predefined suspicious command list
| where command has_any (SuspiciousCommands)
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
// Summarize relevant information for further analysis
| summarize
CommadCount = dcount(command),
EventCount = dcount(EventName),
commands = make_list(command),
Events = make_list(EventName)
by
bin(TimeGenerated, 1min),
RecipientAccountId, AccountName, AccountUPNSuffix,
UserIdentityUserName,
SourceIpAddress,
SessionMfaAuthenticated
// Filter out results with a sufficient count of unique suspicious commands in 1 min time window
| where CommadCount >= 8
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifier: CloudAppAccountId
columnName: RecipientAccountId
customDetails:
SuspiciousCommand: commands
AWSUser: UserIdentityUserName
AWSUserIp: SourceIpAddress
alertDetailsOverride:
alertDisplayNameFormat: Suspicious AWS CLI reconnaissance by {{AccountName}} - {{CommadCount}} commands
alertDescriptionFormat: User {{AccountName}} executed {{CommadCount}} suspicious AWS CLI reconnaissance commands from {{SourceIpAddress}} within 1 minute window.
kind: Scheduled
version: 1.0.2
Stages and Predicates
Let Binding: SuspiciousCommands
let SuspiciousCommands = pack_array('iam.list-users', 'iam.list-groups', 'ec2.describe-vpcs', 'ec2.describe-subnets', 'route53.list-hosted-zones', 'kms.list-keys', 'kms.list-aliases', 'ecs.list-clusters', 'ecs.list-services', 'iam.list-roles', 'iam.get-user''iam.list-access-keys', 'ec2.describe-security-groups', 'ec2.describe-network-acls', 'ec2.describe-network-interfaces', 'ec2.describe-route-tables', 'ec2.describe-internet-gateways', 'ec2.describe-vpc-peering-connections', 'ec2.describe-network-interfaces', 'ec2.describe-network-interfaces', 'ec2.describe-transit-gateway-vpc-attachment', 'ec2.describe-vpc');
Stage 1: source
AWSCloudTrail
Stage 2: where
| where UserAgent startswith "aws-cli"
Stage 3: extend
| extend command = tostring(split(UserAgent, "off command/", 1)[0])
Stage 4: where
| where command has_any (SuspiciousCommands)
Stage 5: extend (4 consecutive steps)
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
Stage 6: summarize
| summarize
CommadCount = dcount(command),
EventCount = dcount(EventName),
commands = make_list(command),
Events = make_list(EventName)
by
bin(TimeGenerated, 1min),
RecipientAccountId, AccountName, AccountUPNSuffix,
UserIdentityUserName,
SourceIpAddress,
SessionMfaAuthenticated
Stage 7: where
| where CommadCount >= 8
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CommadCount | ge |
| field:"CommadCount" kind:ge value:"8" |
UserAgent | starts_with |
| field:"aws::userAgent" kind:starts_with value:"aws-cli" |
command | match |
| field:"command" kind:match value:"SuspiciousCommands" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | summarize |
AccountUPNSuffix | summarize |
CommadCount | summarize |
EventCount | summarize |
Events | summarize |
RecipientAccountId | summarize |
SessionMfaAuthenticated | summarize |
SourceIpAddress | summarize |
UserIdentityUserName | summarize |
commands | summarize |
BloodHound Attack Path Finding - ForceChangePassword Privileges on Tier Zero Objects
#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.
BloodHound Attack Path Finding - ForceChangePassword Privileges on Tier Zero Objects
Rule Body
id: e426256f-eb14-4b05-9aad-f630944e7012
name: BloodHound Attack Path Finding - ForceChangePassword Privileges on Tier Zero
Objects
description: BloodHound Attack Path Finding - ForceChangePassword Privileges on Tier
Zero Objects
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BloodHoundEnterprise
dataTypes:
- BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: "BHEAttackPathsData_CL \n| where Finding == \"T0ForceChangePassword\""
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: domain_name
customDetails:
Description: ShortDescription
Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
BHEAttackPathsData_CL
Stage 2: where
| where Finding == "T0ForceChangePassword"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"T0ForceChangePassword" |
BloodHound Attack Path Finding - Large Default Groups With Add Key Credential Link Privileges
#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.
BloodHound Attack Path Finding - Large Default Groups With Add Key Credential Link Privileges
Rule Body
id: 2f65df97-23a1-403b-93ea-6e478ee75c5a
name: BloodHound Attack Path Finding - Large Default Groups With Add Key Credential
Link Privileges
description: BloodHound Attack Path Finding - Large Default Groups With Add Key Credential
Link Privileges
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BloodHoundEnterprise
dataTypes:
- BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "LargeDefaultGroupsAddKeyCredentialLink"
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: domain_name
customDetails:
Description: ShortDescription
Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
BHEAttackPathsData_CL
Stage 2: where
| where Finding == "LargeDefaultGroupsAddKeyCredentialLink"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"LargeDefaultGroupsAddKeyCredentialLink" |
BloodHound Attack Path Finding - Non-Tier Zero Principals With DCSync Privileges
#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.
BloodHound Attack Path Finding - Non-Tier Zero Principals With DCSync Privileges
Rule Body
id: d6bf4db8-f2f3-4738-9051-bb892b268da3
name: BloodHound Attack Path Finding - Non-Tier Zero Principals With DCSync Privileges
description: BloodHound Attack Path Finding - Non-Tier Zero Principals With DCSync
Privileges
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BloodHoundEnterprise
dataTypes:
- BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: "BHEAttackPathsData_CL \n| where Finding == \"NonT0DCSyncers\""
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: domain_name
customDetails:
Description: ShortDescription
Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
BHEAttackPathsData_CL
Stage 2: where
| where Finding == "NonT0DCSyncers"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"NonT0DCSyncers" |
CiscoISE - Certificate has expired
#'Detects certificate expiration.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Rule Body
id: 6107cba5-2974-4c22-8222-2a6f7bbea664
name: CiscoISE - Certificate has expired
description: |
'Detects certificate expiration.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1552
query: |
let lbtime = 1h;
CiscoISEEvent
| where TimeGenerated > ago(lbtime)
| where EventId == '60167'
| project SrcIpAddr, DvcIpAddr, DvcHostname, DstUserName
| extend HostCustomEntity = DvcHostname
| extend IPCustomEntity = SrcIpAddr
| extend AccountCustomEntity = DstUserName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: HostCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Parameters
let lbtime = 1h;
Stage 1: source
CiscoISEEvent
Stage 2: where
| where TimeGenerated > ago(lbtime)
Stage 3: where
| where EventId == '60167'
Stage 4: project
| project SrcIpAddr, DvcIpAddr, DvcHostname, DstUserName
Stage 5: extend (3 consecutive steps)
| extend HostCustomEntity = DvcHostname
| extend IPCustomEntity = SrcIpAddr
| extend AccountCustomEntity = DstUserName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventId | eq |
| field:"EventId" kind:eq value:"60167" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstUserName | project |
DvcHostname | project |
DvcIpAddr | project |
SrcIpAddr | project |
HostCustomEntity | extend |
IPCustomEntity | extend |
AccountCustomEntity | extend |
CyberArkEPM - MSBuild usage as LOLBin
#'Detects usage of msbuild tool as LOLBin.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Rule Body
id: a11bf869-458e-49fd-be03-58021b14be15
name: CyberArkEPM - MSBuild usage as LOLBin
description: |
'Detects usage of msbuild tool as LOLBin.'
severity: Medium
requiredDataConnectors:
- connectorId: CyberArkEPM
dataTypes:
- CyberArkEPM
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1127
query: |
CyberArkEPM
| where EventSubType != 'AttackAttempt'
| where ActingProcessName endswith @'msbuild.exe'
| where ActingProcessCommandLine has_any ('xml', 'csproj', 'vcxproj', 'vbproj', 'fsproj')
| extend AccountCustomEntity = ActorUsername
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
CyberArkEPM
Stage 2: where
| where EventSubType != 'AttackAttempt'
Stage 3: where
| where ActingProcessName endswith @'msbuild.exe'
Stage 4: where
| where ActingProcessCommandLine has_any ('xml', 'csproj', 'vcxproj', 'vbproj', 'fsproj')
Stage 5: extend
| extend AccountCustomEntity = ActorUsername
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActingProcessCommandLine | match |
| field:"ParentCommandLine" kind:match |
ActingProcessName | ends_with |
| field:"ParentImage" kind:ends_with value:"msbuild.exe" |
EventSubType | ne |
| field:"EventSubType" kind:ne value:"AttackAttempt" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
CyberBlindSpot - Any Issue Detected
#Generic alert that triggers when ANY CyberBlindSpot issue/incident is detected in the logs. Extracts nested metadata from RawPayload.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | |
| Initial Access |
Rule Body
id: abe1a662-d00d-482e-aa68-9394622ae02e
name: CyberBlindSpot - Any Issue Detected
description: Generic alert that triggers when ANY CyberBlindSpot issue/incident is detected in the logs. Extracts nested metadata from RawPayload.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: CTM360CBSConnectorDefinition
dataTypes:
- CBS_MalwareLogs_AzureV2_CL
- CBS_BreachedCredentials_AzureV2_CL
- CBS_CompromisedCards_AzureV2_CL
- CBS_DomainInfringement_AzureV2_CL
- CBS_SubdomainInfringement_AzureV2_CL
- CBSLog_AzureV2_CL
tactics:
- Reconnaissance
- Discovery
- ResourceDevelopment
- InitialAccess
relevantTechniques:
- T1592
- T1598
- T1566
query: |
CBSLog
| project TimeGenerated, IncidentId, Subject, Severity, IncidentType, Status, Class, ExternalLink, Brand, FirstSeen, LastSeen, Remarks
eventGroupingSettings:
aggregationKind: AlertPerResult
customDetails:
Subject: Subject
IncidentType: IncidentType
Brand: Brand
alertDetailsOverride:
alertDisplayNameFormat: CyberBlindSpot Alert - {{IncidentType}} on {{Subject}} - {{Brand}}
alertDescriptionFormat: Type {{IncidentType}} | Subject {{Subject}} | Severity {{Severity}}
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Subject
kind: Scheduled
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: PT5H
suppressionEnabled: false
version: 1.0.0
Stages and Predicates
Stage 1: source
CBSLog
Stage 2: project
| project TimeGenerated, IncidentId, Subject, Severity, IncidentType, Status, Class, ExternalLink, Brand, FirstSeen, LastSeen, Remarks
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Brand | project |
Class | project |
ExternalLink | project |
FirstSeen | project |
IncidentId | project |
IncidentType | project |
LastSeen | project |
Remarks | project |
Severity | project |
Status | project |
Subject | project |
TimeGenerated | project |
Cyble Vision Alerts Compromised Endpoint Cookies
#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.
'Detects compromised browser cookies associated with monitored entities. Identifies exposed authentication cookies with future expiry, enabling potential session hijacking or persistent unauthorized access.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Lateral Movement |
Rule Body
id: 33B07EAA-F451-4C38-AC9F-8AF3F7E99F0E
name: Cyble Vision Alerts Compromised Endpoint Cookies
description: |
'Detects compromised browser cookies associated with monitored entities. Identifies exposed authentication cookies with future expiry, enabling potential session hijacking or persistent unauthorized access.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
dataTypes:
- CybleVisionAlerts_CL
enabled: true
queryfrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: PT1H
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- CredentialAccess
- DefenseEvasion
relevantTechniques:
- T1539
- T1550
query: |
Alerts_compromised_endpoints_cookies
| where Service == "compromised_endpoints_cookies"
| extend MappedSeverity = Severity
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
alertDetailsOverride:
alertDisplayNameFormat: Compromised Session Cookie {{CEC_Email}} {{CEC_URL}}
alertDescriptionFormat: |
A compromised browser session cookie associated with {{CEC_EmailDomain}} was detected for service {{CEC_URL}}. The cookie has not expired and may allow session hijacking. Session ID {{CEC_SessionID}}.
customDetails:
MappedSeverity: Severity
Status: Status
AlertID: AlertID
Service: Service
CookieEmail: CEC_Email
CookieDomain: CEC_EmailDomain
CookieSessionID: CEC_SessionID
CookieExpiry: CEC_Expiry
CookieURL: CEC_URL
entityMappings:
- entityType: Url
fieldMappings:
- identifier: Url
columnName: CEC_URL
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: CEC_Email
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Alerts_compromised_endpoints_cookies
Stage 2: where
| where Service == "compromised_endpoints_cookies"
Stage 3: extend
| extend MappedSeverity = Severity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Service | eq |
| field:"Service" kind:eq value:"compromised_endpoints_cookies" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
MappedSeverity | extend |
Darktrace System Status (Legacy)
#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.
'This rule creates Microsoft Sentinel Alerts based on Darktrace system status alerts for health monitoring, fetched every 5 minutes.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery | |
| Impact |
Rule Body
id: 2e629769-60eb-4a14-8bfc-bde9be66ebeb
name: Darktrace System Status (Legacy)
description: |
'This rule creates Microsoft Sentinel Alerts based on Darktrace system status alerts for health monitoring, fetched every 5 minutes.'
severity: Informational
requiredDataConnectors:
- connectorId: DarktraceRESTConnector
dataTypes:
- darktrace_model_alerts_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
- Impact
relevantTechniques:
- T1082
- T1498
query: |
darktrace_model_alerts_CL //anything starting with 'Dt' is not an ASIM mapping
| where dtProduct_s =="System Alert"
| extend EventVendor="Darktrace", EventProduct="Darktrace DETECT"
| project-rename ThreatCategory=dtProduct_s, EventStartTime=time_s, NetworkRuleName=friendlyName_s, SrcIpAddr=deviceIP_s, SrcHostname=hostname_s, ThreatRiskLevel=priority_code_d, ThreatRiskCategory=priority_s, DtSeverity=Severity, DtName=name_s, DtStatus=status_s, DtMessage=Message, DtURL=url_s, DtUUID=uuid_g
eventGroupingSettings:
aggregationKind: AlertPerResult
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: SrcHostname
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
customDetails:
EventStartTime: EventStartTime
NetworkRuleName: NetworkRuleName
ThreatRiskLevel: ThreatRiskLevel
ThreatRiskCategory: ThreatRiskCategory
DtName: DtName
DtStatus: DtStatus
DtMessage: DtMessage
DtSeverity: DtSeverity
alertDetailsOverride:
alertDisplayNameFormat: 'Darktrace: {{ThreatRiskLevel}} - {{NetworkRuleName}}'
alertDescriptionFormat: '{{DtMessage}}'
alertTacticsColumnName: # none
alertSeverityColumnName: # none
alertDynamicProperties:
- alertProperty: AlertLink
value: DtURL
- alertProperty: ProviderName
value: EventVendor
- alertProperty: ProductName
value: EventProduct
- alertProperty: ProductComponentName
value: ThreatCategory
version: 1.1.1
kind: Scheduled
Stages and Predicates
Stage 1: source
darktrace_model_alerts_CL
Stage 2: where
| where dtProduct_s =="System Alert"
Stage 3: extend
| extend EventVendor="Darktrace", EventProduct="Darktrace DETECT"
Stage 4: project-rename
| project-rename ThreatCategory=dtProduct_s, EventStartTime=time_s, NetworkRuleName=friendlyName_s, SrcIpAddr=deviceIP_s, SrcHostname=hostname_s, ThreatRiskLevel=priority_code_d, ThreatRiskCategory=priority_s, DtSeverity=Severity, DtName=name_s, DtStatus=status_s, DtMessage=Message, DtURL=url_s, DtUUID=uuid_g
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
dtProduct_s | eq |
| field:"dtProduct_s" kind:eq value:"System Alert" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EventProduct | extend |
EventVendor | extend |
DtMessage | project-rename |
DtName | project-rename |
DtSeverity | project-rename |
DtStatus | project-rename |
DtURL | project-rename |
DtUUID | project-rename |
EventStartTime | project-rename |
NetworkRuleName | project-rename |
SrcHostname | project-rename |
SrcIpAddr | project-rename |
ThreatCategory | project-rename |
ThreatRiskCategory | project-rename |
ThreatRiskLevel | project-rename |
Deleted a Custom Field Mapping profile
#'Deleted a Custom Field Mapping profile from SOC Prime platform'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Rule Body
id: a1b2c3d4-e5f6-4078-9012-3456789abcde
name: Deleted a Custom Field Mapping profile
description: |
'Deleted a Custom Field Mapping profile from SOC Prime platform'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SOCPrimeAuditLogsDataConnector
dataTypes:
- SOCPrimeAuditLogs_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1562.001
query: |
SOCPrimeAuditLogs_CL
| where EventName == "Deleted a Custom Field Mapping profile"
| project TimeGenerated, EventName, UserEmail, UserName, SourceIp, Uri, Type
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: UserName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIp
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
SOCPrimeAuditLogs_CL
Stage 2: where
| where EventName == "Deleted a Custom Field Mapping profile"
Stage 3: project
| project TimeGenerated, EventName, UserEmail, UserName, SourceIp, Uri, Type
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventName | eq |
| field:"aws::eventName" kind:eq value:"Deleted a Custom Field Mapping profile" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EventName | project |
SourceIp | project |
TimeGenerated | project |
Type | project |
Uri | project |
UserEmail | project |
UserName | project |
Exchange AuditLog Disabled
#Identifies when the exchange audit logging has been disabled which may be an adversary attempt to evade detection or avoid other defenses.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft 365 | Exchange audit operation Set-AdminAuditLogConfig: Federated domain added |
Rule Body
id: 194dd92e-d6e7-4249-85a5-273350a7f5ce
name: Exchange AuditLog Disabled
description: |
'Identifies when the exchange audit logging has been disabled which may be an adversary attempt to evade detection or avoid other defenses.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Office365
dataTypes:
- OfficeActivity (Exchange)
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1562
query: |
OfficeActivity
| where OfficeWorkload =~ "Exchange"
| where UserType in~ ("Admin","DcAdmin")
// Only admin or global-admin can disable audit logging
| where Operation =~ "Set-AdminAuditLogConfig"
| extend AdminAuditLogEnabledValue = tostring(parse_json(tostring(parse_json(tostring(array_slice(parse_json(Parameters),3,3)))[0])).Value)
| where AdminAuditLogEnabledValue =~ "False"
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP, ResultStatus, Parameters, AdminAuditLogEnabledValue
| extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId)
| extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '')
| extend AccountName = iff(UserId contains '\\', tostring(split(UserId, '\\')[1]), AccountName)
| extend AccountNTDomain = iff(UserId contains '\\', tostring(split(UserId, '\\')[0]), '')
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserId
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountNTDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIP
version: 2.0.6
kind: Scheduled
Stages and Predicates
Stage 1: source
OfficeActivity
Stage 2: where
| where OfficeWorkload =~ "Exchange"
Stage 3: where
| where UserType in~ ("Admin","DcAdmin")
Stage 4: where
| where Operation =~ "Set-AdminAuditLogConfig"
Stage 5: extend
| extend AdminAuditLogEnabledValue = tostring(parse_json(tostring(parse_json(tostring(array_slice(parse_json(Parameters),3,3)))[0])).Value)
Stage 6: where
| where AdminAuditLogEnabledValue =~ "False"
Stage 7: summarize
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP, ResultStatus, Parameters, AdminAuditLogEnabledValue
Stage 8: extend (4 consecutive steps)
| extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId)
| extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '')
| extend AccountName = iff(UserId contains '\\', tostring(split(UserId, '\\')[1]), AccountName)
| extend AccountNTDomain = iff(UserId contains '\\', tostring(split(UserId, '\\')[0]), '')
AccountName =UserId contains "@"tostring(split(UserId, '@')[0])UserIdIndicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AdminAuditLogEnabledValue | eq |
| field:"AdminAuditLogEnabledValue" kind:eq value:"False" |
OfficeWorkload | eq |
| field:"m365::Workload" kind:eq value:"Exchange" |
Operation | eq |
| field:"Operation" kind:eq value:"Set-AdminAuditLogConfig" |
UserType | in |
| field:"UserType" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AdminAuditLogEnabledValue | summarize |
ClientIP | summarize |
EndTimeUtc | summarize |
Operation | summarize |
OperationCount | summarize |
Parameters | summarize |
ResultStatus | summarize |
StartTimeUtc | summarize |
UserId | summarize |
UserType | summarize |
AccountName | extend |
AccountUPNSuffix | extend |
AccountNTDomain | extend |
GreyNoise TI Map IP Entity to DnsEvents
#This query maps any IP indicators of compromise (IOCs) from GreyNoise Threat Intelligence (TI), by searching for matches in DnsEvents.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: ddf47b6f-870c-5712-a296-1383acb13c82
name: GreyNoise TI Map IP Entity to DnsEvents
version: 1.0.2
kind: Scheduled
description: |
This query maps any IP indicators of compromise (IOCs) from GreyNoise Threat Intelligence (TI), by searching for matches in DnsEvents.
severity: Medium
requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: DNS
dataTypes:
- DnsEvents
- connectorId: ASimDnsActivityLogs
dataTypes:
- DnsEvents
- connectorId: GreyNoise2SentinelAPI
dataTypes:
- ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h; // Look back 1 hour for DNS events
let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
// Fetch threat intelligence indicators related to IP addresses
let IP_Indicators = ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
| where SourceSystem == 'GreyNoise'
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127.";
// Perform a join between IP indicators and DNS events
IP_Indicators
// Use innerunique to keep performance fast and result set low, as we only need one match to indicate potential malicious activity that needs investigation
| join kind=innerunique (
DnsEvents
| where TimeGenerated >= ago(dt_lookBack)
| where SubType =~ "LookupQuery" and isnotempty(IPAddresses)
| mv-expand SingleIP = split(IPAddresses, ", ") to typeof(string)
| extend DNS_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.SingleIP
// Filter out DNS events that occurred after the expiration of the corresponding indicator
| where DNS_TimeGenerated < ExpirationDateTime
// Group the results by IndicatorId and SingleIP, and keep the DNS event with the latest timestamp
| summarize DNS_TimeGenerated = arg_max(DNS_TimeGenerated, *) by IndicatorId, SingleIP
// Select the desired output fields
| project DNS_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, DomainName, ExpirationDateTime, ConfidenceScore,
TI_ipEntity, Computer, EventId, SubType, ClientIP, Name, IPAddresses, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress, Type
| extend timestamp = DNS_TimeGenerated, HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIP
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_Indicators is inlined into the numbered stages below.
Stages 1 to 8 define let IP_Indicators as the rule's main pipeline source. Stages 9 to 13 use it.
Stage 1: source
ThreatIntelligenceIndicator
Stage 2: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 3: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
Stage 4: where
| where Active == true and ExpirationDateTime > now()
Stage 5: where
| where SourceSystem == 'GreyNoise'
Stage 6: where
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
Stage 7: extend (3 consecutive steps)
| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
TI_ipEntity =isnotempty(NetworkIP)NetworkIPNetworkDestinationIPStage 8: where
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
Stage 9: join
IP_Indicators
| join kind=innerunique (
DnsEvents
| where TimeGenerated >= ago(dt_lookBack)
| where SubType =~ "LookupQuery" and isnotempty(IPAddresses)
| mv-expand SingleIP = split(IPAddresses, ", ") to typeof(string)
| extend DNS_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.SingleIP
Stage 10: where
where DNS_TimeGenerated < ExpirationDateTime
Stage 11: summarize
summarize DNS_TimeGenerated by IndicatorId, SingleIP
Stage 12: project
project ActivityGroupNames, ClientIP, Computer, ConfidenceScore, DNS_TimeGenerated, Description, DomainName, EmailSourceIpAddress, EventId, ExpirationDateTime, IPAddresses, IndicatorId, Name, NetworkDestinationIP, NetworkIP, NetworkSourceIP, SubType, TI_ipEntity, ThreatType, Type, Url
Stage 13: extend
extend DnsDomain, HostName, timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Active | eq |
| field:"Active" kind:eq value:"true" |
EmailSourceIpAddress | is_not_null | field:"EmailSourceIpAddress" kind:is_not_null | |
IPAddresses | is_not_null | field:"IPAddresses" kind:is_not_null | |
NetworkDestinationIP | is_not_null | field:"NetworkDestinationIP" kind:is_not_null | |
NetworkIP | is_not_null | field:"NetworkIP" kind:is_not_null | |
NetworkSourceIP | is_not_null | field:"NetworkSourceIP" kind:is_not_null | |
SourceSystem | eq |
| field:"SourceSystem" kind:eq value:"GreyNoise" |
SubType | eq |
| field:"SubType" kind:eq value:"LookupQuery" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
TI_ipEntity | cidr_match | 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8 | excludes:TI_ipEntity |
TI_ipEntity | starts_with | 127. | excludes:TI_ipEntity field:"TI_ipEntity" value:"127." |
TI_ipEntity | starts_with | :: | excludes:TI_ipEntity field:"TI_ipEntity" value:"::" |
TI_ipEntity | starts_with | fe80 | excludes:TI_ipEntity field:"TI_ipEntity" value:"fe80" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityGroupNames | project |
ClientIP | project |
Computer | project |
ConfidenceScore | project |
DNS_TimeGenerated | project |
Description | project |
DomainName | project |
EmailSourceIpAddress | project |
EventId | project |
ExpirationDateTime | project |
IPAddresses | project |
IndicatorId | project |
Name | project |
NetworkDestinationIP | project |
NetworkIP | project |
NetworkSourceIP | project |
SubType | project |
TI_ipEntity | project |
ThreatType | project |
Type | project |
Url | project |
DnsDomain | extend |
HostName | extend |
timestamp | extend |
McAfee ePO - Firewall disabled
#Detects when firewall was disabled from Mctray.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment | |
| Command & Control |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| McAfee-ePolicy-Orchestrator | Event ID 35009: Endpoint firewall disabled |
Rule Body
id: bd3cedc3-efba-455a-85bd-0cf9ac1b0727
name: McAfee ePO - Firewall disabled
description: |
'Detects when firewall was disabled from Mctray.'
severity: Medium
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- DefenseEvasion
- CommandAndControl
relevantTechniques:
- T1562
- T1071
query: |
McAfeeEPOEvent
| where EventId in ('35009')
| extend IPCustomEntity = DvcIpAddr, HostCustomEntity = DvcHostname
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: HostCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
McAfeeEPOEvent
Stage 2: where
| where EventId in ('35009')
Stage 3: extend
| extend IPCustomEntity = DvcIpAddr, HostCustomEntity = DvcHostname
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventId | in |
| field:"EventId" kind:in value:"35009" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HostCustomEntity | extend |
IPCustomEntity | extend |
Mimecast Targeted Threat Protection - Impersonation Protect
#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.
Detects a maliciously tagged impersonation
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Rule Body
id: d8e7eca6-4b59-4069-a31e-a022b2a12ea4
name: Mimecast Targeted Threat Protection - Impersonation Protect
description: Detects a maliciously tagged impersonation
severity: High
requiredDataConnectors:
- connectorId: MimecastTTPAPI
dataTypes:
- MimecastTTPImpersonation_CL
enabled: true
query: MimecastTTPImpersonation_CL| where taggedMalicious_b == true;
queryFrequency: 5m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
suppressionDuration: 5h
suppressionEnabled: false
tactics:
- Exfiltration
- Collection
- Discovery
relevantTechniques:
- T1114
alertRuleTemplateName:
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1d
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
entityMappings:
- entityType: MailMessage
fieldMappings:
- identifier: Sender
columnName: senderAddress_s
- identifier: SenderIP
columnName: senderIpAddress_s
- identifier: Recipient
columnName: recipientAddress_s
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
MimecastTTPImpersonation_CL
Stage 2: where
| where taggedMalicious_b == true;
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
taggedMalicious_b | eq |
| field:"taggedMalicious_b" kind:eq value:"true" |
NGINX - Multiple server errors from single IP address
#'Detects multiple server errors from one source in short timeframe'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Impact |
Rule Body
id: b3ae0033-552e-4c3c-b493-3edffb4473bb
name: NGINX - Multiple server errors from single IP address
description: |
'Detects multiple server errors from one source in short timeframe'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CustomLogsAma
dataTypes:
- NGINX_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
- InitialAccess
relevantTechniques:
- T1498
- T1190
- T1133
query: |
let threshold = 100;
NGINXHTTPServer
| where tolong(HttpStatusCode) >= 500 and tolong(HttpStatusCode) <= 599
| summarize MultipleServerErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)
| where MultipleServerErrors > threshold
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 100;
Stage 1: source
NGINXHTTPServer
Stage 2: where
| where tolong(HttpStatusCode) >= 500 and tolong(HttpStatusCode) <= 599
Stage 3: summarize
| summarize MultipleServerErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)
Stage 4: where
| where MultipleServerErrors > threshold
Stage 5: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
MultipleServerErrors | summarize |
SrcIpAddr | summarize |
IPCustomEntity | extend |
NordPass - Declined invitation
#This will alert you when the user declines the invite to the NordPass organization.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Rule Body
id: 283d7506-f3c6-419a-ae9c-d9afe6a15d6d
name: NordPass - Declined invitation
version: 1.0.0
kind: Scheduled
displayName: Declined invitation
description: This will alert you when the user declines the invite to the NordPass organization.
severity: Low
query: |
NordPassEventLogs_CL
| where event_type == "invites"
| where action == "user_invite_declined"
| extend TargetEmail = user_email
requiredDataConnectors:
- connectorId: NordPass
dataTypes:
- NordPassEventLogs_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1078
incidentConfiguration:
createIncident: false
entityMappings:
- entityType: Mailbox
fieldMappings:
- identifier: MailboxPrimaryAddress
columnName: TargetEmail
Stages and Predicates
Stage 1: source
NordPassEventLogs_CL
Stage 2: where
| where event_type == "invites"
Stage 3: where
| where action == "user_invite_declined"
Stage 4: extend
| extend TargetEmail = user_email
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"user_invite_declined" |
event_type | eq |
| field:"event_type" kind:eq value:"invites" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
TargetEmail | extend |
Ping Federate - Authentication from new IP.
#'Detects authentication requests from new IP address.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 30583ed4-d13c-43b8-baf2-d75fbe727210
name: Ping Federate - Authentication from new IP.
description: |
'Detects authentication requests from new IP address.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
let known_src =
PingFederateEvent
| where TimeGenerated between (ago(14d) .. (1d))
| where EventType in~ ('AUTHN_ATTEMPT', 'SSO')
| where isnotempty(SrcIpAddr)
| summarize makeset(SrcIpAddr);
PingFederateEvent
| where EventType in~ ('AUTHN_ATTEMPT', 'SSO')
| where isnotempty(SrcIpAddr)
| where SrcIpAddr !in (known_src)
| extend AccountCustomEntity = DstUserName
| extend IpCustomEntity = SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IpCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Let Binding: known_src
let known_src = PingFederateEvent
| where TimeGenerated between (ago(14d) .. (1d))
| where EventType in~ ('AUTHN_ATTEMPT', 'SSO')
| where isnotempty(SrcIpAddr)
| summarize makeset(SrcIpAddr);
Stage 1: source
PingFederateEvent
Stage 2: where
| where EventType in~ ('AUTHN_ATTEMPT', 'SSO')
Stage 3: where
| where isnotempty(SrcIpAddr)
Stage 4: where
| where SrcIpAddr !in (known_src)
Stage 5: extend
| extend AccountCustomEntity = DstUserName
Stage 6: extend
| extend IpCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | in |
| field:"EventType" kind:in |
SrcIpAddr | is_not_null | field:"src_ip" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
SrcIpAddr | eq | known_src | excludes:SrcIpAddr field:"SrcIpAddr" value:"known_src" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
IpCustomEntity | extend |
Port Sweep
#'Identifies a source IP scanning same open ports on the Azure Firewall IPs. This can indicate malicious scanning of port by an attacker, trying to reveal IPs with specific ports open in the organization. The ports can be compromised by attackers for initial access, most often by exploiting vulnerability. Configurable Parameters: - Port sweep time - the time range to look for multiple hosts scanned. Default is set to 30 seconds. - Minimum different hosts threshold - alert only if more than this number of hosts scanned. Default is set to 200.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | |
| Discovery |
Rule Body
id: 720335f4-ee8c-4270-9424-d0859222168c
name: Port Sweep
description: |
'Identifies a source IP scanning same open ports on the Azure Firewall IPs. This can indicate malicious scanning of port by an attacker, trying to reveal IPs with specific ports open in the organization. The ports can be compromised by attackers for initial access, most often by exploiting vulnerability.
Configurable Parameters:
- Port sweep time - the time range to look for multiple hosts scanned. Default is set to 30 seconds.
- Minimum different hosts threshold - alert only if more than this number of hosts scanned. Default is set to 200.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AzureFirewall
dataTypes:
- AzureDiagnostics
- AZFWApplicationRule
- AZFWNetworkRule
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
- Reconnaissance
relevantTechniques:
- T1046
- T1595.001
query: |
let MinimumDifferentHostsThreshold = 200;
let ExcludedPorts = dynamic([80 , 443]);
let BinTime = 30s;
union isfuzzy=true(
AZFWApplicationRule
| where DestinationPort !in (ExcludedPorts)
| summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
| where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
| mv-expand Fqdn = AlertTimedCountHostsInBinTime),
(AZFWNetworkRule
| extend Fqdn = DestinationIp
| where DestinationPort !in (ExcludedPorts)
| summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
| where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
| mv-expand Fqdn = AlertTimedCountHostsInBinTime),
(AzureDiagnostics
| where OperationName == "AzureFirewallApplicationRuleLog" or OperationName == "AzureFirewallNetworkRuleLog"
| parse msg_s with * "from " SourceIp ":" SourcePort:int " to " Fqdn ":" DestinationPort:int ". " * "Action: " Action "." *
| where DestinationPort !in (ExcludedPorts)
| where isnotempty(Fqdn) and isnotempty(SourceIp) and isnotempty(DestinationPort)
| summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
| where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
| mv-expand Fqdn = AlertTimedCountHostsInBinTime)
| project bin(TimeGenerated, BinTime), SourceIp, DestinationPort, AlertTimedCountHostsInBinTime, Fqdn
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIp
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Fqdn
customDetails:
DestinationPort: DestinationPort
alertDetailsOverride:
alertDisplayNameFormat: 'Port Sweep Detected from {{SourceIp}} on Port {{DestinationPort}}'
alertDescriptionFormat: 'Source IP {{SourceIp}} has scanned {{AlertTimedCountHostsInBinTime}} different hosts on port {{DestinationPort}} within a short time frame, which may indicate reconnaissance activity.'
version: 1.2.3
kind: Scheduled
Stages and Predicates
Parameters
let MinimumDifferentHostsThreshold = 200;
let ExcludedPorts = dynamic([80 , 443]);
let BinTime = 30s;
Union isfuzzy=true (3 Sources)
Each leg below queries one source; the rule matches if any leg does. Sources: AZFWApplicationRule, AZFWNetworkRule, AzureDiagnostics
Leg 1: AZFWApplicationRule
AZFWApplicationRule
| where DestinationPort !in (ExcludedPorts)
| summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
| where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
| mv-expand Fqdn = AlertTimedCountHostsInBinTime
Leg 2: AZFWNetworkRule
AZFWNetworkRule
| extend Fqdn = DestinationIp
| where DestinationPort !in (ExcludedPorts)
| summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
| where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
| mv-expand Fqdn = AlertTimedCountHostsInBinTime
Leg 3: AzureDiagnostics
AzureDiagnostics
| where OperationName == "AzureFirewallApplicationRuleLog" or OperationName == "AzureFirewallNetworkRuleLog"
| parse msg_s with * "from " SourceIp ":" SourcePort:int " to " Fqdn ":" DestinationPort:int ". " * "Action: " Action "." *
| where DestinationPort !in (ExcludedPorts)
| where isnotempty(Fqdn) and isnotempty(SourceIp) and isnotempty(DestinationPort)
| summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
| where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
| mv-expand Fqdn = AlertTimedCountHostsInBinTime
Applied to the Combined Result
| project bin(TimeGenerated, BinTime), SourceIp, DestinationPort, AlertTimedCountHostsInBinTime, Fqdn
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DestinationPort | is_not_null | field:"DestinationPort" kind:is_not_null | |
Fqdn | is_not_null | field:"Fqdn" kind:is_not_null | |
OperationName | eq |
| field:"OperationName" kind:eq |
SourceIp | is_not_null | field:"src_ip" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
DestinationPort | in | 443, 80 | excludes:DestinationPort field:"DestinationPort" value:"443" field:"DestinationPort" value:"80" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AlertTimedCountHostsInBinTime | project |
DestinationPort | project |
Fqdn | project |
SourceIp | project |
Potential re-named sdelete usage (ASIM Version)
#This detection looks for command line parameters associated with the use of Sysinternals sdelete (https://docs.microsoft.com/sysinternals/downloads/sdelete) to delete multiple files on a host's C drive. A threat actor may re-name the tool to avoid detection and then use it for destructive attacks on a host. This detection uses the ASIM imProcess parser, this will need to be deployed before use - https://docs.microsoft.com/azure/sentinel/normalization
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth | |
| Impact |
Telemetry Coverage
Rule Body
id: 5b6ae038-f66e-4f74-9315-df52fd492be4
name: Potential re-named sdelete usage (ASIM Version)
description: |
'This detection looks for command line parameters associated with the use of Sysinternals sdelete (https://docs.microsoft.com/sysinternals/downloads/sdelete) to delete multiple files on a host's C drive.
A threat actor may re-name the tool to avoid detection and then use it for destructive attacks on a host.
This detection uses the ASIM imProcess parser, this will need to be deployed before use - https://docs.microsoft.com/azure/sentinel/normalization'
severity: Low
requiredDataConnectors: []
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
- Impact
relevantTechniques:
- T1485
- T1036
query: |
imProcess
| where CommandLine has_all ("accepteula", "-s", "-r", "-q")
| where Process !endswith "sdelete.exe"
| where CommandLine !has "sdelete"
| extend AccountName = tostring(split(ActorUsername, @'\')[1]), AccountNTDomain = tostring(split(ActorUsername, @'\')[0])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: ActorUsername
- identifier: Name
columnName: AccountName
- identifier: NTDomain
columnName: AccountNTDomain
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Dvc
- identifier: HostName
columnName: DvcHostname
- identifier: DnsDomain
columnName: DvcDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: DvcIpAddr
version: 1.0.6
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Threat Protection" ]
Stages and Predicates
Stage 1: source
imProcess
Stage 2: where
| where CommandLine has_all ("accepteula", "-s", "-r", "-q")
Stage 3: where
| where Process !endswith "sdelete.exe"
Stage 4: where
| where CommandLine !has "sdelete"
Stage 5: extend
| extend AccountName = tostring(split(ActorUsername, @'\')[1]), AccountNTDomain = tostring(split(ActorUsername, @'\')[0])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CommandLine | match |
| field:"CommandLine" kind:match |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Process | ends_with | sdelete.exe | excludes:Process field:"Process" value:"sdelete.exe" |
CommandLine | match | sdelete | excludes:CommandLine field:"CommandLine" value:"sdelete" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountNTDomain | extend |
AccountName | extend |
Sentinel One - Admin login from new location
#'Detects admin user login from new location (IP address).'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Privilege Escalation |
Rule Body
id: 382f37b3-b49a-492f-b436-a4717c8c5c3e
name: Sentinel One - Admin login from new location
description: |
'Detects admin user login from new location (IP address).'
severity: High
status: Available
requiredDataConnectors:
- connectorId: SentinelOne
dataTypes:
- SentinelOne
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- PrivilegeEscalation
relevantTechniques:
- T1078
query: |
let lback_period = 14d;
let lback_time = 1h;
SentinelOne
| where TimeGenerated between(ago(lback_period)..ago(lback_time))
| where ActivityType == 27
| where DataRole =~ 'Admin'
| extend SrcIpAddr = extract(@'Address\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', 1, EventOriginalMessage)
| where isnotempty(SrcIpAddr)
| summarize ip_lst = makeset(SrcIpAddr) by SrcUserName
| join (SentinelOne
| where ActivityType == 27
| where DataRole =~ 'Admin'
| extend SrcIpAddr = extract(@'Address\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', 1, EventOriginalMessage)
| where isnotempty(SrcIpAddr)) on SrcUserName
| where ip_lst !has SrcIpAddr
| extend AccountCustomEntity = SrcUserName, IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.1
kind: Scheduled
Stages and Predicates
Parameters
let lback_period = 14d;
let lback_time = 1h;
Stage 1: source
SentinelOne
Stage 2: where
| where TimeGenerated between(ago(lback_period)..ago(lback_time))
Stage 3: where
| where ActivityType == 27
Stage 4: where
| where DataRole =~ 'Admin'
Stage 5: extend
| extend SrcIpAddr = extract(@'Address\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', 1, EventOriginalMessage)
Stage 6: where
| where isnotempty(SrcIpAddr)
Stage 7: summarize
| summarize ip_lst = makeset(SrcIpAddr) by SrcUserName
Stage 8: join
| join (SentinelOne
| where ActivityType == 27
| where DataRole =~ 'Admin'
| extend SrcIpAddr = extract(@'Address\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', 1, EventOriginalMessage)
| where isnotempty(SrcIpAddr)) on SrcUserName
Stage 9: where
where ip_lst =~ SrcIpAddr
Stage 10: extend
extend AccountCustomEntity, IPCustomEntity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActivityType | eq |
| field:"ActivityType" kind:eq value:"27" |
DataRole | eq |
| field:"DataRole" kind:eq value:"Admin" |
SrcIpAddr | is_not_null | field:"SrcIpAddr" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcUserName | summarize |
ip_lst | summarize |
AccountCustomEntity | extend |
IPCustomEntity | extend |
SpyCloud infostealer malware credential exposure
#'Identifies credentials harvested by infostealer malware on a compromised device. This exposure may indicate an actively compromised endpoint with associated command-and-control risk.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Command & Control |
Rule Body
id: ead4deed-9d48-4646-aee0-6b46c2dd1ae6
name: SpyCloud infostealer malware credential exposure
description: |
'Identifies credentials harvested by infostealer malware on a compromised device. This
exposure may indicate an actively compromised endpoint with associated command-and-control
risk.'
severity: High
requiredDataConnectors:
- connectorId: SpyCloudEnterpriseProtectionCCF
dataTypes:
- SpyCloudBreachWatchlistV2_CL
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
- CommandAndControl
relevantTechniques:
- T1555
- T1071
query: |
// Human-readable constant for the severity threshold used below
let severityThreshold = 25;
SpyCloudBreachWatchlistV2_CL
| where Severity == severityThreshold
| mv-expand IPAddress = IPAddresses to typeof(string)
| extend AccountName = Username, AccountEmail = Email, HostName = UserHostName
| project TimeGenerated, DocumentId, SourceId, SpyCloudPublishDate, AccountEmail,
AccountName, HostName, InfectedMachineId, InfectedTime, UserOS, IPAddress
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPAddress
version: 1.0.0
kind: scheduled
Stages and Predicates
Parameters
let severityThreshold = 25;
Stage 1: source
SpyCloudBreachWatchlistV2_CL
Stage 2: where
| where Severity == severityThreshold
Stage 3: mv-expand
| mv-expand IPAddress = IPAddresses to typeof(string)
Stage 4: extend
| extend AccountName = Username, AccountEmail = Email, HostName = UserHostName
Stage 5: project
| project TimeGenerated, DocumentId, SourceId, SpyCloudPublishDate, AccountEmail,
AccountName, HostName, InfectedMachineId, InfectedTime, UserOS, IPAddress
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Severity | eq |
| field:"Severity" kind:eq value:"25" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountEmail | project |
AccountName | project |
DocumentId | project |
HostName | project |
IPAddress | project |
InfectedMachineId | project |
InfectedTime | project |
SourceId | project |
SpyCloudPublishDate | project |
TimeGenerated | project |
UserOS | project |
Tailscale: User role elevated to admin or owner
#Identifies when a user's tailnet role changes from a lower-privilege role to admin, network-admin, or owner between consecutive snapshots. Privilege escalation is a high-value attacker objective and warrants prompt review.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Rule Body
id: d3c4e5f6-3456-7890-12ab-cdef12345003
name: "Tailscale: User role elevated to admin or owner"
description: |
Identifies when a user's tailnet role changes from a lower-privilege role to admin, network-admin, or owner between consecutive snapshots. Privilege escalation is a high-value attacker objective and warrants prompt review.
severity: High
status: Available
requiredDataConnectors:
- connectorId: TailscaleCCF
dataTypes:
- Tailscale_Users_CL
queryFrequency: 1h
queryPeriod: 2d
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
- Persistence
relevantTechniques:
- T1078
- T1098
query: |
let elevated = dynamic(["admin", "network-admin", "owner"]);
let recent =
Tailscale_Users_CL
| where TimeGenerated > ago(1h)
| summarize arg_max(TimeGenerated, *) by UserId
| where Role in (elevated)
| project UserId, LoginName, RoleNow = Role;
let prior =
Tailscale_Users_CL
| where TimeGenerated between (ago(2d) .. ago(1h))
| summarize arg_max(TimeGenerated, *) by UserId
| project UserId, RolePrior = Role;
recent
| join kind=inner prior on UserId
| where RoleNow != RolePrior
| where RolePrior !in (elevated)
| project TimeGenerated = now(), UserId, LoginName, RolePrior, RoleNow
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: LoginName
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: P1D
matchingMethod: AllEntities
kind: Scheduled
version: 1.0.0
Stages and Predicates
Parameters
let elevated = dynamic(["admin", "network-admin", "owner"]);
let recent is inlined into the numbered stages below.
Let Binding: prior
let prior = Tailscale_Users_CL
| where TimeGenerated between (ago(2d) .. ago(1h))
| summarize arg_max(TimeGenerated, *) by UserId
| project UserId, RolePrior = Role;
Stages 1 to 5 define let recent as the rule's main pipeline source. Stages 6 to 9 use it.
Stage 1: source
Tailscale_Users_CL
Stage 2: where
| where TimeGenerated > ago(1h)
Stage 3: summarize
| summarize arg_max(TimeGenerated, *) by UserId
Stage 4: where
| where Role in (elevated)
Stage 5: project
| project UserId, LoginName, RoleNow = Role
Stage 6: join
recent
| join kind=inner prior on UserId
Stage 7: where
where RoleNow != RolePrior
Stage 8: where
where not (RolePrior in ("admin", "network-admin", "owner"))
Stage 9: project
project LoginName, RoleNow, RolePrior, TimeGenerated, UserId
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Role | in |
| field:"Role" kind:in |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
RolePrior | in | admin, network-admin, owner | excludes:RolePrior field:"RolePrior" value:"admin" field:"RolePrior" value:"network-admin" field:"RolePrior" value:"owner" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
LoginName | project |
RoleNow | project |
RolePrior | project |
TimeGenerated | project |
UserId | project |