Detection rules › Kusto
Kusto rules: 0y
AIShield - Image classification AI Model extraction high suspicious vulnerability detection
#'This alert creates an incident when Image classification AI Model extraction high suspicious, high severity vulnerability detected from the AIShield.'
Rule Body
id: b4cc5396-2a34-45f5-a726-860e476edf15
name: AIShield - Image classification AI Model extraction high suspicious vulnerability detection
description: |
'This alert creates an incident when Image classification AI Model extraction high suspicious, high severity vulnerability detected from the AIShield.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: BoschAIShield
dataTypes:
- AIShield
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
AIShield
| where Message has 'Image Classification AI Model Extraction Attack Identified'
| where Severity =~ 'High'
| where SuspiciousLevel =~ 'high suspicious attack'
| extend NTDomain = tostring(split(Computer, '\\')[0]), HostName = tostring(split(Computer, '.')[0])
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: NTDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceName
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertDisplayNameFormat: AIShield - Image Classification AI Model extraction high suspicious vulnerability detected.
alertDescriptionFormat: |
This query detects Image Classification AI Model extraction high suspicious, high severity alert from AIShield generated at {{TimeGenerated}}.\n\nPlease check the source for more information and investigate further.
alertTacticsColumnName: null
alertSeverityColumnName: Severity
version: 1.0.4
kind: Scheduled
Stages and Predicates
Stage 1: source
AIShield
Stage 2: where
| where Message has 'Image Classification AI Model Extraction Attack Identified'
Stage 3: where
| where Severity =~ 'High'
Stage 4: where
| where SuspiciousLevel =~ 'high suspicious attack'
Stage 5: extend
| extend NTDomain = tostring(split(Computer, '\\')[0]), HostName = tostring(split(Computer, '.')[0])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Message | match |
| field:"Message" kind:match value:"Image Classification AI Model Extraction Attack Identified" |
Severity | eq |
| field:"Severity" kind:eq value:"High" |
SuspiciousLevel | eq |
| field:"SuspiciousLevel" kind:eq value:"high suspicious attack" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HostName | extend |
NTDomain | extend |
AWSCloudTrail - Successful brute force attack on S3 Bucket
#Detects repeated failed GetObject attempts against an S3 bucket followed by a successful access from the same source, which can indicate brute-force-style object discovery or access attempts.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| AWS | CloudTrail event GetObject: Retrieves an object from a specified S3 bucket. |
Rules Detecting the Same Action
These rules filter on the same operation.
- AWS Exfiltration via Anomalous GetObject API Activity (Splunk)
- AWSCloudTrail - S3 bucket suspicious ransomware activity (Kusto)
- AWSCloudTrail - S3 Object Exfiltration from Anonymous User (Kusto)
- S3 Access Via VPC Endpoint From External IP (Panther)
- Suspicious access of BEC related documents in AWS S3 buckets (Kusto)
Rule Body
id: 31b9e94b-0df6-4a3d-a297-3457b53c5d86
name: AWSCloudTrail - Successful brute force attack on S3 Bucket
description: |
Detects repeated failed GetObject attempts against an S3 bucket followed by a successful access from the
same source, which can indicate brute-force-style object discovery or access attempts.
severity: High
status: Available
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1110
query: |
let timeframe = 1h;
let failed_attempts = AWSCloudTrail
| where TimeGenerated >= ago(timeframe)
| where EventName == "GetObject" and isnotempty(ErrorMessage) and isnotempty(ErrorCode)
| where UserIdentityAccountId == "ANONYMOUS_PRINCIPAL" or UserIdentityAccessKeyId <> RecipientAccountId
| 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]), "")
| extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
| summarize time_min_failed=arg_min(TimeGenerated, *), failed_keys = dcount(keyName) by RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, SourceIpAddress, bucketName
| where failed_keys > 20;
let success_attempts = AWSCloudTrail
| where TimeGenerated >= ago(timeframe)
| where EventName == "GetObject" and isempty(ErrorMessage) and isempty(ErrorCode)
| where UserIdentityAccountId == "ANONYMOUS_PRINCIPAL" or UserIdentityAccessKeyId <> RecipientAccountId
| 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]), "")
| extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
| summarize time_min_success=arg_min(TimeGenerated, *), success_keys = dcount(keyName) by RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, SourceIpAddress, bucketName
| where success_keys >= 1;
failed_attempts
| join kind=inner success_attempts on SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, bucketName
| where time_min_success > time_min_failed
| project-away keyName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifier: CloudAppAccountId
columnName: RecipientAccountId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIpAddress
customDetails:
bucketName: bucketName
failed_keys: failed_keys
success_keys: success_keys
RecipientAccountId: RecipientAccountId
alertDetailsOverride:
alertDisplayNameFormat: 'AWS S3 brute force pattern detected from {{SourceIpAddress}}'
alertDescriptionFormat: 'Detected repeated failed and then successful S3 GetObject access for bucket {{bucketName}} in account {{RecipientAccountId}}.'
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let timeframe = 1h;
let failed_attempts is inlined into the numbered stages below.
Let Binding: success_attempts
let success_attempts = AWSCloudTrail
| where TimeGenerated >= ago(timeframe)
| where EventName == "GetObject" and isempty(ErrorMessage) and isempty(ErrorCode)
| where UserIdentityAccountId == "ANONYMOUS_PRINCIPAL" or UserIdentityAccessKeyId <> RecipientAccountId
| 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]), "")
| extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
| summarize time_min_success=arg_min(TimeGenerated, *), success_keys = dcount(keyName) by RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, SourceIpAddress, bucketName
| where success_keys >= 1;
Stages 1 to 7 define let failed_attempts as the rule's main pipeline source. Stages 8 to 10 use it.
Stage 1: source
AWSCloudTrail
Stage 2: where
| where TimeGenerated >= ago(timeframe)
Stage 3: where
| where EventName == "GetObject" and isnotempty(ErrorMessage) and isnotempty(ErrorCode)
Stage 4: where
| where UserIdentityAccountId == "ANONYMOUS_PRINCIPAL" or UserIdentityAccessKeyId <> RecipientAccountId
Stage 5: extend (5 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]), "")
| extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
Stage 6: summarize
| summarize time_min_failed=arg_min(TimeGenerated, *), failed_keys = dcount(keyName) by RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, SourceIpAddress, bucketName
Stage 7: where
| where failed_keys > 20
Stage 8: join
failed_attempts
| join kind=inner success_attempts on SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, bucketName
Stage 9: where
where time_min_success > time_min_failed
Stage 10: project-away
project-away keyName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ErrorCode | is_not_null | field:"ErrorCode" kind:is_not_null | |
ErrorCode | is_null | field:"ErrorCode" kind:is_null | |
ErrorMessage | is_not_null | field:"ErrorMessage" kind:is_not_null | |
ErrorMessage | is_null | field:"ErrorMessage" kind:is_null | |
EventName | eq |
| field:"aws::eventName" kind:eq |
UserIdentityAccountId | eq |
| field:"UserIdentityAccountId" kind:eq value:"ANONYMOUS_PRINCIPAL" |
failed_keys | gt |
| field:"failed_keys" kind:gt value:"20" |
success_keys | ge |
| field:"success_keys" kind:ge value:"1" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | summarize |
AccountUPNSuffix | summarize |
RecipientAccountId | summarize |
SourceIpAddress | summarize |
UserIdentityAccountId | summarize |
bucketName | summarize |
failed_keys | summarize |
time_min_failed | summarize |
AWSCloudTrail - Suspicious overly permissive KMS key policy created
#Detects creation or update of KMS key policies that grant broad encryption permissions to all principals. Overly permissive key policies can be abused for malicious encryption operations and indicate potential account compromise or risky misconfiguration.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
id: 60dfc193-0f73-4279-b43c-110ade02b201
name: AWSCloudTrail - Suspicious overly permissive KMS key policy created
description: |
Detects creation or update of KMS key policies that grant broad encryption permissions to all principals.
Overly permissive key policies can be abused for malicious encryption operations and indicate potential account
compromise or risky misconfiguration.
severity: High
status: Available
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1486
query: |
let kmsActions = dynamic(["kms:Encrypt", "kms:*"]); //Add other overly permissive APIs to this list.
AWSCloudTrail
| where EventName in ("CreateKey","PutKeyPolicy") and isempty(ErrorCode) and isempty(ErrorMessage)
| extend Statement = parse_json(tostring((parse_json(RequestParameters).policy))).Statement
| mvexpand Statement
| extend Action = tostring(parse_json(Statement).Action), Effect = tostring(parse_json(Statement).Effect), Principal = iff(isnotempty(tostring(parse_json(Statement).Principal.AWS)),tostring(parse_json(Statement).Principal.AWS), tostring(parse_json(Statement).Principal))
| where Effect =~ "Allow" and Action has_any (kmsActions) and Principal == "*"
| 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]), "")
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifier: CloudAppAccountId
columnName: RecipientAccountId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIpAddress
customDetails:
EventName: EventName
EventSource: EventSource
AWSRegion: AWSRegion
UserIdentityArn: UserIdentityArn
alertDetailsOverride:
alertDisplayNameFormat: 'AWS overly permissive KMS key policy change by {{AccountName}}'
alertDescriptionFormat: 'Detected {{EventName}} from {{SourceIpAddress}} creating a broadly permissive KMS policy in account {{RecipientAccountId}}.'
version: 1.0.5
kind: Scheduled
Stages and Predicates
Parameters
let kmsActions = dynamic(["kms:Encrypt", "kms:*"]);
Stage 1: source
AWSCloudTrail
Stage 2: where
| where EventName in ("CreateKey","PutKeyPolicy") and isempty(ErrorCode) and isempty(ErrorMessage)
Stage 3: extend
| extend Statement = parse_json(tostring((parse_json(RequestParameters).policy))).Statement
Stage 4: mv-expand
| mvexpand Statement
Stage 5: extend
| extend Action = tostring(parse_json(Statement).Action), Effect = tostring(parse_json(Statement).Effect), Principal = iff(isnotempty(tostring(parse_json(Statement).Principal.AWS)),tostring(parse_json(Statement).Principal.AWS), tostring(parse_json(Statement).Principal))
Principal =isnotempty(tostring(Statement.Principal.AWS))tostring(parse_json(Statement).Principal.AWS)tostring(parse_json(Statement).Principal)Stage 6: where
| where Effect =~ "Allow" and Action has_any (kmsActions) and Principal == "*"
Stage 7: 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]), "")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | match |
| field:"Action" kind:match |
Effect | eq |
| field:"Effect" kind:eq value:"Allow" |
ErrorCode | is_null | field:"aws::errorCode" kind:is_null | |
ErrorMessage | is_null | field:"aws::errorMessage" kind:is_null | |
EventName | in |
| field:"aws::eventName" kind:in |
Principal | eq |
| field:"Principal" kind:eq value:"*" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Statement | extend |
Action | extend |
Effect | extend |
Principal | extend |
UserIdentityArn | extend |
UserName | extend |
AccountName | extend |
AccountUPNSuffix | extend |
BloodHound Attack Path Finding - Large Default Groups in SQL Admins Groups
#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 in SQL Admins Groups
Rule Body
id: 000d4f86-fc35-4451-9b2b-2a660a95f715
name: BloodHound Attack Path Finding - Large Default Groups in SQL Admins Groups
description: BloodHound Attack Path Finding - Large Default Groups in SQL Admins Groups
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 == "LargeDefaultGroupsSQLAdmin"
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 == "LargeDefaultGroupsSQLAdmin"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"LargeDefaultGroupsSQLAdmin" |
BloodHound Attack Path Finding - Large Default Groups With Read LAPS Password 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 Read LAPS Password Privileges
Rule Body
id: d6450ee2-2161-4d3c-96c9-674c3c44a791
name: BloodHound Attack Path Finding - Large Default Groups With Read LAPS Password
Privileges
description: BloodHound Attack Path Finding - Large Default Groups With Read LAPS
Password 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 == "LargeDefaultGroupsReadLAPS"
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 == "LargeDefaultGroupsReadLAPS"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"LargeDefaultGroupsReadLAPS" |
Box - Forbidden file type downloaded
#'Detects when new user downloads forbidden file types.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 8889e69c-2161-412a-94a6-76c1b2d9daa7
name: Box - Forbidden file type downloaded
description: |
'Detects when new user downloads forbidden file types.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BoxDataConnector
dataTypes:
- BoxEvents_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1189
query: |
let forbidden_files = dynamic(['ps1', 'bat', 'scr', 'sh']);
BoxEvents
| where EventType =~ 'DOWNLOAD'
| extend file_type = extract(@'\.(\w+)$', 1, SourceItemName)
| where file_type in (forbidden_files)
| extend AccountCustomEntity = SrcUserName
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let forbidden_files = dynamic(['ps1', 'bat', 'scr', 'sh']);
Stage 1: source
BoxEvents
Stage 2: where
| where EventType =~ 'DOWNLOAD'
Stage 3: extend
| extend file_type = extract(@'\.(\w+)$', 1, SourceItemName)
Stage 4: where
| where file_type in (forbidden_files)
Stage 5: extend
| extend AccountCustomEntity = SrcUserName
Stage 6: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | eq |
| field:"EventType" kind:eq value:"DOWNLOAD" |
file_type | in |
| field:"file_type" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
file_type | extend |
AccountCustomEntity | extend |
IPCustomEntity | extend |
CiscoISE - Command executed with the highest privileges from new IP
#'Detects command execution with PrivilegeLevel - 15 from new source.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence | |
| Privilege Escalation |
Rule Body
id: 1fa0da3e-ec99-484f-aadb-93f59764e158
name: CiscoISE - Command executed with the highest privileges from new IP
description: |
'Detects command execution with PrivilegeLevel - 15 from new source.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Persistence
- PrivilegeEscalation
- DefenseEvasion
- Execution
relevantTechniques:
- T1133
- T1548
- T1059
query: |
let lbperiod = 14d;
let lbtime = 15m;
let knownAdminIpList =
CiscoISEEvent
| where TimeGenerated between (ago(lbperiod) .. ago(lbtime))
| where PrivilegeLevel == '15'
| summarize makelist(SrcIpAddr)
;
CiscoISEEvent
| where TimeGenerated > ago(lbtime)
| where PrivilegeLevel == '15'
| where SrcIpAddr !in (knownAdminIpList)
| project TimeGenerated, SrcIpAddr, DstIpAddr, DstUserName, CmdSet
| extend AccountCustomEntity = DstUserName
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.4
kind: Scheduled
Stages and Predicates
Parameters
let lbperiod = 14d;
let lbtime = 15m;
Let Binding: knownAdminIpList
let knownAdminIpList = CiscoISEEvent
| where TimeGenerated between (ago(lbperiod) .. ago(lbtime))
| where PrivilegeLevel == '15'
| summarize makelist(SrcIpAddr);
Stage 1: source
CiscoISEEvent
Stage 2: where
| where TimeGenerated > ago(lbtime)
Stage 3: where
| where PrivilegeLevel == '15'
Stage 4: where
| where SrcIpAddr !in (knownAdminIpList)
Stage 5: project
| project TimeGenerated, SrcIpAddr, DstIpAddr, DstUserName, CmdSet
Stage 6: extend
| extend AccountCustomEntity = DstUserName
Stage 7: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
PrivilegeLevel | eq |
| field:"PrivilegeLevel" kind:eq value:"15" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
SrcIpAddr | eq | knownAdminIpList | excludes:SrcIpAddr field:"SrcIpAddr" value:"knownAdminIpList" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
CmdSet | project |
DstIpAddr | project |
DstUserName | project |
SrcIpAddr | project |
TimeGenerated | project |
AccountCustomEntity | extend |
IPCustomEntity | extend |
Claroty - Critical baseline deviation
#Detects Claroty events where EventOriginalType or EventType indicates a Baseline Deviation and EventSeverity is 5, which represents a critical deviation from baseline that may indicate disruptive or unauthorized activity.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 9a8b4321-e2be-449b-8227-a78227441b2a
name: Claroty - Critical baseline deviation
description: Detects Claroty events where EventOriginalType or EventType indicates a Baseline Deviation and EventSeverity
is 5, which represents a critical deviation from baseline that may indicate disruptive or unauthorized activity.
severity: High
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1529
- T1565.001
query: |-
ClarotyEvent
| where EventOriginalType has 'Baseline Deviation' or EventType has 'Baseline Deviation'
| where EventSeverity == '5'
| project TimeGenerated, DstIpAddr
| extend IPCustomEntity = DstIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
alertDetailsOverride:
alertDisplayNameFormat: Claroty critical baseline deviation on {{IPCustomEntity}}
alertDescriptionFormat: Claroty reported a critical baseline deviation for {{IPCustomEntity}}.
version: 1.0.4
kind: Scheduled
Stages and Predicates
Stage 1: source
ClarotyEvent
Stage 2: where
| where EventOriginalType has 'Baseline Deviation' or EventType has 'Baseline Deviation'
Stage 3: where
| where EventSeverity == '5'
Stage 4: project
| project TimeGenerated, DstIpAddr
Stage 5: extend
| extend IPCustomEntity = DstIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventOriginalType | match |
| field:"EventOriginalType" kind:match value:"Baseline Deviation" |
EventSeverity | eq |
| field:"EventSeverity" kind:eq value:"5" |
EventType | match |
| field:"EventType" kind:match value:"Baseline Deviation" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstIpAddr | project |
TimeGenerated | project |
IPCustomEntity | extend |
CybleVision Alerts Mobile Apps
#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 suspicious, unauthorized or impersonating mobile applications from 3rd-party marketplaces using CybleVision data. Extracts metadata, screenshots, developer, package name, and detailed app attributes.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | |
| Resource Development | |
| Initial Access |
Rule Body
id: 6d55fefc-b334-4b79-b11c-667746b5bdde
name: CybleVision Alerts Mobile Apps
description: |
'Detects suspicious, unauthorized or impersonating mobile applications from 3rd-party marketplaces using CybleVision data. Extracts metadata, screenshots, developer, package name, and detailed app attributes.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
dataTypes:
- CybleVisionAlerts_CL
enabled: true
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: PT5H
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- Reconnaissance
- ResourceDevelopment
- InitialAccess
relevantTechniques:
- T1595
- T1608
- T1195
subTechniques: []
query: |
Alerts_mobile_apps
| where Service == "mobile_apps"
| extend MappedSeverity = Severity
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
alertDetailsOverride:
alertDisplayNameFormat: CybleVision Suspicious Mobile App {{MA_Title}}
alertDescriptionFormat: |
A potentially suspicious mobile application was detected. Developer {{MA_Developer}} Market {{MA_MarketSource}} Deep Link {{MA_DeepLink}}\n
alertDynamicProperties: []
customDetails:
MappedSeverity: Severity
Status: Status
AlertID: AlertID
Service: Service
AppName: AppName
Developer: MA_Developer
MarketSource: MA_MarketSource
MarketURL: MA_MarketURL
PackageName: MA_PackageName
Description: MA_Description
Downloads: MA_Downloads
MarketStatus: MA_MarketStatus
MarketUpdated: MA_MarketUpdated
CreatedOn: MA_Created
AppEmail: MA_Email
IconURL: MA_MarketURL
Website: MA_Website
Version: MA_Version
Screenshots: MA_Screenshots
Tags: MA_Tags
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: MA_DeepLink
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: Domain
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Alerts_mobile_apps
Stage 2: where
| where Service == "mobile_apps"
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:"mobile_apps" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
MappedSeverity | extend |
CYFIRMA - Data Breach and Web Monitoring - Ransomware Exposure Detected 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.
"This analytics rule detects high-severity ransomware threats targeting the organization, as reported by CYFIRMA's Dark Web and Data Breach Intelligence feeds. The alert is generated when threat actors post, claim, or associate ransomware activity with corporate domains, brands, or subsidiaries, indicating a potential data breach, extortion attempt, or unauthorized access."
MITRE ATT&CK Coverage
Rule Body
id: d5f9a6fe-7fd2-488c-8690-0ca24fba43dc
name: CYFIRMA - Data Breach and Web Monitoring - Ransomware Exposure Detected Rule
description: |
"This analytics rule detects high-severity ransomware threats targeting the organization, as reported by CYFIRMA's Dark Web and Data Breach Intelligence feeds.
The alert is generated when threat actors post, claim, or associate ransomware activity with corporate domains, brands, or subsidiaries, indicating a potential data breach, extortion attempt, or unauthorized access."
version: 1.0.1
kind: Scheduled
severity: Medium
requiredDataConnectors:
- connectorId: CyfirmaDigitalRiskAlertsConnector
dataTypes:
- CyfirmaDBWMRansomwareAlerts_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- InitialAccess
- Exfiltration
relevantTechniques:
- T1566.001
- T1566.002
- T1566.003
query: |
// Medium severity - Data Breach and Web Monitoring - Ransomware Exposure Detected
let timeFrame = 5m;
CyfirmaDBWMRansomwareAlerts_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,
Source=source,
Impact=impact,
Recommendation='',
ProviderName='CYFIRMA',
ProductName='DeCYFIR/DeTCT',
AlertTitle=Alert_title,
ThreatActors= threat_actors
| project
TimeGenerated,
Description,
RiskScore,
FirstSeen,
LastSeen,
AlertUID,
UID,
AssetType,
AssetValue,
Source,
Impact,
Recommendation,
ProductName,
ProviderName,
AlertTitle,
ThreatActors
customDetails:
AssetType: AssetType
AssetValue: AssetValue
ThreatActors: ThreatActors
RiskScore: RiskScore
Source: Source
Impact: Impact
Description: Description
Recommendation: Recommendation
FirstSeen: FirstSeen
LastSeen: LastSeen
AlertUID: AlertUID
UID: UID
TimeGenerated: TimeGenerated
alertDetailsOverride:
alertDisplayNameFormat: "CYFIRMA - Medium Severity Alert: Ransomware Exposure Detected - {{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
CyfirmaDBWMRansomwareAlerts_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,
Source=source,
Impact=impact,
Recommendation='',
ProviderName='CYFIRMA',
ProductName='DeCYFIR/DeTCT',
AlertTitle=Alert_title,
ThreatActors= threat_actors
Stage 4: project
| project
TimeGenerated,
Description,
RiskScore,
FirstSeen,
LastSeen,
AlertUID,
UID,
AssetType,
AssetValue,
Source,
Impact,
Recommendation,
ProductName,
ProviderName,
AlertTitle,
ThreatActors
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
severity | eq |
| field:"severity" kind:eq value:"High" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AlertTitle | project |
AlertUID | project |
AssetType | project |
AssetValue | project |
Description | project |
FirstSeen | project |
Impact | project |
LastSeen | project |
ProductName | project |
ProviderName | project |
Recommendation | project |
RiskScore | project |
Source | project |
ThreatActors | project |
TimeGenerated | project |
UID | project |
Digital Guardian - Exfiltration to private email
#'Detects exfiltration to private email.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Rule Body
id: edead9b5-243a-466b-ae78-2dae32ab1117
name: Digital Guardian - Exfiltration to private email
description: |
'Detects exfiltration to private email.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
relevantTechniques:
- T1048
query: |
DigitalGuardianDLPEvent
| where NetworkApplicationProtocol =~ 'SMTP'
| where isnotempty(inspected_document)
| extend s_user = substring(SrcUserName, 0, indexof(SrcUserName, '@'))
| extend d_user = substring(DstUserName, 0, indexof(DstUserName, '@'))
| extend s_domain = extract(@'@(.*)', 1, SrcUserName)
| extend d_domain = extract(@'@(.*)', 1, DstUserName)
| where s_domain != d_domain
| where s_user == d_user
| extend AccountCustomEntity = SrcUserName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
DigitalGuardianDLPEvent
Stage 2: where
| where NetworkApplicationProtocol =~ 'SMTP'
Stage 3: where
| where isnotempty(inspected_document)
Stage 4: extend (4 consecutive steps)
| extend s_user = substring(SrcUserName, 0, indexof(SrcUserName, '@'))
| extend d_user = substring(DstUserName, 0, indexof(DstUserName, '@'))
| extend s_domain = extract(@'@(.*)', 1, SrcUserName)
| extend d_domain = extract(@'@(.*)', 1, DstUserName)
Stage 5: where
| where s_domain != d_domain
Stage 6: where
| where s_user == d_user
Stage 7: extend
| extend AccountCustomEntity = SrcUserName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
NetworkApplicationProtocol | eq |
| field:"NetworkApplicationProtocol" kind:eq value:"SMTP" |
inspected_document | is_not_null | field:"inspected_document" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
s_user | extend |
d_user | extend |
s_domain | extend |
d_domain | extend |
AccountCustomEntity | extend |
Expired access credentials being used in Azure
#This query searches for logins with an expired access credential (for example an expired cookie). It then matches the IP address from which the expired credential access occurred with the IP addresses of successful logins. If there are logins with expired credentials, but no successful logins from an IP, this might indicate an attacker has copied the authentication cookie and is re-using it on another machine.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
id: 433c3b0a-7278-4d74-b137-963ac6f9a7e7
name: Expired access credentials being used in Azure
description: |
This query searches for logins with an expired access credential (for example an expired cookie). It then matches the IP address from which the expired credential access occurred with the IP addresses of successful logins.
If there are logins with expired credentials, but no successful logins from an IP, this might indicate an attacker has copied the authentication cookie and is re-using it on another machine.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
queryFrequency: 1d
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1528
query: |
// Timeframe to search for failed logins.
let timeframe=1d;
// Timeframe to look back for successful logins from the same user by IP.
let lookback=7d;
let SuspiciousSignings=(
SigninLogs
| where TimeGenerated >= ago(timeframe)
| where ResourceDisplayName contains "Windows Azure Active Directory"
// 50132 = SsoArtifactInvalidOrExpired - The session is not valid due to password expiration or recent password change.
// 50173 = FreshTokenNeeded - The provided grant has expired due to it being revoked, and a fresh auth token is needed.
// 70008 = ExpiredOrRevokedGrant - The refresh token has expired due to inactivity. The token was issued on XXX and was inactive for a certain period of time.
// 81010 = DesktopSsoAuthTokenInvalid - Seamless SSO failed because the user's Kerberos ticket has expired or is invalid.
| where ResultType in (50173, 50132, 70008, 81010)
| summarize FailedCountPerDay=count(),FailedUserAgents=make_set(UserAgent), FailedCountries=make_set(LocationDetails.countryOrRegion),FailedIps=make_set(IPAddress) by UserPrincipalName, Day=bin(TimeGenerated, 1d)
| where FailedCountPerDay >= 1
);
let SuccessLogins=(
SigninLogs
| where TimeGenerated >= ago(lookback)
| where UserPrincipalName in ((SuspiciousSignings | project UserPrincipalName))
| where ResultType == 0
| summarize count() by UserPrincipalName, IPAddress
);
SuspiciousSignings
| mv-expand FailedIp=FailedIps
| extend FailedIp=tostring(FailedIp)
| join kind=leftanti SuccessLogins on $left.FailedIp==$right.IPAddress, UserPrincipalName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserPrincipalName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: FailedIp
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let timeframe = 1d;
let lookback = 7d;
let SuspiciousSignings is inlined into the numbered stages below.
Let Binding: SuccessLogins
let SuccessLogins = (
SigninLogs
| where TimeGenerated >= ago(lookback)
| where UserPrincipalName in ((SuspiciousSignings | project UserPrincipalName))
| where ResultType == 0
| summarize count() by UserPrincipalName, IPAddress
);
Stages 1 to 6 define let SuspiciousSignings as the rule's main pipeline source. Stages 7 to 9 use it.
Stage 1: source
SigninLogs
Stage 2: where
| where TimeGenerated >= ago(timeframe)
Stage 3: where
| where ResourceDisplayName contains "Windows Azure Active Directory"
Stage 4: where
| where ResultType in (50173, 50132, 70008, 81010)
Stage 5: summarize
| summarize FailedCountPerDay=count(),FailedUserAgents=make_set(UserAgent), FailedCountries=make_set(LocationDetails.countryOrRegion),FailedIps=make_set(IPAddress) by UserPrincipalName, Day=bin(TimeGenerated, 1d)
Stage 6: where
| where FailedCountPerDay >= 1
Stage 7: mv-expand
SuspiciousSignings
| mv-expand FailedIp=FailedIps
Stage 8: extend
| extend FailedIp=tostring(FailedIp)
Stage 9: join (negated)
join kind=leftanti (SuccessLogins) on $left.FailedIp, $right.IPAddress, UserPrincipalName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
FailedCountPerDay | ge |
| field:"FailedCountPerDay" kind:ge value:"1" |
ResourceDisplayName | contains |
| field:"ResourceDisplayName" kind:contains value:"Windows Azure Active Directory" |
ResultType | in |
| field:"ResultType" kind:in |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
ResultType | eq | 0 | excludes:ResultType field:"ResultType" value:"0" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Day | summarize |
FailedCountPerDay | summarize |
FailedCountries | summarize |
FailedIps | summarize |
FailedUserAgents | summarize |
UserPrincipalName | summarize |
FailedIp | extend |
Front Door Premium WAF - SQLi Detection
#'Identifies a match for a SQL Injection attack in the Front Door Premium WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements. References: https://owasp.org/Top10/A03_2021-Injection/'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Stealth | |
| Privilege Escalation |
Rules Detecting the Same Action
These rules filter on the same operation.
- AFD WAF - Code Injection (Kusto)
- AFD WAF - Path Traversal Attack (Kusto)
- Front Door Premium WAF - XSS Detection (Kusto)
Rule Body
id: 16da3a2a-af29-48a0-8606-d467c180fe18
name: Front Door Premium WAF - SQLi Detection
description: |
'Identifies a match for a SQL Injection attack in the Front Door Premium WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements.
References: https://owasp.org/Top10/A03_2021-Injection/'
severity: High
status: Available
requiredDataConnectors:
- connectorId: WAF
dataTypes:
- AzureDiagnostics
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
- Execution
- InitialAccess
- PrivilegeEscalation
relevantTechniques:
- T1211
- T1059
- T1190
- T0890
tags:
- SQL Injection
query: |
let Threshold = 1;
AzureDiagnostics
| where Category =~ "FrontDoorWebApplicationFirewallLog"
| where action_s =~ "AnomalyScoring"
| where details_msg_s has "SQL Injection"
| parse details_data_s with MessageText "Matched Data:" MatchedData "AND " * "table_name FROM " TableName " " *
| project trackingReference_s, host_s, requestUri_s, TimeGenerated, clientIP_s, details_matches_s, details_msg_s, details_data_s, TableName, MatchedData
| join kind = inner(
AzureDiagnostics
| where Category =~ "FrontDoorWebApplicationFirewallLog"
| where action_s =~ "Block") on trackingReference_s
| summarize URI_s = make_set(requestUri_s,100), Table = make_set(TableName,100), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), TrackingReference = make_set(trackingReference_s,100), Matched_Data = make_set(MatchedData,100), Detail_Data = make_set(details_data_s,100), Detail_Message = make_set(details_msg_s,100), Total_TrackingReference = dcount(trackingReference_s) by clientIP_s, host_s, action_s
| where Total_TrackingReference >= Threshold
# The Threshold value above can be changed as per your infrastructure's requirement
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URI_s
- entityType: IP
fieldMappings:
- identifier: Address
columnName: clientIP_s
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let Threshold = 1;
Stage 1: source
AzureDiagnostics
Stage 2: where
| where Category =~ "FrontDoorWebApplicationFirewallLog"
Stage 3: where
| where action_s =~ "AnomalyScoring"
Stage 4: where
| where details_msg_s has "SQL Injection"
Stage 5: parse
| parse details_data_s with MessageText "Matched Data:" MatchedData "AND " * "table_name FROM " TableName " " *
Stage 6: project
| project trackingReference_s, host_s, requestUri_s, TimeGenerated, clientIP_s, details_matches_s, details_msg_s, details_data_s, TableName, MatchedData
Stage 7: join
| join kind = inner(
AzureDiagnostics
| where Category =~ "FrontDoorWebApplicationFirewallLog"
| where action_s =~ "Block") on trackingReference_s
Stage 8: summarize
summarize Detail_Data, Detail_Message, EndTime, Matched_Data, StartTime, Table, Total_TrackingReference, TrackingReference, URI_s by clientIP_s, host_s, action_s
Stage 9: where
where Total_TrackingReference >= 1
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Category | eq |
| field:"Category" kind:eq value:"FrontDoorWebApplicationFirewallLog" |
Total_TrackingReference | ge |
| field:"Total_TrackingReference" kind:ge value:"1" |
action_s | eq |
| field:"action_s" kind:eq |
details_msg_s | match |
| field:"details_msg_s" kind:match value:"SQL Injection" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Detail_Data | summarize |
Detail_Message | summarize |
EndTime | summarize |
Matched_Data | summarize |
StartTime | summarize |
Table | summarize |
Total_TrackingReference | summarize |
TrackingReference | summarize |
URI_s | summarize |
action_s | summarize |
clientIP_s | summarize |
host_s | summarize |
GitHub - User visibility Was changed
#'Detect activities when a user visibility Was changed. This query runs every day and its severity is Medium.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 0b85a077-8ba5-4cb5-90f7-1e882afe20c9
name: GitHub - User visibility Was changed
description: |
'Detect activities when a user visibility Was changed. This query runs every day and its severity is Medium.'
severity: Medium
status: Available
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
GitHubAuditData
| where Visibility != PreviousVisibility
| project Actor, PreviousVisibility, Visibility
| extend Name = iif(Actor contains "@", split(Actor, "@")[0], Actor)
| extend UPNSuffix = iif(Actor contains "@", split(Actor, "@")[1], "")
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Actor
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
GitHubAuditData
Stage 2: where
| where Visibility != PreviousVisibility
Stage 3: project
| project Actor, PreviousVisibility, Visibility
Stage 4: extend
| extend Name = iif(Actor contains "@", split(Actor, "@")[0], Actor)
Stage 5: extend
| extend UPNSuffix = iif(Actor contains "@", split(Actor, "@")[1], "")
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Actor | project |
PreviousVisibility | project |
Visibility | project |
Name | extend |
UPNSuffix | extend |
Google DNS - CVE-2020-1350 (SIGRED) exploitation pattern
#'Detects exploitation pattern of CVE-2020-1350 (SIGRED) vulnerability.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Rule Body
id: 1267d53d-f5fd-418b-b8da-34453a5994c2
name: Google DNS - CVE-2020-1350 (SIGRED) exploitation pattern
description: |
'Detects exploitation pattern of CVE-2020-1350 (SIGRED) vulnerability.'
severity: High
requiredDataConnectors:
- connectorId: GCPDNSDataConnector
dataTypes:
- GCPCloudDNS
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
relevantTechniques:
- T1068
query: |
let threshold = 15;
GCPCloudDNS
| where QueryTypeName in~ ('SIG', 'RRSIG')
| where NetworkProtocol =~ 'TCP'
| summarize count() by SrcIpAddr, bin(TimeGenerated, 1m)
| where count_ < threshold
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 15;
Stage 1: source
GCPCloudDNS
Stage 2: where
| where QueryTypeName in~ ('SIG', 'RRSIG')
Stage 3: where
| where NetworkProtocol =~ 'TCP'
Stage 4: summarize
| summarize count() by SrcIpAddr, bin(TimeGenerated, 1m)
Stage 5: where
| where count_ < threshold
Stage 6: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
NetworkProtocol | eq |
| field:"NetworkProtocol" kind:eq value:"TCP" |
QueryTypeName | in |
| field:"QueryTypeName" kind:in |
count_ | lt |
| field:"count_" kind:lt value:"15" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcIpAddr | summarize |
IPCustomEntity | extend |
GTI - Critical CVSS Score Vulnerability Detected
#Detects vulnerabilities ingested from Google Threat Intelligence with a CVSS v3 base score at or above the critical threshold (9.0). Uses explicit Cvssv3xBaseScore and Cvssv3xTranslatedBaseScore fields from the GTIVulnerabilities parser. Fires when a matching record is found, indicating a critically rated vulnerability that warrants immediate analyst review.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Privilege Escalation |
Rule Body
id: 3794141b-9bd3-41ae-8303-79f97692998b
name: GTI - Critical CVSS Score Vulnerability Detected
description: |
Detects vulnerabilities ingested from Google Threat Intelligence with a CVSS v3 base score at or
above the critical threshold (9.0). Uses explicit Cvssv3xBaseScore and Cvssv3xTranslatedBaseScore
fields from the GTIVulnerabilities parser. Fires when a matching record is found, indicating a
critically rated vulnerability that warrants immediate analyst review.
severity: High
status: Available
requiredDataConnectors:
- connectorId: GTIVulnerabilitiesConnector
dataTypes:
- GTI_Vulnerabilities_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Execution
- PrivilegeEscalation
- Impact
relevantTechniques:
- T1190
- T1068
- T1203
query: |
GTIVulnerabilities
| extend Cvssv3Score = coalesce(Cvssv3xBaseScore, Cvssv3xTranslatedBaseScore)
| where isnotempty(Cvssv3Score) and Cvssv3Score >= 9.0
| extend
Vendor = tostring(Cpes[0].start_cpe.vendor),
Product = tostring(Cpes[0].start_cpe.product),
CpeVersion = tostring(Cpes[0].start_cpe.version),
VendorFixUrl = coalesce(tostring(VendorFixReferences[0].url), tostring(VendorFixReferences[0]))
| mv-apply _c = Cpes on (
where isnotnull(_c.start_cpe)
| summarize AffectedVendors = make_set(tostring(_c.start_cpe.vendor)),
AffectedProducts = make_set(tostring(_c.start_cpe.product))
)
| project
TimeGenerated,
CveId,
VulnName,
Cvssv3Score,
Cvssv4xScore,
EpssScore,
EpssPercentile,
RiskRating,
Priority,
ExploitationState,
ExploitationConsequence,
Cvssv4xThreatExploitMaturity,
Vendor,
Product,
CpeVersion,
AffectedVendors,
AffectedProducts,
Description,
VendorFixUrl,
VendorFixReferences,
Tags,
Type
alertDetailsOverride:
alertDisplayNameFormat: 'GTI Critical CVSS: {{CveId}} (Score: {{Cvssv3Score}})'
alertDescriptionFormat: 'CVE: {{CveId}} | CVSS v3: {{Cvssv3Score}} | Risk: {{RiskRating}}'
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: VendorFixUrl
customDetails:
CveId: CveId
Cvssv3Score: Cvssv3Score
Cvssv4Score: Cvssv4xScore
EpssScore: EpssScore
EpssPercentile: EpssPercentile
RiskRating: RiskRating
Priority: Priority
ExploitState: ExploitationState
ExploitMaturity: Cvssv4xThreatExploitMaturity
AffectedVendor: Vendor
AffectedProduct: Product
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: P1D
matchingMethod: Selected
groupByCustomDetails:
- CveId
eventGroupingSettings:
aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
GTIVulnerabilities
Stage 2: extend
| extend Cvssv3Score = coalesce(Cvssv3xBaseScore, Cvssv3xTranslatedBaseScore)
Stage 3: where
| where isnotempty(Cvssv3Score) and Cvssv3Score >= 9.0
Stage 4: extend
| extend
Vendor = tostring(Cpes[0].start_cpe.vendor),
Product = tostring(Cpes[0].start_cpe.product),
CpeVersion = tostring(Cpes[0].start_cpe.version),
VendorFixUrl = coalesce(tostring(VendorFixReferences[0].url), tostring(VendorFixReferences[0]))
Stage 5: kusto:mv-apply
| mv-apply _c = Cpes on (
where isnotnull(_c.start_cpe)
| summarize AffectedVendors = make_set(tostring(_c.start_cpe.vendor)),
AffectedProducts = make_set(tostring(_c.start_cpe.product))
)
Stage 6: project
| project
TimeGenerated,
CveId,
VulnName,
Cvssv3Score,
Cvssv4xScore,
EpssScore,
EpssPercentile,
RiskRating,
Priority,
ExploitationState,
ExploitationConsequence,
Cvssv4xThreatExploitMaturity,
Vendor,
Product,
CpeVersion,
AffectedVendors,
AffectedProducts,
Description,
VendorFixUrl,
VendorFixReferences,
Tags,
Type
Stage 7: summarize aggregation inside the mv-apply branch
summarize
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Cvssv3Score | ge |
| field:"Cvssv3Score" kind:ge value:"9.0" |
Cvssv3Score | is_not_null | field:"Cvssv3Score" kind:is_not_null | |
_c.start_cpe | is_not_null | field:"_c.start_cpe" kind:is_not_null |
High count of connections by client IP on many ports
#'Identifies when 30 or more ports are used for a given client IP in 10 minutes occurring on the IIS server. This could be indicative of attempted port scanning or exploit attempt at internet facing web applications. This could also simply indicate a misconfigured service or device. References: IIS status code mapping - https://support.microsoft.com/help/943891/the-http-status-code-in-iis-7-0-iis-7-5-and-iis-8-0 Win32 Status code mapping - https://msdn.microsoft.com/library/cc231199.aspx'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 44a555d8-ecee-4a25-95ce-055879b4b14b
name: High count of connections by client IP on many ports
description: |
'Identifies when 30 or more ports are used for a given client IP in 10 minutes occurring on the IIS server.
This could be indicative of attempted port scanning or exploit attempt at internet facing web applications.
This could also simply indicate a misconfigured service or device.
References:
IIS status code mapping - https://support.microsoft.com/help/943891/the-http-status-code-in-iis-7-0-iis-7-5-and-iis-8-0
Win32 Status code mapping - https://msdn.microsoft.com/library/cc231199.aspx'
severity: Medium
requiredDataConnectors:
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
query: |
let timeBin = 10m;
let portThreshold = 30;
W3CIISLog
| extend scStatusFull = strcat(scStatus, ".",scSubStatus)
// Map common IIS codes
| extend scStatusFull_Friendly = case(
scStatusFull == "401.0", "Access denied.",
scStatusFull == "401.1", "Logon failed.",
scStatusFull == "401.2", "Logon failed due to server configuration.",
scStatusFull == "401.3", "Unauthorized due to ACL on resource.",
scStatusFull == "401.4", "Authorization failed by filter.",
scStatusFull == "401.5", "Authorization failed by ISAPI/CGI application.",
scStatusFull == "403.0", "Forbidden.",
scStatusFull == "403.4", "SSL required.",
"See - https://support.microsoft.com/help/943891/the-http-status-code-in-iis-7-0-iis-7-5-and-iis-8-0")
// Mapping to Hex so can be mapped using website in comments above
| extend scWin32Status_Hex = tohex(tolong(scWin32Status))
// Map common win32 codes
| extend scWin32Status_Friendly = case(
scWin32Status_Hex =~ "775", "The referenced account is currently locked out and cannot be logged on to.",
scWin32Status_Hex =~ "52e", "Logon failure: Unknown user name or bad password.",
scWin32Status_Hex =~ "532", "Logon failure: The specified account password has expired.",
scWin32Status_Hex =~ "533", "Logon failure: Account currently disabled.",
scWin32Status_Hex =~ "2ee2", "The request has timed out.",
scWin32Status_Hex =~ "0", "The operation completed successfully.",
scWin32Status_Hex =~ "1", "Incorrect function.",
scWin32Status_Hex =~ "2", "The system cannot find the file specified.",
scWin32Status_Hex =~ "3", "The system cannot find the path specified.",
scWin32Status_Hex =~ "4", "The system cannot open the file.",
scWin32Status_Hex =~ "5", "Access is denied.",
scWin32Status_Hex =~ "8009030e", "SEC_E_NO_CREDENTIALS",
scWin32Status_Hex =~ "8009030C", "SEC_E_LOGON_DENIED",
"See - https://msdn.microsoft.com/library/cc231199.aspx")
// decode URI when available
| extend decodedUriQuery = url_decode(csUriQuery)
// Count of attempts by client IP on many ports
| summarize makeset(sPort), makeset(decodedUriQuery), makeset(csUserName), makeset(sSiteName), makeset(sPort), makeset(csUserAgent), makeset(csMethod), makeset(csUriQuery), makeset(scStatusFull), makeset(scStatusFull_Friendly), makeset(scWin32Status_Hex), makeset(scWin32Status_Friendly), ConnectionsCount = count() by bin(TimeGenerated, timeBin), cIP, Computer, sIP
| extend portCount = arraylength(set_sPort)
| where portCount >= portThreshold
| project TimeGenerated, cIP, set_sPort, set_csUserName, set_decodedUriQuery, Computer, set_sSiteName, sIP, set_csUserAgent, set_csMethod, set_scStatusFull, set_scStatusFull_Friendly, set_scWin32Status_Hex, set_scWin32Status_Friendly, ConnectionsCount, portCount
| order by portCount
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: cIP
version: 1.0.3
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Network" ]
Stages and Predicates
Parameters
let timeBin = 10m;
let portThreshold = 30;
Stage 1: source
W3CIISLog
Stage 2: extend (5 consecutive steps)
| extend scStatusFull = strcat(scStatus, ".",scSubStatus)
| extend scStatusFull_Friendly = case(
scStatusFull == "401.0", "Access denied.",
scStatusFull == "401.1", "Logon failed.",
scStatusFull == "401.2", "Logon failed due to server configuration.",
scStatusFull == "401.3", "Unauthorized due to ACL on resource.",
scStatusFull == "401.4", "Authorization failed by filter.",
scStatusFull == "401.5", "Authorization failed by ISAPI/CGI application.",
scStatusFull == "403.0", "Forbidden.",
scStatusFull == "403.4", "SSL required.",
"See - https://support.microsoft.com/help/943891/the-http-status-code-in-iis-7-0-iis-7-5-and-iis-8-0")
| extend scWin32Status_Hex = tohex(tolong(scWin32Status))
| extend scWin32Status_Friendly = case(
scWin32Status_Hex =~ "775", "The referenced account is currently locked out and cannot be logged on to.",
scWin32Status_Hex =~ "52e", "Logon failure: Unknown user name or bad password.",
scWin32Status_Hex =~ "532", "Logon failure: The specified account password has expired.",
scWin32Status_Hex =~ "533", "Logon failure: Account currently disabled.",
scWin32Status_Hex =~ "2ee2", "The request has timed out.",
scWin32Status_Hex =~ "0", "The operation completed successfully.",
scWin32Status_Hex =~ "1", "Incorrect function.",
scWin32Status_Hex =~ "2", "The system cannot find the file specified.",
scWin32Status_Hex =~ "3", "The system cannot find the path specified.",
scWin32Status_Hex =~ "4", "The system cannot open the file.",
scWin32Status_Hex =~ "5", "Access is denied.",
scWin32Status_Hex =~ "8009030e", "SEC_E_NO_CREDENTIALS",
scWin32Status_Hex =~ "8009030C", "SEC_E_LOGON_DENIED",
"See - https://msdn.microsoft.com/library/cc231199.aspx")
| extend decodedUriQuery = url_decode(csUriQuery)
Stage 3: summarize
| summarize makeset(sPort), makeset(decodedUriQuery), makeset(csUserName), makeset(sSiteName), makeset(sPort), makeset(csUserAgent), makeset(csMethod), makeset(csUriQuery), makeset(scStatusFull), makeset(scStatusFull_Friendly), makeset(scWin32Status_Hex), makeset(scWin32Status_Friendly), ConnectionsCount = count() by bin(TimeGenerated, timeBin), cIP, Computer, sIP
Stage 4: extend
| extend portCount = arraylength(set_sPort)
Stage 5: where
| where portCount >= portThreshold
Stage 6: project
| project TimeGenerated, cIP, set_sPort, set_csUserName, set_decodedUriQuery, Computer, set_sSiteName, sIP, set_csUserAgent, set_csMethod, set_scStatusFull, set_scStatusFull_Friendly, set_scWin32Status_Hex, set_scWin32Status_Friendly, ConnectionsCount, portCount
Stage 7: sort
| order by portCount
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
portCount | ge |
| field:"portCount" kind:ge value:"30" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Computer | project |
ConnectionsCount | project |
TimeGenerated | project |
cIP | project |
portCount | project |
sIP | project |
set_csMethod | project |
set_csUserAgent | project |
set_csUserName | project |
set_decodedUriQuery | project |
set_sPort | project |
set_sSiteName | project |
set_scStatusFull | project |
set_scStatusFull_Friendly | project |
set_scWin32Status_Friendly | project |
set_scWin32Status_Hex | project |
iboss - Command-and-Control Detected
#Identifies web traffic where the iboss platform flagged command-and-control (C2) activity. A populated CNCDetected flag indicates the gateway observed communication to a known or suspected C2 destination. Surfaces the user, destination URL, and host to support triage of potentially compromised endpoints.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 40cf9670-d4be-4149-9082-5809a2b12ca1
name: iboss - Command-and-Control Detected
description: |
Identifies web traffic where the iboss platform flagged command-and-control (C2) activity. A populated CNCDetected flag indicates the gateway observed communication to a known or suspected C2 destination. Surfaces the user, destination URL, and host to support triage of potentially compromised endpoints.
severity: High
status: Available
requiredDataConnectors:
- connectorId: ibossAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
ibossUrlEvent
| where CNCDetected == 1
| project EventTime, SrcUsername, Url, Domain, SrcIpAddr, DstIpAddr, DstPortNumber, DvcAction, UrlCategory, HttpUserAgent
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: SrcUsername
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
ibossUrlEvent
Stage 2: where
| where CNCDetected == 1
Stage 3: project
| project EventTime, SrcUsername, Url, Domain, SrcIpAddr, DstIpAddr, DstPortNumber, DvcAction, UrlCategory, HttpUserAgent
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CNCDetected | eq |
| field:"CNCDetected" kind:eq value:"1" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Domain | project |
DstIpAddr | project |
DstPortNumber | project |
DvcAction | project |
EventTime | project |
HttpUserAgent | project |
SrcIpAddr | project |
SrcUsername | project |
Url | project |
UrlCategory | project |
Object Marked as Clean
#Detects when an object is marked as clean.
Rule Body
id: 56d70592-077e-4e7b-9d8a-b5d8b0a6a52b
name: Object Marked as Clean
description: Detects when an object is marked as clean.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 41610\n| project\n Date\
\ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n DataSource = original_host,\n\
\ EventId = instanceId,\n [\"User Name\"] = 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 == 41610
Stage 3: project
| project
Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
DataSource = original_host,
EventId = instanceId,
["User Name"] = user,
MessageDetails = Description,
Severity = SeverityDescription
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"41610" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
EventId | project |
MessageDetails | project |
Severity | project |
User Name | project |
Objects Deleted from Malware Detection Exclusions
#Detects when an object is deleted from malware detection exclusions.
Rule Body
id: f4cad1aa-a1e2-46dc-bf09-6cf8dc67f0ca
name: Objects Deleted from Malware Detection Exclusions
description: Detects when an object is deleted from malware detection exclusions.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 42270\n| extend RemovedObjectNames\
\ = extract(\"RemovedObjectNames=\\\"([^\\\"]*)\\\"\", 1, SyslogMessage)\n| project\n\
\ Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n DataSource\
\ = original_host,\n EventId = instanceId,\n [\"User Name\"] = user,\n \
\ [\"Object Names\"] = RemovedObjectNames,\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 == 42270
Stage 3: extend
| extend RemovedObjectNames = extract("RemovedObjectNames=\"([^\"]*)\"", 1, SyslogMessage)
Stage 4: project
| project
Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
DataSource = original_host,
EventId = instanceId,
["User Name"] = user,
["Object Names"] = RemovedObjectNames,
MessageDetails = Description,
Severity = SeverityDescription
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"42270" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
EventId | project |
MessageDetails | project |
Object Names | project |
Severity | project |
User Name | project |
OCI - Multiple rejects on rare ports
#'Detects multiple rejects on rare ports.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance |
Rule Body
id: 482c24b9-a700-4b2a-85d3-1c42110ba78c
name: OCI - Multiple rejects on rare ports
description: |
'Detects multiple rejects on rare ports.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: OracleCloudInfrastructureLogsConnector
dataTypes:
- OCILogs
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Reconnaissance
relevantTechniques:
- T1595
query: |
let threshold = 50;
let r_dports =
OCILogs
| where EventType contains 'vcn.flowlogs'
| where ipv4_is_private(DstIpAddr)
| where ipv4_is_private(SrcIpAddr) == False
| summarize count() by DstPortNumber
| top 10 by count_ asc
| summarize dports = makeset(DstPortNumber)
| extend k = 1;
OCILogs
| where EventType contains 'vcn.flowlogs'
| where data_action_s =~ 'REJECT'
| where ipv4_is_private(DstIpAddr)
| where ipv4_is_private(SrcIpAddr) == False
| extend k = 1
| join kind=innerunique (r_dports) on k
| where dports has tostring(DstPortNumber)
| summarize count() by SrcIpAddr, bin(TimeGenerated, 5m)
| where count_ > threshold
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 50;
Let Binding: r_dports
let r_dports = OCILogs
| where EventType contains 'vcn.flowlogs'
| where ipv4_is_private(DstIpAddr)
| where ipv4_is_private(SrcIpAddr) == False
| summarize count() by DstPortNumber
| top 10 by count_ asc
| summarize dports = makeset(DstPortNumber)
| extend k = 1;
Stage 1: source
OCILogs
Stage 2: where
| where EventType contains 'vcn.flowlogs'
Stage 3: where
| where data_action_s =~ 'REJECT'
Stage 4: where
| where ipv4_is_private(DstIpAddr)
Stage 5: where
| where ipv4_is_private(SrcIpAddr) == False
Stage 6: extend
| extend k = 1
Stage 7: join
| join kind=innerunique (r_dports) on k
Stage 8: where
where dports =~ <unknown:tostring(, DstPortNumber)>
Stage 9: summarize
summarize by SrcIpAddr
Stage 10: where
where count_ > 50
Stage 11: extend
extend IPCustomEntity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DstIpAddr | cidr_match |
| field:"DstIpAddr" kind:cidr_match |
EventType | contains |
| field:"EventType" kind:contains value:"vcn.flowlogs" |
count_ | gt |
| field:"count_" kind:gt value:"50" |
data_action_s | eq |
| field:"data_action_s" kind:eq value:"REJECT" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
SrcIpAddr | 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:SrcIpAddr |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcIpAddr | summarize |
IPCustomEntity | extend |
ProofpointPOD - Suspicious attachment
#'Detects when email contains suspicious attachment (file type).'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: f6a51e2c-2d6a-4f92-a090-cfb002ca611f
name: ProofpointPOD - Suspicious attachment
description: |
'Detects when email contains suspicious attachment (file type).'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: ProofpointPOD
dataTypes:
- ProofpointPOD_message_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1566
query: |
let lbtime = 10m;
let disallowed_ext = dynamic(['ps1', 'exe', 'vbs', 'js', 'scr']);
ProofpointPOD
| where TimeGenerated > ago(lbtime)
| where EventType == 'message'
| where NetworkDirection == 'inbound'
| where FilterDisposition !in ('reject', 'discard')
| extend attachedExt = todynamic(MsgParts)[0]['detectedExt']
| where tolower(attachedExt) in (disallowed_ext)
| project SrcUserUpn, AccountCustomEntity = parse_json(DstUserUpn)[0], attachedExt
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let lbtime = 10m;
let disallowed_ext = dynamic(['ps1', 'exe', 'vbs', 'js', 'scr']);
Stage 1: source
ProofpointPOD
Stage 2: where
| where TimeGenerated > ago(lbtime)
Stage 3: where
| where EventType == 'message'
Stage 4: where
| where NetworkDirection == 'inbound'
Stage 5: where
| where FilterDisposition !in ('reject', 'discard')
Stage 6: extend
| extend attachedExt = todynamic(MsgParts)[0]['detectedExt']
Stage 7: where
| where tolower(attachedExt) in (disallowed_ext)
Stage 8: project
| project SrcUserUpn, AccountCustomEntity = parse_json(DstUserUpn)[0], attachedExt
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | eq |
| field:"EventType" kind:eq value:"message" |
NetworkDirection | eq |
| field:"NetworkDirection" kind:eq value:"inbound" |
attachedExt | in |
| field:"attachedExt" kind:in |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
FilterDisposition | in | discard, reject | excludes:FilterDisposition field:"FilterDisposition" value:"discard" field:"FilterDisposition" value:"reject" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | project |
SrcUserUpn | project |
attachedExt | project |
Samsung Knox - Security Log Full Events
#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.
When the Knox Security Log is full on a device.
Rule Body
id: bf9be360-7f08-48b2-8e9d-ca240c48b404
name: Samsung Knox - Security Log Full Events
version: 1.0.2
kind: NRT
description: When the Knox Security Log is full on a device.
severity: High
status: Available
requiredDataConnectors:
- connectorId: SamsungDCDefinition
dataTypes:
- Samsung_Knox_Audit_CL
tactics: []
relevantTechniques: []
query: |
Samsung_Knox_Audit_CL
| where Name == "LOG_IS_FULL"
and MitreTtp has "KNOX.1"
suppressionEnabled: false
suppressionDuration: PT5H
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: SingleAlert
Stages and Predicates
Stage 1: source
Samsung_Knox_Audit_CL
Stage 2: where
| where Name == "LOG_IS_FULL"
and MitreTtp has "KNOX.1"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
MitreTtp | match |
| field:"MitreTtp" kind:match value:"KNOX.1" |
Name | eq |
| field:"Name" kind:eq value:"LOG_IS_FULL" |
Suspicious Entra ID Joined Device Update
#This query looks for suspicious updates to an Microsoft Entra ID joined device where the device name is changed and the device falls out of compliance. This could occur when a threat actor updates the details of an Autopilot provisioned device using a stolen device ticket, in order to access certificates and keys. Ref: https://dirkjanm.io/assets/raw/Insomnihack Breaking and fixing Azure AD device identity security.pdf
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Azure | Update device |
Rules Detecting the Same Action
These rules filter on the same operation.
- Azure Device No Longer Managed or Compliant (Sigma)
- Azure Device or Configuration Modified or Deleted (Sigma)
- Entra ID AiTM Phishing-Kit Chain Detected (Elastic)
- Entra ID Device Registration with Phishing Kit Default OS Build (Elastic)
- Entra ID Device Registration with ROADtools Default OS Build (Elastic)
- Entra ID Deviceless Windows Hello Sign-in Followed by Device Registration (Elastic)
- Entra ID Multiple Device Registrations by a Single User (Elastic)
- Entra ID Protection User Alert and Device Registration (Elastic)
Rule Body
id: 3a3c6835-0086-40ca-b033-a93bf26d878f
name: Suspicious Entra ID Joined Device Update
description: |
'This query looks for suspicious updates to an Microsoft Entra ID joined device where the device name is changed and the device falls out of compliance.
This could occur when a threat actor updates the details of an Autopilot provisioned device using a stolen device ticket, in order to access certificates and keys.
Ref: https://dirkjanm.io/assets/raw/Insomnihack%20Breaking%20and%20fixing%20Azure%20AD%20device%20identity%20security.pdf'
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- CredentialAccess
relevantTechniques:
- T1528
query: |
AuditLogs
| where OperationName =~ "Update device"
| mv-apply TargetResource=TargetResources on (
where TargetResource.type =~ "Device"
| extend ModifiedProperties = TargetResource.modifiedProperties
| extend DeviceId = TargetResource.id)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "CloudDisplayName"
| extend OldName = Prop.oldValue
| extend NewName = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "IsCompliant"
| extend OldComplianceState = Prop.oldValue
| extend NewComplianceState = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "TargetId.DeviceTrustType"
| extend OldTrustType = Prop.oldValue
| extend NewTrustType = Prop.newValue)
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "Included Updated Properties"
| extend UpdatedProperties = Prop.newValue)
| extend OldDeviceName = tostring(parse_json(tostring(OldName))[0])
| extend NewDeviceName = tostring(parse_json(tostring(NewName))[0])
| extend OldComplianceState = tostring(parse_json(tostring(OldComplianceState))[0])
| extend NewComplianceState = tostring(parse_json(tostring(NewComplianceState))[0])
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend UpdatedPropertiesCount = array_length(split(UpdatedProperties, ','))
| where OldDeviceName != NewDeviceName
| where OldComplianceState =~ 'true' and NewComplianceState =~ 'false'
// Most common is transferring from AAD Registered to AAD Joined - we just want AAD Joined devices
| where NewTrustType == '"AzureAd"' and OldTrustType != '"Workplace"'
// We can modify this value to tune FPs - more properties changed about the device beyond its name the more suspicious it could be
| where UpdatedPropertiesCount > 1
| project-reorder TimeGenerated, DeviceId, NewDeviceName, OldDeviceName, NewComplianceState, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, AADOperationType, OldTrustType, NewTrustType, UpdatedProperties, UpdatedPropertiesCount
| extend InitiatedByName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: NewDeviceName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: OldDeviceName
- entityType: Host
fieldMappings:
- identifier: AzureID
columnName: DeviceId
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: InitiatingUserPrincipalName
- identifier: Name
columnName: InitiatedByName
- identifier: UPNSuffix
columnName: InitiatedByUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: InitiatingAadUserId
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: InitiatingAppServicePrincipalId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: InitiatingIpAddress
alertDetailsOverride:
alertDisplayNameFormat: Suspicious Entra ID Joined Device Update {{OldDeviceName}} renamed to {{NewDeviceName}} and {{UpdatedPropertiesCount}} properties changed
alertDescriptionFormat: |
This query looks for suspicious updates to an Microsoft Entra ID joined device where the device name is changed and the device falls out of compliance.
In this case {{OldDeviceName}} was renamed to {{NewDeviceName}} and {{UpdatedPropertiesCount}} properties were changed.
This could occur when a threat actor updates the details of an Autopilot provisioned device using a stolen device ticket, in order to access certificates and keys.
Ref: https://dirkjanm.io/assets/raw/Insomnihack%20Breaking%20and%20fixing%20Azure%20AD%20device%20identity%20security.pdf
version: 1.0.4
kind: Scheduled
Stages and Predicates
Stage 1: source
AuditLogs
Stage 2: where
| where OperationName =~ "Update device"
Stage 3: kusto:mv-apply
| mv-apply TargetResource=TargetResources on (
where TargetResource.type =~ "Device"
| extend ModifiedProperties = TargetResource.modifiedProperties
| extend DeviceId = TargetResource.id)
Stage 4: kusto:mv-apply
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "CloudDisplayName"
| extend OldName = Prop.oldValue
| extend NewName = Prop.newValue)
Stage 5: kusto:mv-apply
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "IsCompliant"
| extend OldComplianceState = Prop.oldValue
| extend NewComplianceState = Prop.newValue)
Stage 6: kusto:mv-apply
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "TargetId.DeviceTrustType"
| extend OldTrustType = Prop.oldValue
| extend NewTrustType = Prop.newValue)
Stage 7: kusto:mv-apply
| mv-apply Prop=ModifiedProperties on (
where Prop.displayName =~ "Included Updated Properties"
| extend UpdatedProperties = Prop.newValue)
Stage 8: extend (10 consecutive steps)
| extend OldDeviceName = tostring(parse_json(tostring(OldName))[0])
| extend NewDeviceName = tostring(parse_json(tostring(NewName))[0])
| extend OldComplianceState = tostring(parse_json(tostring(OldComplianceState))[0])
| extend NewComplianceState = tostring(parse_json(tostring(NewComplianceState))[0])
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend UpdatedPropertiesCount = array_length(split(UpdatedProperties, ','))
Stage 9: where
| where OldDeviceName != NewDeviceName
Stage 10: where
| where OldComplianceState =~ 'true' and NewComplianceState =~ 'false'
Stage 11: where
| where NewTrustType == '"AzureAd"' and OldTrustType != '"Workplace"'
Stage 12: where
| where UpdatedPropertiesCount > 1
Stage 13: project-reorder
| project-reorder TimeGenerated, DeviceId, NewDeviceName, OldDeviceName, NewComplianceState, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, AADOperationType, OldTrustType, NewTrustType, UpdatedProperties, UpdatedPropertiesCount
Stage 14: extend
| extend InitiatedByName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
NewComplianceState | eq |
| field:"NewComplianceState" kind:eq value:"false" |
NewTrustType | eq |
| field:"NewTrustType" kind:eq |
OldComplianceState | eq |
| field:"OldComplianceState" kind:eq value:"true" |
OldTrustType | ne |
| field:"OldTrustType" kind:ne |
OperationName | eq |
| field:"OperationName" kind:eq value:"Update device" |
Prop.displayName | eq |
| field:"Prop.displayName" kind:eq |
TargetResource.type | eq |
| field:"TargetResource.type" kind:eq value:"Device" |
UpdatedPropertiesCount | gt |
| field:"UpdatedPropertiesCount" kind:gt value:"1" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
OldDeviceName | extend |
NewDeviceName | extend |
OldComplianceState | extend |
NewComplianceState | extend |
InitiatingAppName | extend |
InitiatingAppServicePrincipalId | extend |
InitiatingUserPrincipalName | extend |
InitiatingAadUserId | extend |
InitiatingIpAddress | extend |
UpdatedPropertiesCount | extend |
InitiatedByName | extend |
InitiatedByUPNSuffix | extend |
Zero Networks Segment - Rare JIT Rule Creation
#'Identifies when a JIT Rule connection is new or rare by a given account today based on comparison with the previous 14 days. JIT Rule creations are indicated by the Activity Type Id 20'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement |
Rule Body
id: 58688058-68b2-4b39-8009-ac6dc4d81ea1
name: Zero Networks Segment - Rare JIT Rule Creation
description: |
'Identifies when a JIT Rule connection is new or rare by a given account today based on comparison with the previous 14 days.
JIT Rule creations are indicated by the Activity Type Id 20'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: ZeroNetworksSegmentAuditFunction
dataTypes:
- ZNSegmentAudit_CL
- connectorId: ZeroNetworksSegmentAuditNativePoller
dataTypes:
- ZNSegmentAuditNativePoller_CL
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- LateralMovement
relevantTechniques:
- T1021
query: |
let starttime = 14d;
let endtime = 1d;
ZNSegmentAudit
| where TimeGenerated >= ago(endtime)
| where AuditTypeId == 20
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ConnectionCount = count()
by PerformedByName, tostring(DestinationEntityName)
// use left anti to exclude anything from the previous 14 days that is not rare
| join kind=leftanti (
ZNSegmentAudit
| where TimeGenerated between (ago(starttime) .. ago(endtime))
| where AuditTypeId == 20
| summarize by tostring(DestinationEntityName)
) on DestinationEntityName
| summarize StartTime = min(StartTime), EndTime = max(EndTime), ConnectionCount = sum(ConnectionCount)
by PerformedByName, DestinationEntityName
| extend TimeGenerated = StartTime
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: PerformedByName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DestinationEntityName
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let starttime = 14d;
let endtime = 1d;
Stage 1: source
ZNSegmentAudit
Stage 2: where
| where TimeGenerated >= ago(endtime)
Stage 3: where
| where AuditTypeId == 20
Stage 4: summarize
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ConnectionCount = count()
by PerformedByName, tostring(DestinationEntityName)
Stage 5: join (negated)
join kind=leftanti (ZNSegmentAudit) on DestinationEntityName
Stage 6: summarize
summarize ConnectionCount, EndTime, StartTime by PerformedByName, DestinationEntityName
Stage 7: extend
extend TimeGenerated
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AuditTypeId | eq |
| field:"AuditTypeId" kind:eq value:"20" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
AuditTypeId | eq | 20 | excludes:AuditTypeId field:"AuditTypeId" value:"20" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ConnectionCount | summarize |
DestinationEntityName | summarize |
EndTime | summarize |
PerformedByName | summarize |
StartTime | summarize |
TimeGenerated | extend |