Detection rules › Kusto
Kusto rules: 3f
AWSCloudTrail - Creation of new CRUD IAM policy and then privilege escalation
#Identifies creation of IAM policies that grant broad IAM CRUD permissions, followed by attachment of that policy to a user, role, or group. This pattern may indicate an attempt to establish or expand privileged access and should be reviewed as potential privilege escalation.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
- ASL AWS Create Policy Version to allow all resources (Splunk)
- AWS IAM CompromisedKeyQuarantine Policy Attached to User (Elastic)
- AWS Lateral Movement from Kubernetes SA via AssumeRoleWithWebIdentity (Elastic)
- AWSCloudTrail - CloudFormation policy created then used for privilege escalation (Kusto)
- AWSCloudTrail - Created CRUD S3 policy and then privilege escalation (Kusto)
- AWSCloudTrail - Creation of CRUD DynamoDB policy and then privilege escalation (Kusto)
- AWSCloudTrail - Creation of CRUD KMS policy and then privilege escalation (Kusto)
- AWSCloudTrail - Creation of CRUD Lambda policy and then privilege escalation (Kusto)
Rule Body
id: 8a607285-d95c-473d-8aab-59920de63af6
name: AWSCloudTrail - Creation of new CRUD IAM policy and then privilege escalation
description: |
Identifies creation of IAM policies that grant broad IAM CRUD permissions, followed by attachment of that policy to a user, role, or group. This pattern may indicate an attempt to establish or expand
privileged access and should be reviewed as potential privilege escalation.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
- PrivilegeEscalation
- Persistence
relevantTechniques:
- T1484
- T1098.003
query: |
let EventNameList = dynamic(["AttachUserPolicy","AttachRolePolicy","AttachGroupPolicy"]);
let createPolicy = dynamic(["CreatePolicy", "CreatePolicyVersion"]);
let timeframe = 1d;
let lookback = 14d;
// Creating Master table with all the events to use with materialize for better performance
let EventInfo = AWSCloudTrail
| where TimeGenerated >= ago(lookback)
| where EventName in (EventNameList) or EventName in (createPolicy)
| 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]), "");
//Checking for Policy creation event with Full Admin Privileges since lookback period.
let FullAdminPolicyEvents = materialize( EventInfo
| where TimeGenerated >= ago(lookback)
| where EventName in (createPolicy)
| extend PolicyName = tostring(parse_json(RequestParameters).policyName)
| extend Statement = parse_json(tostring((parse_json(RequestParameters).policyDocument))).Statement
| mvexpand Statement
| extend Action = parse_json(Statement).Action , Effect = tostring(parse_json(Statement).Effect), Resource = tostring(parse_json(Statement).Resource), Condition = tostring(parse_json(Statement).Condition)
| extend Action = tostring(Action)
| where Effect =~ "Allow" and (Action contains "iam:Create" and (Action contains "iam:Get" or Action contains "iam:List") and Action contains "iam:Update" and Action contains "iam:Delete") and Resource == "*" and Condition == ""
| distinct TimeGenerated, EventName, PolicyName, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityArn
| project-rename StartTime = TimeGenerated );
let PolicyAttach = materialize( EventInfo
| where TimeGenerated >= ago(timeframe)
| where EventName in (EventNameList) and isempty(ErrorCode) and isempty(ErrorMessage)
| extend PolicyName = tostring(split(tostring(parse_json(RequestParameters).policyArn),"/")[1])
| summarize AttachEventCount=count(), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by EventSource, EventName, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityType , UserIdentityArn, SourceIpAddress, PolicyName
| extend AttachEvent = pack("StartTime", StartTime, "EndTime", EndTime, "EventName", EventName, "UserIdentityType", UserIdentityType, "SourceIpAddress", SourceIpAddress,"AccountName", AccountName, "AccountUPNSuffix", AccountUPNSuffix, "RecipientAccountId", RecipientAccountId, "UserIdentityArn", UserIdentityArn)
| project EventSource, PolicyName, AttachEvent, AttachEventCount, RecipientAccountId, AccountName, AccountUPNSuffix
);
// Joining the list of PolicyNames and checking if it has been attached to any Roles/Users/Groups.
// These Roles/Users/Groups will be Privileged and can be used by adversaries as pivot point for privilege escalation via multiple ways.
FullAdminPolicyEvents
| join kind=leftouter
(
PolicyAttach
)
on PolicyName
| project-away PolicyName1
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifier: CloudAppAccountId
columnName: RecipientAccountId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIpAddress
customDetails:
PolicyName: PolicyName
EventName: EventName
RecipientAccountId: RecipientAccountId
UserIdentityArn: UserIdentityArn
alertDetailsOverride:
alertDisplayNameFormat: 'AWS CRUD IAM privilege escalation policy activity: {{PolicyName}} by {{AccountName}}'
alertDescriptionFormat: 'Detected {{EventName}} for policy {{PolicyName}} in account {{RecipientAccountId}}.'
version: 1.0.3
kind: Scheduled
Stages and Predicates
Parameters
let EventNameList = dynamic(["AttachUserPolicy","AttachRolePolicy","AttachGroupPolicy"]);
let createPolicy = dynamic(["CreatePolicy", "CreatePolicyVersion"]);
let timeframe = 1d;
let lookback = 14d;
let EventInfo and let FullAdminPolicyEvents are inlined into the numbered stages below.
Let Binding: PolicyAttach
let PolicyAttach = materialize( EventInfo
| where TimeGenerated >= ago(timeframe)
| where EventName in (EventNameList) and isempty(ErrorCode) and isempty(ErrorMessage)
| extend PolicyName = tostring(split(tostring(parse_json(RequestParameters).policyArn),"/")[1])
| summarize AttachEventCount=count(), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by EventSource, EventName, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityType , UserIdentityArn, SourceIpAddress, PolicyName
| extend AttachEvent = pack("StartTime", StartTime, "EndTime", EndTime, "EventName", EventName, "UserIdentityType", UserIdentityType, "SourceIpAddress", SourceIpAddress,"AccountName", AccountName, "AccountUPNSuffix", AccountUPNSuffix, "RecipientAccountId", RecipientAccountId, "UserIdentityArn", UserIdentityArn)
| project EventSource, PolicyName, AttachEvent, AttachEventCount, RecipientAccountId, AccountName, AccountUPNSuffix
);
Stages 1 to 14 define let FullAdminPolicyEvents as the rule's main pipeline source. Stages 15 to 17 use it.
Stage 1: source
AWSCloudTrail
Stage 2: where
| where TimeGenerated >= ago(lookback)
Stage 3: where
| where EventName in (EventNameList) or EventName in (createPolicy)
Stage 4: 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 5: where
| where TimeGenerated >= ago(lookback)
Stage 6: where
| where EventName in (createPolicy)
Stage 7: extend
| extend PolicyName = tostring(parse_json(RequestParameters).policyName)
Stage 8: extend
| extend Statement = parse_json(tostring((parse_json(RequestParameters).policyDocument))).Statement
Stage 9: mv-expand
| mvexpand Statement
Stage 10: extend
| extend Action = parse_json(Statement).Action , Effect = tostring(parse_json(Statement).Effect), Resource = tostring(parse_json(Statement).Resource), Condition = tostring(parse_json(Statement).Condition)
Stage 11: extend
| extend Action = tostring(Action)
Stage 12: where
| where Effect =~ "Allow" and (Action contains "iam:Create" and (Action contains "iam:Get" or Action contains "iam:List") and Action contains "iam:Update" and Action contains "iam:Delete") and Resource == "*" and Condition == ""
Stage 13: distinct
| distinct TimeGenerated, EventName, PolicyName, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityArn
Stage 14: project-rename
| project-rename StartTime = TimeGenerated
Stage 15: join
FullAdminPolicyEvents
| join kind=leftouter
(
PolicyAttach
)
on PolicyName
Stage 16: project-away
project-away PolicyName1
Stage 17: summarize aggregation inside the join branch
summarize by EventSource, EventName, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityType, UserIdentityArn, SourceIpAddress, PolicyName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | contains |
| field:"Action" kind:contains |
Effect | eq |
| field:"Effect" kind:eq value:"Allow" |
ErrorCode | is_null | field:"ErrorCode" kind:is_null | |
ErrorMessage | is_null | field:"ErrorMessage" kind:is_null | |
EventName | in |
| field:"aws::eventName" kind:in |
Resource | eq |
| field:"Resource" kind:eq value:"*" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | summarize |
AccountUPNSuffix | summarize |
EventName | summarize |
EventSource | summarize |
PolicyName | summarize |
RecipientAccountId | summarize |
SourceIpAddress | summarize |
UserIdentityArn | summarize |
UserIdentityType | summarize |
AWSCloudTrail - S3 bucket access point publicly exposed
#Detects S3 access point policy changes that allow public principals. Public exposure through access point configuration can lead to unauthorized data access and potential information disclosure.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Telemetry Coverage
Rule Body
id: b7a44e0d-ae4c-4fb2-be1b-aa0e45f2327b
name: AWSCloudTrail - S3 bucket access point publicly exposed
description: |
Detects S3 access point policy changes that allow public principals. Public exposure through access point
configuration can lead to unauthorized data access and potential information disclosure.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
relevantTechniques:
- T1537
query: |
AWSCloudTrail
| where EventName == "PutAccessPointPolicy" and isempty(ErrorCode) and isempty(ErrorMessage)
| extend Statement = parse_json(tostring((parse_json(RequestParameters).PutAccessPointPolicyRequest.Policy))).Statement
| mvexpand Statement
| extend Action = parse_json(Statement).Action , Effect = tostring(parse_json(Statement).Effect), Resource = tostring(parse_json(Statement).Resource), Condition = tostring(parse_json(Statement).Condition), Principal_aws = tostring(parse_json(Statement).Principal.AWS), Principal = tostring(parse_json(Statement).Principal)
| extend Action = tostring(Action)
| where Effect =~ "Allow" and (Principal_aws == "*" or Principal == "*") and isempty(Condition)
| 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]), "")
| distinct TimeGenerated, EventName, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityArn, UserIdentityUserName
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
UserIdentityArn: UserIdentityArn
RecipientAccountId: RecipientAccountId
alertDetailsOverride:
alertDisplayNameFormat: 'AWS S3 access point publicly exposed by {{AccountName}}'
alertDescriptionFormat: 'Detected {{EventName}} from {{SourceIpAddress}} creating public S3 access point exposure in account {{RecipientAccountId}}.'
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
AWSCloudTrail
Stage 2: where
| where EventName == "PutAccessPointPolicy" and isempty(ErrorCode) and isempty(ErrorMessage)
Stage 3: extend
| extend Statement = parse_json(tostring((parse_json(RequestParameters).PutAccessPointPolicyRequest.Policy))).Statement
Stage 4: mv-expand
| mvexpand Statement
Stage 5: extend
| extend Action = parse_json(Statement).Action , Effect = tostring(parse_json(Statement).Effect), Resource = tostring(parse_json(Statement).Resource), Condition = tostring(parse_json(Statement).Condition), Principal_aws = tostring(parse_json(Statement).Principal.AWS), Principal = tostring(parse_json(Statement).Principal)
Stage 6: extend
| extend Action = tostring(Action)
Stage 7: where
| where Effect =~ "Allow" and (Principal_aws == "*" or Principal == "*") and isempty(Condition)
Stage 8: 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 9: distinct
| distinct TimeGenerated, EventName, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityArn, UserIdentityUserName
Indicators
These rows show field, operator, and value matches.
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Statement | extend |
Action | extend |
Condition | extend |
Effect | extend |
Principal | extend |
Principal_aws | extend |
Resource | extend |
UserIdentityArn | extend |
UserName | extend |
AccountName | extend |
AccountUPNSuffix | extend |
BloodHound Attack Path Finding - Legacy SID History 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 - Legacy SID History on Tier Zero Objects
Rule Body
id: 92914273-eb38-4a54-9c5d-03db8d24fba7
name: BloodHound Attack Path Finding - Legacy SID History on Tier Zero Objects
description: BloodHound Attack Path Finding - Legacy SID History 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 | where Finding == "T0HasSIDHistory"
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 == "T0HasSIDHistory"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"T0HasSIDHistory" |
Conditional Access - A Conditional Access policy was deleted
#A Conditional Access policy was deleted from Entra ID.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Azure | Delete conditional access policy |
Rules Detecting the Same Action
These rules filter on the same operation.
- Azure MFA Disabled (Panther)
- Azure Policy Violation Detected (Panther)
- CA Policy Removed by Non Approved Actor (Sigma)
- CA Policy Updated by Non Approved Actor (Sigma)
- Certificate-Based Authentication Enabled (Sigma)
- Changes to Device Registration Policy (Sigma)
- Conditional Access - A Conditional Access app exclusion has changed (Kusto)
- Conditional Access - A Conditional Access Device platforms condition has changed (the Device platforms condition can be spoofed) (Kusto)
Rule Body
id: 2e96fa64-ac4d-4c92-b79e-e9c54b5d8230
name: Conditional Access - A Conditional Access policy was deleted
version: 1.0.1
kind: Scheduled
description: A Conditional Access policy was deleted from Entra ID.
severity: Low
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1562.007
query: |+
// A Conditional Access policy was deleted.
AuditLogs
| where OperationName in ("Delete conditional access policy")
| extend modifiedBy = tostring(InitiatedBy.user.userPrincipalName)
| extend accountName = tostring(split(modifiedBy, "@")[0])
| extend upnSuffix = tostring(split(modifiedBy, "@")[1])
| project
TimeGenerated,
OperationName,
policy = TargetResources[0].displayName,
modifiedBy,
accountName,
upnSuffix,
result = Result,
oldPolicy = TargetResources[0].modifiedProperties[0].oldValue
| order by TimeGenerated desc
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: accountName
- identifier: UPNSuffix
columnName: upnSuffix
suppressionEnabled: false
suppressionDuration: 5h
eventGroupingSettings:
aggregationKind: AlertPerResult
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT1H
matchingMethod: AllEntities
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
Stages and Predicates
Stage 1: source
AuditLogs
Stage 2: where
| where OperationName in ("Delete conditional access policy")
Stage 3: extend (3 consecutive steps)
| extend modifiedBy = tostring(InitiatedBy.user.userPrincipalName)
| extend accountName = tostring(split(modifiedBy, "@")[0])
| extend upnSuffix = tostring(split(modifiedBy, "@")[1])
Stage 4: project
| project
TimeGenerated,
OperationName,
policy = TargetResources[0].displayName,
modifiedBy,
accountName,
upnSuffix,
result = Result,
oldPolicy = TargetResources[0].modifiedProperties[0].oldValue
Stage 5: sort
| order by TimeGenerated desc
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
OperationName | in |
| field:"OperationName" kind:in value:"Delete conditional access policy" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
OperationName | project |
TimeGenerated | project |
accountName | project |
modifiedBy | project |
oldPolicy | project |
policy | project |
result | project |
upnSuffix | project |
Copilot - Jailbreak Attempt Detected
#'Detects jailbreak attempts in Copilot interactions where users are trying to bypass Copilot guardrails and security controls. This rule identifies prompt injection and LLM abuse scenarios that could lead to initial access, credential access, or system impact.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Credential Access | |
| Impact |
Rule Body
id: e5f6a7b8-c9d0-41e2-f3a4-b5c6d7e8f9a0
name: Copilot - Jailbreak Attempt Detected
description: |
'Detects jailbreak attempts in Copilot interactions where users are trying to bypass Copilot guardrails and security controls.
This rule identifies prompt injection and LLM abuse scenarios that could lead to initial access, credential access, or system impact.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: MicrosoftCopilot
dataTypes:
- CopilotActivity
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- CredentialAccess
- Impact
relevantTechniques:
- T1078
- T1110
- T1565
query: |
CopilotActivity
| where RecordType == "CopilotInteraction"
| where LLMEventData has "JailbreakDetected"
| extend Data = parse_json(LLMEventData)
| extend Jailbreak = tostring(Data.Messages[0].JailbreakDetected)
| where Jailbreak == "true"
| project TimeGenerated, ActorName, AIModelName, Jailbreak
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: ActorName
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
CopilotActivity
Stage 2: where
| where RecordType == "CopilotInteraction"
Stage 3: where
| where LLMEventData has "JailbreakDetected"
Stage 4: extend
| extend Data = parse_json(LLMEventData)
Stage 5: extend
| extend Jailbreak = tostring(Data.Messages[0].JailbreakDetected)
Stage 6: where
| where Jailbreak == "true"
Stage 7: project
| project TimeGenerated, ActorName, AIModelName, Jailbreak
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Jailbreak | eq |
| field:"Jailbreak" kind:eq value:"true" |
LLMEventData | match |
| field:"LLMEventData" kind:match value:"JailbreakDetected" |
RecordType | eq |
| field:"RecordType" kind:eq value:"CopilotInteraction" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AIModelName | project |
ActorName | project |
Jailbreak | project |
TimeGenerated | project |
Corelight - Possible Webshell (Rare PUT or POST)
#'Detects rare post requests to a single webserver location.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Rule Body
id: db662e49-6e34-4d10-9d3c-5d04b5479658
name: Corelight - Possible Webshell (Rare PUT or POST)
description: |
'Detects rare post requests to a single webserver location.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Corelight
dataTypes:
- corelight_http
- corelight_http_red
- corelight_http2
- corelight_http_agg
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1505
query: |
let threshold = 3;
union isfuzzy=true corelight_http, corelight_http_agg
| where method in~ ('POST', 'PUT')
| where toint(status_code) !between (400 .. 499)
| where request_body_len != 0 or response_body_len != 0
| extend fe = extract(@'.*(\.\w+)$', 1, uri)
| where fe in~ ('.aspx', '.asp', '.php', '.jsp', '.jspx', '.war', '.ashx', '.asmx', '.ascx', '.asx', '.cshtml', '.cfm', '.cfc', '.cfml', '.wss', '.do', '.action', '.pl', '.plx', '.pm', '.xs', '.t', '.pod', '.php-s', '.pht', '.phar', '.phps', '.php7', '.php5', '.php4', '.php3', '.phtml', '.py', '.rb', '.rhtml', '.cgi', '.dll', '.ayws', '.cgi', '.erb', '.rjs', '.hta', '.htc', '.cs', '.kt', '.lua', '.vbhtml')
| summarize count() by uri, id_orig_h
| where count_ < threshold
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: id_orig_h
version: 2.2.0
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 3;
Union isfuzzy=true (2 Sources)
Each leg below queries one source; the rule matches if any leg does. Sources: corelight_http, corelight_http_agg
Leg 1: corelight_http
Leg 2: corelight_http_agg
Applied to the Combined Result
| where method in~ ('POST', 'PUT') | where toint(status_code) !between (400 .. 499) | where request_body_len != 0 or response_body_len != 0 | extend fe = extract(@'.*(\.\w+)$', 1, uri) | where fe in~ ('.aspx', '.asp', '.php', '.jsp', '.jspx', '.war', '.ashx', '.asmx', '.ascx', '.asx', '.cshtml', '.cfm', '.cfc', '.cfml', '.wss', '.do', '.action', '.pl', '.plx', '.pm', '.xs', '.t', '.pod', '.php-s', '.pht', '.phar', '.phps', '.php7', '.php5', '.php4', '.php3', '.phtml', '.py', '.rb', '.rhtml', '.cgi', '.dll', '.ayws', '.cgi', '.erb', '.rjs', '.hta', '.htc', '.cs', '.kt', '.lua', '.vbhtml') | summarize count() by uri, id_orig_h | where count_ < threshold
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
count_ | lt |
| field:"count_" kind:lt value:"3" |
fe | in |
| field:"fe" kind:in |
method | in |
| field:"method" kind:in |
request_body_len | ne |
| field:"request_body_len" kind:ne value:"0" |
response_body_len | ne |
| field:"response_body_len" kind:ne value:"0" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
status_code | ge | 400 | excludes:status_code field:"status_code" value:"400" |
status_code | le | 499 | excludes:status_code field:"status_code" value:"499" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
id_orig_h | summarize |
uri | summarize |
Cyble Vision Alerts SSL Certificate Expiry
#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.
'Generates Microsoft Sentinel incidents for SSL certificates nearing expiry as detected by Cyble. These alerts help identify certificate hygiene risks that may lead to service disruption or security issues. Severity is normalized using MappedSeverity for downstream automation.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Impact |
Rule Body
id: A667D635-D2A7-47E7-8827-8FB243AF2AFD
name: Cyble Vision Alerts SSL Certificate Expiry
description: |
'Generates Microsoft Sentinel incidents for SSL certificates nearing expiry as detected by Cyble. These alerts help identify certificate hygiene risks that may lead to service disruption or security issues. Severity is normalized using MappedSeverity for downstream automation.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
dataTypes:
- CybleVisionAlerts_CL
enabled: true
queryfrequency: 30m
queryPeriod: 30m
query: |
Alerts_ssl_expiry
| where Service == "ssl_expiry"
| extend MappedSeverity = Severity
triggerOperator: GreaterThan
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- InitialAccess
- Impact
relevantTechniques:
- T1190
- T1499
entityMappings:
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: SS_Asset
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: SS_Asset
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: SS_Port
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
alertDetailsOverride:
alertDisplayNameFormat: SSL Certificate Expiry {{DC_ServerName}}
alertDescriptionFormat: ""
customDetails:
MappedSeverity: Severity
Status: Status
AlertID: AlertID
Service: Service
Asset: SS_Asset
ExpiryDate: SS_Expiry
DaysToExpiry: SS_DaysUntilExpiry
CertificateTitle: SS_Title
Port: SS_Port
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Alerts_ssl_expiry
Stage 2: where
| where Service == "ssl_expiry"
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:"ssl_expiry" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
MappedSeverity | extend |
CYFIRMA - High severity File Hash Indicators with Monitor Action and Malware
#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 KQL query retrieves file hash indicators (MD5, SHA1, SHA256) from the CyfirmaIndicators_CL table within the last 5 minutes. It filters records with a confidence score of 80 or higher, containing file hash patterns, a recommended action of 'Monitor', and roles marked as 'Malware'. Extracted hashes and key threat intelligence details are projected for monitoring and investigation."
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Stealth | |
| Impact |
Rule Body
id: c919c911-8b01-44f8-9c3b-60b1edfc417f
name: CYFIRMA - High severity File Hash Indicators with Monitor Action and Malware
description: |
"This KQL query retrieves file hash indicators (MD5, SHA1, SHA256) from the CyfirmaIndicators_CL table within the last 5 minutes.
It filters records with a confidence score of 80 or higher, containing file hash patterns, a recommended action of 'Monitor', and roles marked as 'Malware'.
Extracted hashes and key threat intelligence details are projected for monitoring and investigation."
version: 1.0.1
kind: Scheduled
severity: High
enabled: false
requiredDataConnectors:
- connectorId: CyfirmaCyberIntelligenceDC
dataTypes:
- CyfirmaIndicators_CL
query: |
// File Hash Indicators with Monitor Action and Malware
let timeFrame = 5m;
CyfirmaIndicators_CL
| where ConfidenceScore >= 80
and TimeGenerated between (ago(timeFrame) .. now())
and pattern contains 'file:hashes' and RecommendedActions has 'Monitor' and (Roles contains 'Malware')
| extend MD5 = extract(@"file:hashes\.md5\s*=\s*'([a-fA-F0-9]{32})'", 1, pattern)
| extend SHA1 = extract(@"file:hashes\.'SHA-1'\s*=\s*'([a-fA-F0-9]{40})'", 1, pattern)
| extend SHA256 = extract(@"file:hashes\.'SHA-256'\s*=\s*'([a-fA-F0-9]{64})'", 1, pattern)
| extend
Algo_MD5='md5',
Algo_SHA1= 'SHA1',
Algo_SHA256='SHA256',
ProviderName = 'CYFIRMA',
ProductName = 'DeCYFIR/DeTCT'
| project
MD5,
Algo_MD5,
SHA1,
Algo_SHA1,
SHA256,
Algo_SHA256,
ThreatActors,
Sources,
RecommendedActions,
Roles,
Country,
name,
Description,
ConfidenceScore,
SecurityVendors,
IndicatorID,
created,
modified,
valid_from,
Tags,
ThreatType,
TimeGenerated,
ProductName,
ProviderName
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: 5m
suppressionEnabled: true
tactics:
- DefenseEvasion
- InitialAccess
- Impact
- Execution
relevantTechniques:
- T1027
- T1486
- T1204
- T1485
- T1218
- T1566.001
alertDetailsOverride:
alertDisplayNameFormat: "High-Confidence File Hash Indicators with Monitor Action and Malware - {{name}} "
alertDescriptionFormat: "{{Description}} - {{name}} "
alertDynamicProperties:
- alertProperty: ProviderName
value: ProviderName
- alertProperty: ProductName
value: ProductName
customDetails:
ThreatActors: ThreatActors
Sources: Sources
RecommendedActions: RecommendedActions
Roles: Roles
Country: Country
Description: Description
ConfidenceScore: ConfidenceScore
SecurityVendors: SecurityVendors
IndicatorID: IndicatorID
created: created
modified: modified
valid_from: valid_from
Tags: Tags
ThreatType: ThreatType
TimeGenerated: TimeGenerated
entityMappings:
- entityType: FileHash
fieldMappings:
- identifier: Algorithm
columnName: Algo_MD5
- identifier: Value
columnName: MD5
- entityType: FileHash
fieldMappings:
- identifier: Algorithm
columnName: Algo_SHA1
- identifier: Value
columnName: SHA1
- entityType: FileHash
fieldMappings:
- identifier: Algorithm
columnName: Algo_SHA256
- identifier: Value
columnName: SHA256
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
Stages and Predicates
Parameters
let timeFrame = 5m;
Stage 1: source
CyfirmaIndicators_CL
Stage 2: where
| where ConfidenceScore >= 80
and TimeGenerated between (ago(timeFrame) .. now())
and pattern contains 'file:hashes' and RecommendedActions has 'Monitor' and (Roles contains 'Malware')
Stage 3: extend (4 consecutive steps)
| extend MD5 = extract(@"file:hashes\.md5\s*=\s*'([a-fA-F0-9]{32})'", 1, pattern)
| extend SHA1 = extract(@"file:hashes\.'SHA-1'\s*=\s*'([a-fA-F0-9]{40})'", 1, pattern)
| extend SHA256 = extract(@"file:hashes\.'SHA-256'\s*=\s*'([a-fA-F0-9]{64})'", 1, pattern)
| extend
Algo_MD5='md5',
Algo_SHA1= 'SHA1',
Algo_SHA256='SHA256',
ProviderName = 'CYFIRMA',
ProductName = 'DeCYFIR/DeTCT'
Stage 4: project
| project
MD5,
Algo_MD5,
SHA1,
Algo_SHA1,
SHA256,
Algo_SHA256,
ThreatActors,
Sources,
RecommendedActions,
Roles,
Country,
name,
Description,
ConfidenceScore,
SecurityVendors,
IndicatorID,
created,
modified,
valid_from,
Tags,
ThreatType,
TimeGenerated,
ProductName,
ProviderName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ConfidenceScore | ge |
| field:"ConfidenceScore" kind:ge value:"80" |
RecommendedActions | match |
| field:"RecommendedActions" kind:match value:"Monitor" |
Roles | contains |
| field:"Roles" kind:contains value:"Malware" |
pattern | contains |
| field:"pattern" kind:contains value:"file:hashes" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Algo_MD5 | project |
Algo_SHA1 | project |
Algo_SHA256 | project |
ConfidenceScore | project |
Country | project |
Description | project |
IndicatorID | project |
MD5 | project |
ProductName | project |
ProviderName | project |
RecommendedActions | project |
Roles | project |
SHA1 | project |
SHA256 | project |
SecurityVendors | project |
Sources | project |
Tags | project |
ThreatActors | project |
ThreatType | project |
TimeGenerated | project |
created | project |
modified | project |
name | project |
valid_from | project |
Detect Unsigned executable launch from scheduled task
#Persistence via Scheduled Tasks is a well-known technique used by adversaries to make sure their malware programs keep running an the compromised device. With this detection rule, you can search for unknown executables being launched from scheduled tasks. > [!WARNING] > This detection rule is the base for the detection. You will need to add environment specific finetuning in order to limit the BP detections on legitimate processes
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Persistence | |
| Privilege Escalation |
References
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceProcessEvents action any: Process activity |
Rule Body
let scheduled_binaries = (
DeviceProcessEvents
| where ActionType !contains "aggregated"
| where Timestamp > ago(1h)
| where InitiatingProcessCommandLine == "svchost.exe -k netsvcs -p -s Schedule"
| distinct SHA1
);
let untrusted_binaries = (
scheduled_binaries
| join kind=leftanti (
DeviceFileCertificateInfo
| where Timestamp > ago(1h)
| summarize max_trusted=max(IsTrusted) by SHA1
| where max_trusted==1
) on SHA1
);
untrusted_binaries
| invoke FileProfile(SHA1,1000)
| where IsCertificateValid != 1 // Exclude signed binaries
| where (isnotempty(GlobalPrevalence) and GlobalPrevalence < 1000)
| join (
DeviceProcessEvents
| where ActionType !contains "aggregated"
| where InitiatingProcessCommandLine == "svchost.exe -k netsvcs -p -s Schedule"
) on SHA1
Stages and Predicates
let scheduled_binaries and let untrusted_binaries are inlined into the numbered stages below.
Stage 1: source
let scheduled_binaries
Stage 2: source
let untrusted_binaries
Stage 3: source
DeviceProcessEvents
Stage 4: where
where not (ActionType contains "aggregated")
Stage 5: where
where Timestamp > ago(3600s)
Stage 6: where
where InitiatingProcessCommandLine == "svchost.exe -k netsvcs -p -s Schedule"
Stage 7: distinct
distinct SHA1
Stage 8: join (negated)
join kind=leftanti (DeviceFileCertificateInfo) on SHA1
Stage 9: invoke
invoke
Stage 10: where
where IsCertificateValid != 1
Stage 11: where
where isnotempty(GlobalPrevalence) and GlobalPrevalence < 1000
Stage 12: join
join (DeviceProcessEvents) on SHA1
Stage 13: summarize aggregation inside the join branch
summarize by SHA1
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
GlobalPrevalence | is_not_null | field:"GlobalPrevalence" kind:is_not_null | |
GlobalPrevalence | lt |
| field:"GlobalPrevalence" kind:lt value:"1000" |
InitiatingProcessCommandLine | eq |
| field:"InitiatingProcessCommandLine" kind:eq value:"svchost.exe -k netsvcs -p -s Schedule" |
IsCertificateValid | ne |
| field:"IsCertificateValid" kind:ne value:"1" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
ActionType | contains | aggregated | excludes:ActionType field:"ActionType" value:"aggregated" |
max_trusted | eq | 1 | excludes:max_trusted field:"max_trusted" value:"1" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SHA1 | summarize |
Jira - Workflow scheme copied
#'Detects when workflow scheme was copied.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Rule Body
id: 398aa0ca-45a2-4f79-bc21-ee583bbb63bc
name: Jira - Workflow scheme copied
description: |
'Detects when workflow scheme was copied.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: JiraAuditAPI
dataTypes:
- JiraAudit
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
relevantTechniques:
- T1213
query: |
JiraAudit
| where EventMessage =~ 'Workflow scheme copied'
| extend workflow = todynamic(AssociatedItems)[0]['name']
| project EventCreationTime, ObjectItemName, UserName, SrcIpAddr, workflow
| extend AccountCustomEntity = UserName, IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
JiraAudit
Stage 2: where
| where EventMessage =~ 'Workflow scheme copied'
Stage 3: extend
| extend workflow = todynamic(AssociatedItems)[0]['name']
Stage 4: project
| project EventCreationTime, ObjectItemName, UserName, SrcIpAddr, workflow
Stage 5: extend
| extend AccountCustomEntity = UserName, IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventMessage | eq |
| field:"EventMessage" kind:eq value:"Workflow scheme copied" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EventCreationTime | project |
ObjectItemName | project |
SrcIpAddr | project |
UserName | project |
workflow | project |
AccountCustomEntity | extend |
IPCustomEntity | extend |
Large number of AD objects accessed by user
#This query detects a user accessing a large number of Group and User objects from Active Directory which is outside the baseline of normal behavior for that particular user.
Known False Positives
- There are legitimate use-cases for downloading large number of AD objects. For example, an administrator might run a script to extract all users as part of certain migration activities.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery | |
| Collection |
References
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 4662: An operation was performed on an object. |
Rule Body
let timeframe = 2*1d;
let RuleId = "0108";
let DedupFields = dynamic(["TimeGenerated"]);
let lookback_days=14d; // Look back this many days to calculate baseline for maximum number of objects accessed per day.
let min_suspicious_count = 20000; // Only consider users looking up at least this many objects during the timeframe.
let suspicious_factor = 50; // Consider as suspicious if requesting more than suspicious_factor*daily maximum in lookback period.
let ADObjectAccess=(
SecurityEvent
| where EventID == 4662
| where not(Account endswith "$")
| where ObjectType in~ (
"%{bf967aba-0de6-11d0-a285-00aa003049e2}", // User.
"%{bf967a9c-0de6-11d0-a285-00aa003049e2}" // Group.
)
);
let AccessBaseline=(
ADObjectAccess
| where TimeGenerated <= ago(timeframe)
| where TimeGenerated >= ago(timeframe + lookback_days)
| summarize BaselineObjectCount=count() by Account, bin(TimeGenerated,1d)
| summarize PreviousMaxObjectPerDay=max(BaselineObjectCount) by Account
);
ADObjectAccess
| where ingestion_time() >= ago(timeframe)
| summarize ObjectCount=count(),TimeGenerated=min(TimeGenerated) by Account
| where ObjectCount > min_suspicious_count
| join kind=leftouter AccessBaseline on Account
| extend PreviousMaxObjectPerDay=coalesce(PreviousMaxObjectPerDay,0)
// Calculate what is considered a suspicious number for the given user.
| extend SuspiciousThreshold=max_of(PreviousMaxObjectPerDay*suspicious_factor,min_suspicious_count)
| where ObjectCount > SuspiciousThreshold
| project TimeGenerated, Account, ObjectCount, PreviousMaxObjectPerDay, SuspiciousThreshold
| extend AccountName=iif(Account contains @"\",tostring(split(Account,@"\")[1]),Account),AccountDomain=iif(Account contains @"\",tostring(split(Account,@"\")[0]),"")
// Begin environment-specific filter.
// End environment-specific filter.
// Begin de-duplication logic.
| extend DedupFieldValues=pack_all()
| mv-apply e=DedupFields to typeof(string) on (
extend DedupValue=DedupFieldValues[tostring(e)]
| order by e // Sorting is required to ensure make_list is deterministic.
| summarize DedupValues=make_list(DedupValue)
)
| extend DedupEntity=strcat_array(DedupValues, "|")
| project-away DedupFieldValues, DedupValues
| join kind=leftanti (
SecurityAlert
| where AlertName has RuleId and ProviderName has "ASI"
| where TimeGenerated >= ago(timeframe)
| extend DedupEntity = tostring(parse_json(tostring(parse_json(ExtendedProperties)["Custom Details"])).DedupEntity[0])
| project DedupEntity
) on DedupEntity
// End de-duplication logic.
Stages and Predicates
Parameters
let timeframe = 2*1d;
let RuleId = "0108";
let DedupFields = dynamic(["TimeGenerated"]);
let lookback_days = 14d;
let min_suspicious_count = 20000;
let suspicious_factor = 50;
let ADObjectAccess is inlined into the numbered stages below.
Let Binding: AccessBaseline
let AccessBaseline = (
ADObjectAccess
| where TimeGenerated <= ago(timeframe)
| where TimeGenerated >= ago(timeframe + lookback_days)
| summarize BaselineObjectCount=count() by Account, bin(TimeGenerated,1d)
| summarize PreviousMaxObjectPerDay=max(BaselineObjectCount) by Account
);
Stages 1 to 6 define let ADObjectAccess as the rule's main pipeline source. Stages 7 to 20 use it.
Stage 1: source
let ADObjectAccess
Stage 2: source
let AccessBaseline
Stage 3: source
SecurityEvent
Stage 4: where
| where EventID == 4662
Stage 5: where
| where not(Account endswith "$")
Stage 6: where
| where ObjectType in~ (
"%{bf967aba-0de6-11d0-a285-00aa003049e2}",
"%{bf967a9c-0de6-11d0-a285-00aa003049e2}"
)
Stage 7: where
ADObjectAccess
| where ingestion_time() >= ago(timeframe)
Stage 8: summarize
| summarize ObjectCount=count(),TimeGenerated=min(TimeGenerated) by Account
Stage 9: where
| where ObjectCount > min_suspicious_count
Stage 10: join
| join kind=leftouter AccessBaseline on Account
Stage 11: extend
extend PreviousMaxObjectPerDay
Stage 12: extend
extend SuspiciousThreshold
Stage 13: where
where ObjectCount > SuspiciousThreshold
Stage 14: project
project Account, ObjectCount, PreviousMaxObjectPerDay, SuspiciousThreshold, TimeGenerated
Stage 15: extend
extend AccountDomain, AccountName
Stage 16: extend
extend DedupFieldValues
Stage 17: kusto:mv-apply
kusto:mv-apply
Stage 18: extend
extend DedupEntity
Stage 19: project-away
project-away DedupFieldValues, DedupValues
Stage 20: join (negated)
join kind=leftanti (SecurityAlert) on DedupEntity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventID | eq |
| field:"EventID" kind:eq value:"4662" |
ObjectCount | gt |
| field:"ObjectCount" kind:gt value:"20000" |
ObjectType | in |
| field:"ObjectType" kind:in |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Account | ends_with | $ | excludes:Account field:"Account" value:"$" |
AlertName | match | 0108 | excludes:AlertName field:"AlertName" value:"0108" |
ProviderName | match | ASI | excludes:ProviderName field:"ProviderName" value:"ASI" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account | project |
ObjectCount | project |
PreviousMaxObjectPerDay | project |
SuspiciousThreshold | project |
TimeGenerated | project |
AccountDomain | extend |
AccountName | extend |
DedupEntity | extend |
Microsoft Entra ID Hybrid Health AD FS Suspicious Application
#This detection uses AzureActivity logs (Administrative category) to identify a suspicious application adding a server instance to an Microsoft Entra ID Hybrid Health AD FS service or deleting the AD FS service instance. Usually the Microsoft Entra ID Connect Health Agent application with ID cf6d7e68-f018-4e0a-a7b3-126e053fb88d and ID cb1056e2-e479-49de-ae31-7812af012ed8 is used to perform those operations.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Lateral Movement |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
- Azure Active Directory Hybrid Health AD FS New Server (Sigma)
- Azure Active Directory Hybrid Health AD FS Service Delete (Sigma)
- Azure Subscription Permission Elevation Via AuditLogs (Sigma)
- Detect Custom Script or Run Command deployment by risky user (Kusto)
- Microsoft Entra ID Hybrid Health AD FS New Server (Kusto)
- Microsoft Entra ID Hybrid Health AD FS Service Delete (Kusto)
- NRT Microsoft Entra ID Hybrid Health AD FS New Server (Kusto)
Rule Body
id: d9938c3b-16f9-444d-bc22-ea9a9110e0fd
name: Microsoft Entra ID Hybrid Health AD FS Suspicious Application
description: |
'This detection uses AzureActivity logs (Administrative category) to identify a suspicious application adding a server instance to an Microsoft Entra ID Hybrid Health AD FS service or deleting the AD FS service instance.
Usually the Microsoft Entra ID Connect Health Agent application with ID cf6d7e68-f018-4e0a-a7b3-126e053fb88d and ID cb1056e2-e479-49de-ae31-7812af012ed8 is used to perform those operations.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AzureActivity
dataTypes:
- AzureActivity
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
- DefenseEvasion
relevantTechniques:
- T1528
- T1550
tags:
- SimuLand
query: |
// Microsoft Entra ID Connect Health Agent - cf6d7e68-f018-4e0a-a7b3-126e053fb88d
// Microsoft Entra ID Connect - cb1056e2-e479-49de-ae31-7812af012ed8
let appList = dynamic(['cf6d7e68-f018-4e0a-a7b3-126e053fb88d','cb1056e2-e479-49de-ae31-7812af012ed8']);
let operationNamesList = dynamic(['Microsoft.ADHybridHealthService/services/servicemembers/action','Microsoft.ADHybridHealthService/services/delete']);
AzureActivity
| where CategoryValue =~ 'Administrative'
| where ResourceProviderValue =~ 'Microsoft.ADHybridHealthService'
| where _ResourceId has 'AdFederationService'
| where OperationNameValue in~ (operationNamesList)
| extend claimsJson = parse_json(Claims)
| extend AppId = tostring(claimsJson.appid), AccountName = tostring(claimsJson.name), Name = tostring(split(Caller,'@',0)[0]), UPNSuffix = tostring(split(Caller,'@',1)[0])
| where AppId !in (appList)
| project-away claimsJson
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Caller
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: CallerIpAddress
version: 2.0.3
kind: Scheduled
Stages and Predicates
Parameters
let appList = dynamic(['cf6d7e68-f018-4e0a-a7b3-126e053fb88d','cb1056e2-e479-49de-ae31-7812af012ed8']);
let operationNamesList = dynamic(['Microsoft.ADHybridHealthService/services/servicemembers/action','Microsoft.ADHybridHealthService/services/delete']);
Stage 1: source
AzureActivity
Stage 2: where
| where CategoryValue =~ 'Administrative'
Stage 3: where
| where ResourceProviderValue =~ 'Microsoft.ADHybridHealthService'
Stage 4: where
| where _ResourceId has 'AdFederationService'
Stage 5: where
| where OperationNameValue in~ (operationNamesList)
Stage 6: extend
| extend claimsJson = parse_json(Claims)
Stage 7: extend
| extend AppId = tostring(claimsJson.appid), AccountName = tostring(claimsJson.name), Name = tostring(split(Caller,'@',0)[0]), UPNSuffix = tostring(split(Caller,'@',1)[0])
Stage 8: where
| where AppId !in (appList)
Stage 9: project-away
| project-away claimsJson
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CategoryValue | eq |
| field:"CategoryValue" kind:eq value:"Administrative" |
OperationNameValue | in |
| field:"azure_ad::operation_name_value" kind:in |
ResourceProviderValue | eq |
| field:"ResourceProviderValue" kind:eq value:"Microsoft.ADHybridHealthService" |
_ResourceId | match |
| field:"_ResourceId" kind:match value:"AdFederationService" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
AppId | in | cb1056e2-e479-49de-ae31-7812af012ed8, cf6d7e68-f018-4e0a-a7b3-126e053fb88d | excludes:AppId field:"AppId" value:"cb1056e2-e479-49de-ae31-7812af012ed8" field:"AppId" value:"cf6d7e68-f018-4e0a-a7b3-126e053fb88d" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | extend |
AppId | extend |
Name | extend |
UPNSuffix | extend |
New High Severity Vulnerability Detected Across Multiple Hosts
#'This creates an incident when a new high severity vulnerability is detected across multilple hosts'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 6116dc19-475a-4148-84b2-efe89c073e27
name: New High Severity Vulnerability Detected Across Multiple Hosts
description: |
'This creates an incident when a new high severity vulnerability is detected across multilple hosts'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: QualysVMLogsCCPDefinition
dataTypes:
- QualysHostDetection
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
query: |
let threshold = 10;
QualysHostDetection
| extend Status = tostring(Status), Vulnerability = tostring(QID), Severity = tostring(Severity)
| where Status =~ "New" and Severity == "5"
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(NetBios) by tostring(QID)
| where dcount_NetBios >= threshold
| extend timestamp = StartTime
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 10;
Stage 1: source
QualysHostDetection
Stage 2: extend
| extend Status = tostring(Status), Vulnerability = tostring(QID), Severity = tostring(Severity)
Stage 3: where
| where Status =~ "New" and Severity == "5"
Stage 4: summarize
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(NetBios) by tostring(QID)
Stage 5: where
| where dcount_NetBios >= threshold
Stage 6: extend
| extend timestamp = StartTime
Indicators
These rows show field, operator, and value matches.
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EndTime | summarize |
StartTime | summarize |
timestamp | extend |
TI Map URL Entity to DeviceNetworkEvents
#This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in DeviceNetworkEvents.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceNetworkEvents action any: Network activity |
Rule Body
id: 6ddbd892-a9be-47be-bab7-521241695bd6
name: TI Map URL Entity to DeviceNetworkEvents
description: |
'This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in DeviceNetworkEvents.'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceNetworkEvents
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let DeviceNetworkEvents_ = DeviceNetworkEvents
| where isnotempty(RemoteUrl)
| where TimeGenerated >= ago(dt_lookBack)
| where ActionType !has "ConnectionFailed"
| extend RemoteUrl = tolower(RemoteUrl)
| project-rename DeviceNetworkEvents_TimeGenerated = TimeGenerated;
let DeviceNetworkEventUrls = DeviceNetworkEvents_
| distinct Url = RemoteUrl
| summarize make_list(Url);
ThreatIntelligenceIndicator
| where isnotempty(Url)
| where TimeGenerated >= ago(ioc_lookBack)
| extend Url = tolower(Url)
| where Url in (DeviceNetworkEventUrls)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
| join kind=innerunique (DeviceNetworkEvents_) on $left.Url == $right.RemoteUrl
| where DeviceNetworkEvents_TimeGenerated < ExpirationDateTime
| summarize DeviceNetworkEvents_TimeGenerated = arg_max(DeviceNetworkEvents_TimeGenerated, *) by IndicatorId, Url
| project DeviceNetworkEvents_TimeGenerated, IndicatorId, Url, ConfidenceScore, Description, ThreatType, Tags, TrafficLightProtocolLevel, ActionType, DeviceId, DeviceName, InitiatingProcessAccountUpn, InitiatingProcessCommandLine, RemoteIP, RemotePort
| extend Name = tostring(split(InitiatingProcessAccountUpn, '@', 0)[0]), UPNSuffix = tostring(split(InitiatingProcessAccountUpn, '@', 1)[0])
| extend timestamp = DeviceNetworkEvents_TimeGenerated, UserPrincipalName = InitiatingProcessAccountUpn
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
- entityType: Process
fieldMappings:
- identifier: CommandLine
columnName: InitiatingProcessCommandLine
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
Let Binding: DeviceNetworkEvents_
let DeviceNetworkEvents_ = DeviceNetworkEvents
| where isnotempty(RemoteUrl)
| where TimeGenerated >= ago(dt_lookBack)
| where ActionType !has "ConnectionFailed"
| extend RemoteUrl = tolower(RemoteUrl)
| project-rename DeviceNetworkEvents_TimeGenerated = TimeGenerated;
Let Binding: DeviceNetworkEventUrls
let DeviceNetworkEventUrls = DeviceNetworkEvents_
| distinct Url = RemoteUrl
| summarize make_list(Url);
Stage 1: source
let DeviceNetworkEvents_
Stage 2: source
let DeviceNetworkEventUrls
Stage 3: source
ThreatIntelligenceIndicator
Stage 4: where
where isnotempty(Url)
Stage 5: where
where TimeGenerated >= ago(1209600s)
Stage 6: extend
extend Url
Stage 7: where
where Url == "DeviceNetworkEventUrls"
Stage 8: summarize
summarize LatestIndicatorTime by IndicatorId
Stage 9: where
where Active == true
Stage 10: where
where not (Description contains_cs "State: falsepos;") and not (Description contains_cs "State: inactive;")
Stage 11: join
join kind=innerunique (DeviceNetworkEvents_) on $left.Url, $right.RemoteUrl
Stage 12: where
where DeviceNetworkEvents_TimeGenerated < ExpirationDateTime
Stage 13: summarize
summarize DeviceNetworkEvents_TimeGenerated by IndicatorId, Url
Stage 14: project
project ActionType, ConfidenceScore, Description, DeviceId, DeviceName, DeviceNetworkEvents_TimeGenerated, IndicatorId, InitiatingProcessAccountUpn, InitiatingProcessCommandLine, RemoteIP, RemotePort, Tags, ThreatType, TrafficLightProtocolLevel, Url
Stage 15: extend
extend Name, UPNSuffix
Stage 16: extend
extend UserPrincipalName, timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Active | eq |
| field:"Active" kind:eq value:"true" |
RemoteUrl | is_not_null | field:"RemoteUrl" kind:is_not_null | |
Url | in |
| field:"Url" kind:in value:"DeviceNetworkEventUrls" |
Url | is_not_null | field:"Url" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Description | contains | State: falsepos; | excludes:Description field:"Description" value:"State: falsepos;" |
Description | contains | State: inactive; | excludes:Description field:"Description" value:"State: inactive;" |
ActionType | match | ConnectionFailed | excludes:ActionType field:"ActionType" value:"ConnectionFailed" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActionType | project |
ConfidenceScore | project |
Description | project |
DeviceId | project |
DeviceName | project |
DeviceNetworkEvents_TimeGenerated | project |
IndicatorId | project |
InitiatingProcessAccountUpn | project |
InitiatingProcessCommandLine | project |
RemoteIP | project |
RemotePort | project |
Tags | project |
ThreatType | project |
TrafficLightProtocolLevel | project |
Url | project |
Name | extend |
UPNSuffix | extend |
UserPrincipalName | extend |
timestamp | extend |
TI Map URL Entity to OfficeActivity Data [Deprecated]
#'This query is Deprecated as its filter conditions will never yield results. This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in OfficeActivity data.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 36a9c9e5-3dc1-4ed9-afaa-1d13617bfc2b
name: TI Map URL Entity to OfficeActivity Data [Deprecated]
description: |
'This query is Deprecated as its filter conditions will never yield results. This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in OfficeActivity data.'
severity: Medium
requiredDataConnectors:
- connectorId: Office365
dataTypes:
- OfficeActivity
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h;
// let ioc_lookBack = 14d;
// ThreatIntelligenceIndicator
// // Picking up only IOC's that contain the entities we want
// | where isnotempty(Url)
// | where TimeGenerated >= ago(ioc_lookBack)
// | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
// | where Active == true and ExpirationDateTime > now()
// // using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
// | join kind=innerunique (
// OfficeActivity
// | where TimeGenerated >= ago(dt_lookBack)
// //Extract the Url from a number of potential fields
// | extend Url = iif(OfficeWorkload == "AzureActiveDirectory",extract("(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)", 1,ModifiedProperties),tostring(parse_json(ModifiedProperties)[12].NewValue))
// | where isnotempty(Url)
// // Ensure we get a clean URL
// | extend Url = tostring(split(Url, ';')[0])
// | extend OfficeActivity_TimeGenerated = TimeGenerated
// // Project a single user identity that we can use for entity mapping
// | extend User = iif(isnotempty(UserId), UserId, iif(isnotempty(Actor), tostring(parse_json(Actor)[0].ID), tostring(parse_json(Parameters)[0].Value)))
// ) on Url
// | where OfficeActivity_TimeGenerated < ExpirationDateTime
// | summarize OfficeActivity_TimeGenerated = arg_max(OfficeActivity_TimeGenerated, *) by IndicatorId, Url
// | project OfficeActivity_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Operation,
// UserType, OfficeWorkload, Parameters, Url, User
// | extend timestamp = OfficeActivity_TimeGenerated, Name = tostring(split(User, '@', 0)[0]), UPNSuffix = tostring(split(User, '@', 1)[0])
datatable() []
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: User
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
version: 1.2.9
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
Stage 1: source
datatable() []
UniFi Site Manager: ISP High Latency
#Identifies when ISP latency exceeds acceptable thresholds, impacting VoIP quality, video conferencing, and overall application performance.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 77d8f6d0-b45c-ea6e-7c58-daac194a095f
name: "UniFi Site Manager: ISP High Latency"
description: |
Identifies when ISP latency exceeds acceptable thresholds, impacting VoIP quality, video conferencing, and overall application performance.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: UniFiSiteManagerConnectorDefinition
dataTypes:
- Unifi_SiteManager_ISPMetrics_CL
queryFrequency: 15m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1498
- T1499
subTechniques:
- T1498.001
- T1499.002
query: |
// UniFi ISP High Latency Detection
let AvgLatencyThreshold = 200;
let MaxLatencyThreshold = 1000;
let SustainedPeriodCount = 2;
Unifi_SiteManager_ISPMetrics_CL
| where TimeGenerated > ago(30m)
| mv-expand period = Periods
| extend
metricTime = todatetime(period.metricTime),
avgLatency = toint(period.data.wan.avgLatency),
maxLatency = toint(period.data.wan.maxLatency),
ispName = tostring(period.data.wan.ispName),
ispAsn = tostring(period.data.wan.ispAsn)
// De-duplicate Periods: each poll returns the same hour buckets, so collapse to latest value per metricTime
| summarize arg_max(TimeGenerated, avgLatency, maxLatency, ispAsn) by tostring(SiteId), ispName, metricTime
| where metricTime > ago(30m)
| where avgLatency > AvgLatencyThreshold or maxLatency > MaxLatencyThreshold
| summarize
EventCount = count(),
AvgLatency = avg(avgLatency),
MaxLatency = max(maxLatency),
FirstSeen = min(metricTime),
LastSeen = max(metricTime)
by SiteId, ispName, ispAsn
| where EventCount >= SustainedPeriodCount
| extend
TimeGenerated = now(),
AvgLatencyRounded = round(AvgLatency, 1)
| project
TimeGenerated,
SiteId = SiteId,
ISPName = ispName,
ISPAsn = ispAsn,
AvgLatencyMs = AvgLatencyRounded,
MaxLatencyMs = MaxLatency,
EventCount,
FirstSeen,
LastSeen
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: SiteId
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: ISPName
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: PT4H
matchingMethod: AllEntities
kind: Scheduled
version: 1.0.1
Stages and Predicates
Parameters
let AvgLatencyThreshold = 200;
let MaxLatencyThreshold = 1000;
let SustainedPeriodCount = 2;
Stage 1: source
Unifi_SiteManager_ISPMetrics_CL
Stage 2: where
| where TimeGenerated > ago(30m)
Stage 3: mv-expand
| mv-expand period = Periods
Stage 4: extend
| extend
metricTime = todatetime(period.metricTime),
avgLatency = toint(period.data.wan.avgLatency),
maxLatency = toint(period.data.wan.maxLatency),
ispName = tostring(period.data.wan.ispName),
ispAsn = tostring(period.data.wan.ispAsn)
Stage 5: summarize
| summarize arg_max(TimeGenerated, avgLatency, maxLatency, ispAsn) by tostring(SiteId), ispName, metricTime
Stage 6: where
| where metricTime > ago(30m)
Stage 7: where
| where avgLatency > AvgLatencyThreshold or maxLatency > MaxLatencyThreshold
Stage 8: summarize
| summarize
EventCount = count(),
AvgLatency = avg(avgLatency),
MaxLatency = max(maxLatency),
FirstSeen = min(metricTime),
LastSeen = max(metricTime)
by SiteId, ispName, ispAsn
Stage 9: where
| where EventCount >= SustainedPeriodCount
Stage 10: extend
| extend
TimeGenerated = now(),
AvgLatencyRounded = round(AvgLatency, 1)
Stage 11: project
| project
TimeGenerated,
SiteId = SiteId,
ISPName = ispName,
ISPAsn = ispAsn,
AvgLatencyMs = AvgLatencyRounded,
MaxLatencyMs = MaxLatency,
EventCount,
FirstSeen,
LastSeen
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventCount | ge |
| field:"EventCount" kind:ge value:"2" |
avgLatency | gt |
| field:"avgLatency" kind:gt value:"200" |
maxLatency | gt |
| field:"maxLatency" kind:gt value:"1000" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AvgLatencyMs | project |
EventCount | project |
FirstSeen | project |
ISPAsn | project |
ISPName | project |
LastSeen | project |
MaxLatencyMs | project |
SiteId | project |
TimeGenerated | project |
Vulerabilities
#This query searches for all the vulerabilities that RidgeBot identified
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Privilege Escalation |
Rule Body
id: d096643d-6789-4c74-8893-dd3fc8a94069
name: Vulerabilities
description: |
This query searches for all the vulerabilities that RidgeBot identified
severity: High
status: Available
requiredDataConnectors:
- connectorId: RidgeBotDataConnector
dataTypes:
- CommonSecurityLog
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 12h
queryPeriod: 12h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
- InitialAccess
- PrivilegeEscalation
relevantTechniques:
- T1189
- T1059
- T1053
- T1548
query: |
CommonSecurityLog
| where DeviceVendor == "RidgeSecurity"
| where DeviceEventClassID startswith "40"
| order by LogSeverity
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: DeviceVendor
- entityType: IP
fieldMappings:
- identifier: Address
columnName: Computer
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
CommonSecurityLog
Stage 2: where
| where DeviceVendor == "RidgeSecurity"
Stage 3: where
| where DeviceEventClassID startswith "40"
Stage 4: sort
| order by LogSeverity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DeviceEventClassID | starts_with |
| field:"DeviceEventClassID" kind:starts_with value:"40" |
DeviceVendor | eq |
| field:"DeviceVendor" kind:eq value:"RidgeSecurity" |