Detection rules › Kusto
Kusto rules: 0c
ApexOne - Commands in Url
#'Detects commands in Url.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 4a9a5900-67b7-11ec-90d6-0242ac120003
name: ApexOne - Commands in Url
description: |
'Detects commands in Url.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
- T1133
query: |
TMApexOneEvent
| where EventSubType in ("WB:7", "CnC:Block", "VAD", "700106")
| where UrlOriginal contains 'cat%20/etc/passwd' or UrlOriginal contains '/etc/passwd' or UrlOriginal contains 'ping -i' or UrlOriginal contains '/usr/bin/id(' or UrlOriginal contains '%2f%75%73%72%2f%62%69%6e%2f%69%64' or UrlOriginal contains 'phpinfo()' or UrlOriginal contains '%70%68%70%69%6e%66%6f%28%29' or UrlOriginal contains ';id' or UrlOriginal contains '%3b%69%64' or UrlOriginal contains '/bin/bash -c' or UrlOriginal contains '%2f%62%69%6e%2f%62%61%73%68%20%2d%63%27' or UrlOriginal contains '/bin/bash' or UrlOriginal contains '%2f%62%69%6e%2f%62%61%73%68' or UrlOriginal contains 'sleep(' or UrlOriginal contains '%73%6c%65%65%70%28' or UrlOriginal contains 'curl' or UrlOriginal contains '%63%75%72%6c' or UrlOriginal contains '&dir' or UrlOriginal contains '%26%64%69%72' or UrlOriginal contains '& dir' or UrlOriginal =~ '%26%20%64%69%72' or UrlOriginal contains '<script>' or UrlOriginal contains '%3c%73%63%72%69%70%74%3e' or UrlOriginal contains 'eval(' or UrlOriginal contains '%65%76%61%6c%28' or UrlOriginal contains 'exec(' or UrlOriginal contains '%65%78%65%63%28' or UrlOriginal contains 'whoami' or UrlOriginal contains '%77%68%6f%61%6d%69' or UrlOriginal contains 'wget' or UrlOriginal contains 'python' or UrlOriginal contains 'gcc' or UrlOriginal contains 'uname' or UrlOriginal contains 'systeminfo' or UrlOriginal contains '%77%67%65%74' or UrlOriginal contains '%70%79%74%68%6f%6e' or UrlOriginal contains '%75%6e%61%6d%65' or UrlOriginal =~ '%73%79%73%74%65%6d%69%6e%66%6f'
| extend UrlCustomEntity = UrlOriginal
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: UrlCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Stage 1: source
TMApexOneEvent
Stage 2: where
| where EventSubType in ("WB:7", "CnC:Block", "VAD", "700106")
Stage 3: where
| where UrlOriginal contains 'cat%20/etc/passwd' or UrlOriginal contains '/etc/passwd' or UrlOriginal contains 'ping -i' or UrlOriginal contains '/usr/bin/id(' or UrlOriginal contains '%2f%75%73%72%2f%62%69%6e%2f%69%64' or UrlOriginal contains 'phpinfo()' or UrlOriginal contains '%70%68%70%69%6e%66%6f%28%29' or UrlOriginal contains ';id' or UrlOriginal contains '%3b%69%64' or UrlOriginal contains '/bin/bash -c' or UrlOriginal contains '%2f%62%69%6e%2f%62%61%73%68%20%2d%63%27' or UrlOriginal contains '/bin/bash' or UrlOriginal contains '%2f%62%69%6e%2f%62%61%73%68' or UrlOriginal contains 'sleep(' or UrlOriginal contains '%73%6c%65%65%70%28' or UrlOriginal contains 'curl' or UrlOriginal contains '%63%75%72%6c' or UrlOriginal contains '&dir' or UrlOriginal contains '%26%64%69%72' or UrlOriginal contains '& dir' or UrlOriginal =~ '%26%20%64%69%72' or UrlOriginal contains '<script>' or UrlOriginal contains '%3c%73%63%72%69%70%74%3e' or UrlOriginal contains 'eval(' or UrlOriginal contains '%65%76%61%6c%28' or UrlOriginal contains 'exec(' or UrlOriginal contains '%65%78%65%63%28' or UrlOriginal contains 'whoami' or UrlOriginal contains '%77%68%6f%61%6d%69' or UrlOriginal contains 'wget' or UrlOriginal contains 'python' or UrlOriginal contains 'gcc' or UrlOriginal contains 'uname' or UrlOriginal contains 'systeminfo' or UrlOriginal contains '%77%67%65%74' or UrlOriginal contains '%70%79%74%68%6f%6e' or UrlOriginal contains '%75%6e%61%6d%65' or UrlOriginal =~ '%73%79%73%74%65%6d%69%6e%66%6f'
Stage 4: extend
| extend UrlCustomEntity = UrlOriginal
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventSubType | in |
| field:"EventSubType" kind:in |
UrlOriginal | contains |
| field:"UrlOriginal" kind:contains |
UrlOriginal | eq |
| field:"UrlOriginal" kind:eq |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
UrlCustomEntity | extend |
AWSCloudTrail - AWS GuardDuty detector disabled or suspended
#Identifies AWS GuardDuty detectors being disabled or suspended. This behavior can indicate defense evasion and should be validated with the initiating identity and approved administrative activity.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
id: 9da99021-d318-4711-a78a-6dea76129b3a
name: AWSCloudTrail - AWS GuardDuty detector disabled or suspended
description: |
Identifies AWS GuardDuty detectors being disabled or suspended. This behavior can indicate defense evasion and should be validated with the initiating identity and approved administrative activity.
severity: High
status: Available
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1562
query: |
AWSCloudTrail
| where (EventName == "DeleteDetector" and isempty(ErrorCode) and isempty( ErrorMessage)) or (EventName == "UpdateDetector" and tostring(parse_json(RequestParameters).enable) == "false" and isempty(ErrorCode) and isempty( ErrorMessage))
| 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
UserIdentityArn: UserIdentityArn
UserName: UserName
SourceIpAddress: SourceIpAddress
alertDetailsOverride:
alertDisplayNameFormat: 'AWS GuardDuty detector disabled or suspended by {{AccountName}}'
alertDescriptionFormat: 'AWS GuardDuty detector activity {{EventName}} was detected for {{AccountName}} from {{SourceIpAddress}}.'
version: 1.0.3
kind: Scheduled
Stages and Predicates
Stage 1: source
AWSCloudTrail
Stage 2: where
| where (EventName == "DeleteDetector" and isempty(ErrorCode) and isempty( ErrorMessage)) or (EventName == "UpdateDetector" and tostring(parse_json(RequestParameters).enable) == "false" and isempty(ErrorCode) and isempty( ErrorMessage))
Stage 3: 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 |
|---|---|---|---|
ErrorCode | is_null | field:"aws::errorCode" kind:is_null | |
ErrorMessage | is_null | field:"aws::errorMessage" kind:is_null | |
EventName | eq |
| field:"aws::eventName" kind:eq |
RequestParameters.enable | eq |
| field:"RequestParameters.enable" kind:eq value:"false" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
UserIdentityArn | extend |
UserName | extend |
AccountName | extend |
AccountUPNSuffix | extend |
AWSCloudTrail - Creation of EC2 policy and then privilege escalation
#Identifies creation of IAM policies that grant broad EC2 permissions or EC2 actions combined with IAM pass-role capability, followed by policy attachment activity. This pattern can indicate attempts to gain elevated cloud execution privileges and should be validated as authorized change activity.
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: a694e977-740c-4578-9f8f-5e39029f1d23
name: AWSCloudTrail - Creation of EC2 policy and then privilege escalation
description: |
Identifies creation of IAM policies that grant broad EC2 permissions or EC2 actions combined with IAM pass-role
capability, followed by policy attachment activity. This pattern can indicate attempts to gain elevated cloud
execution privileges and should be validated as authorized change activity.
severity: High
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:*" or Action contains "iam:PassRole") and Action contains "ec2:*") or ((Action contains "iam:*" or Action contains "iam:PassRole") and Action contains "ec2:RunInstances") or ((Action contains "iam:*" or Action contains "iam:PassRole") and Action contains "ec2:Run*")) or (Action contains "ec2:*") or (Action contains "ec2:ListInstances" and Action contains "ec2:StartInstance" and Action contains "ec2:ModifyInstanceAttribute") or (Action contains "ec2:List*" and Action contains "ec2:Start*" and Action contains "ec2:Modify*")) and Resource == "*" and Condition == ""
| distinct TimeGenerated, EventName, PolicyName, SourceIpAddress, UserIdentityArn, RecipientAccountId, AccountName, AccountUPNSuffix
| 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, UserIdentityType , UserIdentityArn, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, PolicyName
| extend AttachEvent = pack("StartTime", StartTime, "EndTime", EndTime, "EventName", EventName, "UserIdentityType", UserIdentityType, "AccountName", AccountName, "AccountUPNSuffix", AccountUPNSuffix, "RecipientAccountId", RecipientAccountId, "UserIdentityArn", UserIdentityArn, "SourceIpAddress", SourceIpAddress)
| project EventSource, PolicyName, AttachEvent, RecipientAccountId, AccountName, AccountUPNSuffix, AttachEventCount
);
// 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 EC2 privilege escalation policy activity: {{PolicyName}} by {{AccountName}}'
alertDescriptionFormat: 'Detected {{EventName}} for policy {{PolicyName}} in account {{RecipientAccountId}}.'
version: 1.0.4
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, UserIdentityType , UserIdentityArn, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, PolicyName
| extend AttachEvent = pack("StartTime", StartTime, "EndTime", EndTime, "EventName", EventName, "UserIdentityType", UserIdentityType, "AccountName", AccountName, "AccountUPNSuffix", AccountUPNSuffix, "RecipientAccountId", RecipientAccountId, "UserIdentityArn", UserIdentityArn, "SourceIpAddress", SourceIpAddress)
| project EventSource, PolicyName, AttachEvent, RecipientAccountId, AccountName, AccountUPNSuffix, AttachEventCount
);
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:*" or Action contains "iam:PassRole") and Action contains "ec2:*") or ((Action contains "iam:*" or Action contains "iam:PassRole") and Action contains "ec2:RunInstances") or ((Action contains "iam:*" or Action contains "iam:PassRole") and Action contains "ec2:Run*")) or (Action contains "ec2:*") or (Action contains "ec2:ListInstances" and Action contains "ec2:StartInstance" and Action contains "ec2:ModifyInstanceAttribute") or (Action contains "ec2:List*" and Action contains "ec2:Start*" and Action contains "ec2:Modify*")) and Resource == "*" and Condition == ""
Stage 13: distinct
| distinct TimeGenerated, EventName, PolicyName, SourceIpAddress, UserIdentityArn, RecipientAccountId, AccountName, AccountUPNSuffix
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, UserIdentityType, UserIdentityArn, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, 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 |
CYFIRMA - Attack Surface - Weak Certificate Exposure - Medium 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 alert indicates that a weak or insecure SSL/TLS certificate has been detected on a public-facing asset monitored by Cyfirma. Such certificates do not meet modern encryption standards and are considered insecure, especially for handling sensitive transactions. This exposure increases the risk of man-in-the-middle attacks and loss of data integrity or confidentiality. Immediate remediation is advised by replacing weak certificates with strong, industry-compliant ones.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | |
| Resource Development | |
| Initial Access | |
| Defense Impairment | |
| Credential Access |
Rule Body
id: 5a617ff2-3e3d-44e7-b761-9f0d542ae191
name: CYFIRMA - Attack Surface - Weak Certificate Exposure - Medium Rule
description: |
'This alert indicates that a weak or insecure SSL/TLS certificate has been detected on a public-facing asset monitored by Cyfirma.
Such certificates do not meet modern encryption standards and are considered insecure, especially for handling sensitive transactions.
This exposure increases the risk of man-in-the-middle attacks and loss of data integrity or confidentiality.
Immediate remediation is advised by replacing weak certificates with strong, industry-compliant ones.'
version: 1.0.1
kind: Scheduled
severity: Medium
requiredDataConnectors:
- connectorId: CyfirmaAttackSurfaceAlertsConnector
dataTypes:
- CyfirmaASCertificatesAlerts_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- DefenseEvasion
- ResourceDevelopment
- Reconnaissance
- InitialAccess
- CredentialAccess
relevantTechniques:
- T1553
- T1588
- T1595
- T1190
- T1552
query: |
// Medium Severity Weak Certificate Exposure Detected
let timeFrame = 5m;
CyfirmaASCertificatesAlerts_CL
| where severity == 'High' and TimeGenerated between (ago(timeFrame) .. now())
| extend
Description=description,
FirstSeen=first_seen,
LastSeen=last_seen,
ValidFrom=valid_from,
ValidTo=valid_to,
RiskScore=risk_score,
Domain=sub_domain,
TopDomain=top_domain,
Protocols=protocols,
SelfSigned=self_signed,
AlertUID=alert_uid,
UID=uid,
CertificateData= cert_data,
CertificateHash=cert_hash,
IssuedBy=issued_by,
IssuedTo=issued_to,
ProviderName='CYFIRMA',
ProductName='DeCYFIR/DeTCT'
| project
TimeGenerated,
Description,
Domain,
TopDomain,
RiskScore,
FirstSeen,
LastSeen,
ValidFrom,
ValidTo,
Protocols,
SelfSigned,
CertificateData,
CertificateHash,
IssuedBy,
IssuedTo,
ProviderName,
ProductName
entityMappings:
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: HostName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: TopDomain
- identifier: DnsDomain
columnName: Domain
customDetails:
TimeGenerated: TimeGenerated
CertificateData: CertificateData
CertificateHash: CertificateHash
FirstSeen: FirstSeen
LastSeen: LastSeen
ValidFrom: ValidFrom
ValidTo: ValidTo
Protocols: Protocols
SelfSigned: SelfSigned
IssuedBy: IssuedBy
IssuedTo: IssuedTo
Description: Description
RiskScore: RiskScore
TopDomain: TopDomain
Domain: Domain
alertDetailsOverride:
alertDisplayNameFormat: CYFIRMA - Medium Severity Weak Certificate Exposure Detected for this Domain - {{Domain}}
alertDescriptionFormat: CYFIRMA - Medium Severity Weak Certificate Exposure Detected - {{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
CyfirmaASCertificatesAlerts_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,
ValidFrom=valid_from,
ValidTo=valid_to,
RiskScore=risk_score,
Domain=sub_domain,
TopDomain=top_domain,
Protocols=protocols,
SelfSigned=self_signed,
AlertUID=alert_uid,
UID=uid,
CertificateData= cert_data,
CertificateHash=cert_hash,
IssuedBy=issued_by,
IssuedTo=issued_to,
ProviderName='CYFIRMA',
ProductName='DeCYFIR/DeTCT'
Stage 4: project
| project
TimeGenerated,
Description,
Domain,
TopDomain,
RiskScore,
FirstSeen,
LastSeen,
ValidFrom,
ValidTo,
Protocols,
SelfSigned,
CertificateData,
CertificateHash,
IssuedBy,
IssuedTo,
ProviderName,
ProductName
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 |
|---|---|
CertificateData | project |
CertificateHash | project |
Description | project |
Domain | project |
FirstSeen | project |
IssuedBy | project |
IssuedTo | project |
LastSeen | project |
ProductName | project |
Protocols | project |
ProviderName | project |
RiskScore | project |
SelfSigned | project |
TimeGenerated | project |
TopDomain | project |
ValidFrom | project |
ValidTo | project |
DomainTools New Domain Discovery Indicators Ingested
#Creates an incident whenever DomainTools Domain Discovery new indicators (domains) are ingested in the last 1 hour.
Rule Body
id: f8c223c1-1a28-4b28-8e9f-5e33b610916b
name: DomainTools New Domain Discovery Indicators Ingested
description: Creates an incident whenever DomainTools Domain Discovery new indicators (domains) are ingested in the last 1 hour.
kind: Scheduled
severity: Medium
requiredDataConnectors:
- connectorId: DomainTools
dataTypes:
- DomainToolsThreatIntelDomains_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: "DomainToolsThreatIntelDomains(1h,\"domaindiscovery\")"
entityMappings:
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: DomainCustomEntity
eventGroupingSettings:
aggregationKind: SingleAlert
version: 1.0.0
Stages and Predicates
Stage 1: source
DomainToolsThreatIntelDomains(1h,"domaindiscovery")
Firmware Updates (Microsoft Defender for IoT)
#'This alert leverages Defender for IoT to detect unauthorized firmware updates that may indicate malicious activity on the network such as a cyber threat that attempts to manipulate PLC firmware to compromise PLC function.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Inhibit Response Function |
Rule Body
id: 7cad4b66-5e83-4756-8de4-f21315ab1e77
name: Firmware Updates (Microsoft Defender for IoT)
description: |
'This alert leverages Defender for IoT to detect unauthorized firmware updates that may indicate malicious activity on the network such as a cyber threat that attempts to manipulate PLC firmware to compromise PLC function.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: IoT
dataTypes:
- SecurityAlert (ASC for IoT)
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T0857
query: |
SecurityAlert
| where ProviderName == "IoTSecurity"
| where AlertName has_any ("Beckhoff Software Changed", "Firmware Version Changed", "Firmware Change", "Firmware Changed", "Firmware Update")
| extend ExtendedProperties = parse_json(ExtendedProperties)
| where tostring(ExtendedProperties.isNew) == "True"
| extend DeviceId = tostring(ExtendedProperties.DeviceId),
SourceDeviceAddress = tostring(ExtendedProperties.SourceDeviceAddress),
DestDeviceAddress = tostring(ExtendedProperties.DestinationDeviceAddress),
RemediationSteps = tostring(parse_json(RemediationSteps)[0]),
Protocol = tostring(ExtendedProperties.Protocol),
AlertManagementUri = tostring(ExtendedProperties.AlertManagementUri)
| project
TimeGenerated,
DeviceId,
ProductName,
ProductComponentName,
AlertSeverity,
AlertName,
Description,
Protocol,
SourceDeviceAddress,
DestDeviceAddress,
RemediationSteps,
Tactics,
Entities,
VendorOriginalId,
AlertLink,
AlertManagementUri,
Techniques
entityMappings:
sentinelEntitiesMappings:
- columnName: Entities
eventGroupingSettings:
aggregationKind: AlertPerResult
customDetails:
Sensor: DeviceId
Protocol: Protocol
VendorOriginalId: VendorOriginalId
AlertManagementUri: AlertManagementUri
alertDetailsOverride:
alertDisplayNameFormat: (MDIoT) {{AlertName}}
alertDescriptionFormat: (MDIoT) {{Description}}
alertTacticsColumnName: Tactics
alertSeverityColumnName: AlertSeverity
alertDynamicProperties:
- alertProperty: ProductName
value: ProductName
- alertProperty: RemediationSteps
value: RemediationSteps
- alertProperty: Techniques
value: Techniques
- alertProperty: ProductComponentName
value: ProductComponentName
- alertProperty: AlertLink
value: AlertLink
version: 1.0.3
kind: Scheduled
Stages and Predicates
Stage 1: source
SecurityAlert
Stage 2: where
| where ProviderName == "IoTSecurity"
Stage 3: where
| where AlertName has_any ("Beckhoff Software Changed", "Firmware Version Changed", "Firmware Change", "Firmware Changed", "Firmware Update")
Stage 4: extend
| extend ExtendedProperties = parse_json(ExtendedProperties)
Stage 5: where
| where tostring(ExtendedProperties.isNew) == "True"
Stage 6: extend
| extend DeviceId = tostring(ExtendedProperties.DeviceId),
SourceDeviceAddress = tostring(ExtendedProperties.SourceDeviceAddress),
DestDeviceAddress = tostring(ExtendedProperties.DestinationDeviceAddress),
RemediationSteps = tostring(parse_json(RemediationSteps)[0]),
Protocol = tostring(ExtendedProperties.Protocol),
AlertManagementUri = tostring(ExtendedProperties.AlertManagementUri)
Stage 7: project
| project
TimeGenerated,
DeviceId,
ProductName,
ProductComponentName,
AlertSeverity,
AlertName,
Description,
Protocol,
SourceDeviceAddress,
DestDeviceAddress,
RemediationSteps,
Tactics,
Entities,
VendorOriginalId,
AlertLink,
AlertManagementUri,
Techniques
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AlertName | match |
| field:"AlertName" kind:match |
ExtendedProperties.isNew | eq |
| field:"ExtendedProperties.isNew" kind:eq value:"True" |
ProviderName | eq |
| field:"ProviderName" kind:eq value:"IoTSecurity" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AlertLink | project |
AlertManagementUri | project |
AlertName | project |
AlertSeverity | project |
Description | project |
DestDeviceAddress | project |
DeviceId | project |
Entities | project |
ProductComponentName | project |
ProductName | project |
Protocol | project |
RemediationSteps | project |
SourceDeviceAddress | project |
Tactics | project |
Techniques | project |
TimeGenerated | project |
VendorOriginalId | project |
M2131_EventLogManagementPostureChanged_EL1
#'This alert is desinged to monitor Azure policies aligned with the Maturity Model for Event Log Management (M-21-31) standard. The alert triggers when EL1 policy compliance falls below 70% within a 1 week timeframe.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Rule Body
id: 036ce0a8-a1ff-4731-a078-02b3207fa4f3
name: M2131_EventLogManagementPostureChanged_EL1
description: |
'This alert is desinged to monitor Azure policies aligned with the Maturity Model for Event Log Management (M-21-31) standard. The alert triggers when EL1 policy compliance falls below 70% within a 1 week timeframe.'
severity: Medium
status: Available
requiredDataConnectors: []
queryFrequency: 7d
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
relevantTechniques:
- T1082
query: |
SecurityRecommendation
| where RecommendationDisplayName <> ""
| extend MaturityLevel=iff(RecommendationDisplayName has_any("agent","extension","retention","logs encryption","collect","retained","log profile","CloudTrail","metric","AWS","GCP","DNS","Auditing","Flow","logging","usage"), "Event Logging (EL0)",
iff(RecommendationDisplayName has_any("container registries","logic apps","Enhanced monitoring","IoT Hub","Event Hub","App Service", "Kubernetes","updates","email", "automation", "adaptive"), "Advanced Event Logging (EL3)",
iff(RecommendationDisplayName has_any("signatures","CMK","CMEK","double encryption","managed key","KMS","container","Watcher"), "Intermediate Event Logging (EL2)",
iff(RecommendationDisplayName has_any("Exploit Guard","endpoint protection","Antimalware","health","VPC","Defender","Vulnerabilities","vulnerability","diagnostic","Key","activity log alert",""), "Basic Event Logging (EL1)","Other"))))
| summarize arg_max(TimeGenerated, *) by RecommendationDisplayName, AssessedResourceId, MaturityLevel
| summarize Failed = countif(RecommendationState == "Unhealthy"), Passed = countif(RecommendationState == "Healthy"), Total = countif(RecommendationState == "Healthy" or RecommendationState == "Unhealthy") by MaturityLevel
| extend PassedControls = (Passed/todouble(Total))*100
| extend RemediationLink = strcat('https://portal.azure.com/#blade/Microsoft_Azure_Security/SecurityMenuBlade/22')
| project MaturityLevel, Total, PassedControls, Passed, Failed, RemediationLink, LastObserved=now()
| where MaturityLevel <> ''
| where MaturityLevel == "Basic Event Logging (EL1)"
| where PassedControls < 70
//Adjust Either FailedRatePercentage or PasedRatePercentage Thresholds within Organizational Needs
| sort by PassedControls desc
| extend URLCustomEntity = RemediationLink
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URLCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
SecurityRecommendation
Stage 2: where
| where RecommendationDisplayName <> ""
Stage 3: extend
| extend MaturityLevel=iff(RecommendationDisplayName has_any("agent","extension","retention","logs encryption","collect","retained","log profile","CloudTrail","metric","AWS","GCP","DNS","Auditing","Flow","logging","usage"), "Event Logging (EL0)",
iff(RecommendationDisplayName has_any("container registries","logic apps","Enhanced monitoring","IoT Hub","Event Hub","App Service", "Kubernetes","updates","email", "automation", "adaptive"), "Advanced Event Logging (EL3)",
iff(RecommendationDisplayName has_any("signatures","CMK","CMEK","double encryption","managed key","KMS","container","Watcher"), "Intermediate Event Logging (EL2)",
iff(RecommendationDisplayName has_any("Exploit Guard","endpoint protection","Antimalware","health","VPC","Defender","Vulnerabilities","vulnerability","diagnostic","Key","activity log alert",""), "Basic Event Logging (EL1)","Other"))))
MaturityLevel =(RecommendationDisplayName has "agent" or RecommendationDisplayName has "extension" or RecommendationDisplayName has "retention" or RecommendationDisplayName has "logs encryption" or RecommendationDisplayName has "collect" or RecommendationDisplayName has "retained" or RecommendationDisplayName has "log profile" or RecommendationDisplayName has "CloudTrail" or RecommendationDisplayName has "metric" or RecommendationDisplayName has "AWS" or RecommendationDisplayName has "GCP" or RecommendationDisplayName has "DNS" or RecommendationDisplayName has "Auditing" or RecommendationDisplayName has "Flow" or RecommendationDisplayName has "logging" or RecommendationDisplayName has "usage")"Event Logging (EL0)"iff((RecommendationDisplayName has_any ("container registries", "logic apps", "Enhanced monitoring", "IoT Hub", "Event Hub", "App Service", "Kubernetes", "updates", "email", "automation", "adaptive")), "Advanced Event Logging (EL3)", iff((RecommendationDisplayName has_any ("signatures", "CMK", "CMEK", "double encryption", "managed key", "KMS", "container", "Watcher")), "Intermediate Event Logging (EL2)", iff((RecommendationDisplayName has_any ("Exploit Guard", "endpoint protection", "Antimalware", "health", "VPC", "Defender", "Vulnerabilities", "vulnerability", "diagnostic", "Key", "activity log alert", "")), "Basic Event Logging (EL1)", "Other")))Stage 4: summarize
| summarize arg_max(TimeGenerated, *) by RecommendationDisplayName, AssessedResourceId, MaturityLevel
Stage 5: summarize
| summarize Failed = countif(RecommendationState == "Unhealthy"), Passed = countif(RecommendationState == "Healthy"), Total = countif(RecommendationState == "Healthy" or RecommendationState == "Unhealthy") by MaturityLevel
Stage 6: extend
| extend PassedControls = (Passed/todouble(Total))*100
Stage 7: extend
| extend RemediationLink = strcat('https://portal.azure.com/#blade/Microsoft_Azure_Security/SecurityMenuBlade/22')
Stage 8: project
| project MaturityLevel, Total, PassedControls, Passed, Failed, RemediationLink, LastObserved=now()
Stage 9: where
| where MaturityLevel <> ''
Stage 10: where
| where MaturityLevel == "Basic Event Logging (EL1)"
Stage 11: where
| where PassedControls < 70
Stage 12: sort
| sort by PassedControls desc
Stage 13: extend
| extend URLCustomEntity = RemediationLink
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
MaturityLevel | eq |
| field:"MaturityLevel" kind:eq value:"Basic Event Logging (EL1)" |
PassedControls | lt |
| field:"PassedControls" kind:lt value:"70" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Failed | project |
LastObserved | project |
MaturityLevel | project |
Passed | project |
PassedControls | project |
RemediationLink | project |
Total | project |
URLCustomEntity | extend |
Office Apps Launching Wscipt
#This query was originally published in the threat analytics report, Trickbot: Pervasive & underestimated. Trickbot is a very prevalent piece of malware with an array of malicious capabilities. Originally designed to steal banking credentials, it has since evolved into a modular trojan that can deploy other malware, disable security software, and perform command-and-control (C2) operations. Trickbot is frequently spread through email. An attacker will send a target a message with an attachment containing a malicious macro. If the target enables the macro, it will write a JScript Encoded (JSE) file to disk (JScript is a Microsoft dialect of ECMAScript). The JSE file will then be launched using wscript.exe to perform a variety of malicious tasks, particularly reconnaissance. The following query detects when Office applications have launched wscript.exe to run a JSE file. See Detect rundll.exe being used for reconnaissance and command-and-control for another query related to Trickbot activity. Reference - https://attack.mitre.org/software/S0266/
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Command & Control |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceProcessEvents action any: Process activity |
Rule Body
id: 174de33b-107b-4cd8-a85d-b4025a35453f
name: Office Apps Launching Wscipt
description: |
This query was originally published in the threat analytics report, Trickbot: Pervasive & underestimated.
Trickbot is a very prevalent piece of malware with an array of malicious capabilities. Originally designed to steal banking credentials, it has since evolved into a modular trojan that can deploy other malware, disable security software, and perform command-and-control (C2) operations.
Trickbot is frequently spread through email. An attacker will send a target a message with an attachment containing a malicious macro. If the target enables the macro, it will write a JScript Encoded (JSE) file to disk (JScript is a Microsoft dialect of ECMAScript). The JSE file will then be launched using wscript.exe to perform a variety of malicious tasks, particularly reconnaissance.
The following query detects when Office applications have launched wscript.exe to run a JSE file.
See Detect rundll.exe being used for reconnaissance and command-and-control for another query related to Trickbot activity.
Reference - https://attack.mitre.org/software/S0266/
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceProcessEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
- Collection
- CommandAndControl
relevantTechniques:
- T1059
- T1105
- T1203
tags:
- Trickbot
- JSE
- Office Applications
query: |
DeviceProcessEvents
| where InitiatingProcessFileName in~ ('winword.exe', 'excel.exe', 'outlook.exe')
| where FileName =~ "wscript.exe" and ProcessCommandLine has ".jse"
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
- entityType: Process
fieldMappings:
- identifier: ProcessId
columnName: ProcessId
- identifier: CommandLine
columnName: ProcessCommandLine
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
DeviceProcessEvents
Stage 2: where
| where InitiatingProcessFileName in~ ('winword.exe', 'excel.exe', 'outlook.exe')
Stage 3: where
| where FileName =~ "wscript.exe" and ProcessCommandLine has ".jse"
Stage 4: extend
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
HostName =DeviceName has "."substring(DeviceName, 0, indexof(DeviceName, '.'))DeviceNameStage 5: extend
| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
DnsDomain =DeviceName has "."substring(DeviceName, (indexof(DeviceName, '.') + 1))""Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
FileName | eq |
| field:"process_basename" kind:eq value:"wscript.exe" |
InitiatingProcessFileName | in |
| field:"parent_process_basename" kind:in |
ProcessCommandLine | match |
| field:"CommandLine" kind:match value:".jse" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HostName | extend |
DnsDomain | extend |
Regsvr32 Rundll32 Image Loads Abnormal Extension
#This query is looking for regsvr32.exe or rundll32.exe loading DLL images with other extensions than .dll. Joins the data to public network events. References: https://threathunt.blog/running-live-malware-for-threat-hunting-purposes/
MITRE ATT&CK Coverage
Telemetry Coverage
Rule Body
id: 36fbd4e7-5630-4414-aa42-702a7fdded21
name: Regsvr32 Rundll32 Image Loads Abnormal Extension
description: |
This query is looking for regsvr32.exe or rundll32.exe loading DLL images with other extensions than .dll.
Joins the data to public network events.
References:
https://threathunt.blog/running-live-malware-for-threat-hunting-purposes/
severity: High
status: Available
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceProcessEvents
- DeviceNetworkEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1218.010
- T1218.011
tags:
- Defense Evasion
- Image Load
- Regsvr32 Abuse
- Rundll32 Abuse
query: |
DeviceImageLoadEvents
| where InitiatingProcessFileName has_any ("rundll32.exe","regsvr32.exe")
| where FileName !endswith ".dll"
| join (
DeviceNetworkEvents
| where InitiatingProcessFileName has_any ("rundll32.exe","regsvr32.exe")
| where RemoteIPType == "Public"
) on InitiatingProcessFileName, InitiatingProcessId, InitiatingProcessCreationTime, InitiatingProcessCommandLine
| project TimeGenerated, DeviceName, FileName, FolderPath, SHA1, InitiatingProcessFileName, InitiatingProcessCommandLine, LocalIP, LocalPort, RemoteIP, RemoteUrl, RemotePort, InitiatingProcessParentFileName
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: LocalIP
- entityType: IP
fieldMappings:
- identifier: Address
columnName: RemoteIP
- entityType: URL
fieldMappings:
- identifier: Url
columnName: RemoteUrl
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
DeviceImageLoadEvents
Stage 2: where
| where InitiatingProcessFileName has_any ("rundll32.exe","regsvr32.exe")
Stage 3: where
| where FileName !endswith ".dll"
Stage 4: join
| join (
DeviceNetworkEvents
| where InitiatingProcessFileName has_any ("rundll32.exe","regsvr32.exe")
| where RemoteIPType == "Public"
) on InitiatingProcessFileName, InitiatingProcessId, InitiatingProcessCreationTime, InitiatingProcessCommandLine
Stage 5: project
project DeviceName, FileName, FolderPath, InitiatingProcessCommandLine, InitiatingProcessFileName, InitiatingProcessParentFileName, LocalIP, LocalPort, RemoteIP, RemotePort, RemoteUrl, SHA1, TimeGenerated
Stage 6: extend
extend HostName
HostName =DeviceName has "."substring(DeviceName, 0, indexof(DeviceName, '.'))DeviceNameStage 7: extend
extend DnsDomain
DnsDomain =DeviceName has "."substring(DeviceName, (indexof(DeviceName, '.') + 1))""Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
InitiatingProcessFileName | match |
| field:"process_basename" kind:match |
RemoteIPType | eq |
| field:"RemoteIPType" kind:eq value:"Public" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
FileName | ends_with | .dll | excludes:FileName field:"FileName" value:".dll" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeviceName | project |
FileName | project |
FolderPath | project |
InitiatingProcessCommandLine | project |
InitiatingProcessFileName | project |
InitiatingProcessParentFileName | project |
LocalIP | project |
LocalPort | project |
RemoteIP | project |
RemotePort | project |
RemoteUrl | project |
SHA1 | project |
TimeGenerated | project |
HostName | extend |
DnsDomain | extend |
Service Principal Name (SPN) Assigned to User Account
#This query identifies whether an Active Directory user object was assigned a service principal name which could indicate that an adversary is preparing for performing Kerberoasting. This query checks for event id 5136, that the Object Class field is "user" and the LDAP Display Name is "servicePrincipalName". Ref: https://thevivi.net/assets/docs/2019/theVIVI-AD-Security-Workshop_AfricaHackon2019.pdf
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 5136: A directory service object was modified. |
Rule Body
id: 875d0eb1-883a-4191-bd0e-dbfdeb95a464
name: Service Principal Name (SPN) Assigned to User Account
description: |
'This query identifies whether an Active Directory user object was assigned a service principal name which could indicate that an adversary is preparing for performing Kerberoasting.
This query checks for event id 5136, that the Object Class field is "user" and the LDAP Display Name is "servicePrincipalName".
Ref: https://thevivi.net/assets/docs/2019/theVIVI-AD-Security-Workshop_AfricaHackon2019.pdf'
severity: Medium
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
relevantTechniques:
- T1134
query: |
SecurityEvent
| where EventID == 5136
| parse EventData with * 'AttributeLDAPDisplayName">' AttributeLDAPDisplayName "<" *
| parse EventData with * 'ObjectClass">' ObjectClass "<" *
| where AttributeLDAPDisplayName == "servicePrincipalName" and ObjectClass == "user"
| parse EventData with * 'ObjectDN">' ObjectDN "<" *
| parse EventData with * 'AttributeValue">' AttributeValue "<" *
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by Computer, SubjectAccount, ObjectDN, AttributeValue, SubjectUserName, SubjectDomainName, SubjectUserSid
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| project-away DomainIndex
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: SubjectAccount
- identifier: Name
columnName: SubjectUserName
- identifier: NTDomain
columnName: SubjectDomainName
- entityType: Account
fieldMappings:
- identifier: Sid
columnName: SubjectUserSid
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: HostNameDomain
version: 1.0.4
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Vasileios Paschalidis
support:
tier: Community
categories:
domains: [ "Security - Others", "Identity" ]
Stages and Predicates
Stage 1: source
SecurityEvent
Stage 2: where
| where EventID == 5136
Stage 3: parse
| parse EventData with * 'AttributeLDAPDisplayName">' AttributeLDAPDisplayName "<" *
Stage 4: parse
| parse EventData with * 'ObjectClass">' ObjectClass "<" *
Stage 5: where
| where AttributeLDAPDisplayName == "servicePrincipalName" and ObjectClass == "user"
Stage 6: parse
| parse EventData with * 'ObjectDN">' ObjectDN "<" *
Stage 7: parse
| parse EventData with * 'AttributeValue">' AttributeValue "<" *
Stage 8: summarize
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by Computer, SubjectAccount, ObjectDN, AttributeValue, SubjectUserName, SubjectDomainName, SubjectUserSid
Stage 9: extend
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
Stage 10: extend
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
HostNameDomain =DomainIndex != -1substring(Computer, (DomainIndex + 1))ComputerStage 11: project-away
| project-away DomainIndex
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AttributeLDAPDisplayName | eq |
| field:"AttributeLDAPDisplayName" kind:eq value:"servicePrincipalName" |
EventID | eq |
| field:"EventID" kind:eq value:"5136" |
ObjectClass | eq |
| field:"ObjectClass" kind:eq value:"user" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AttributeValue | summarize |
Computer | summarize |
EndTime | summarize |
ObjectDN | summarize |
StartTime | summarize |
SubjectAccount | summarize |
SubjectDomainName | summarize |
SubjectUserName | summarize |
SubjectUserSid | summarize |
HostName | extend |
HostNameDomain | extend |
TacitRed - Repeat Compromise Detection
#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 users who have been compromised multiple times within a 7-day window. This may indicate a persistent threat or inadequate remediation. Ref: https://data443.com/tacitred-attack-surface-intelligence/
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Rule Body
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
name: TacitRed - Repeat Compromise Detection
version: 1.0.1
kind: Scheduled
description: |-
Detects users who have been compromised multiple times within a 7-day window.
This may indicate a persistent threat or inadequate remediation.
Ref: https://data443.com/tacitred-attack-surface-intelligence/
severity: High
requiredDataConnectors:
- connectorId: TacitRedThreatIntel
dataTypes:
- TacitRed_Findings_CL
queryFrequency: 1h
queryPeriod: 7d
triggerOperator: GreaterThan
triggerThreshold: 0
tactics:
- CredentialAccess
- Persistence
relevantTechniques:
- T1078
query: |-
let lookback = 7d;
TacitRed_Findings_CL
| where TimeGenerated >= ago(lookback)
| summarize
CompromiseCount = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
Sources = make_set(source_s),
AvgConfidence = avg(confidence_d)
by email_s, domain_s
| where CompromiseCount > 1
| extend
Email = tostring(email_s),
Domain = tostring(domain_s)
| project
Email,
Domain,
CompromiseCount,
FirstSeen,
LastSeen,
Sources,
AvgConfidence
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 5h
matchingMethod: Selected
groupByEntities:
- Account
suppressionDuration: 5h
eventGroupingSettings:
aggregationKind: AlertPerResult
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Email
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: Domain
Stages and Predicates
Parameters
let lookback = 7d;
Stage 1: source
TacitRed_Findings_CL
Stage 2: where
| where TimeGenerated >= ago(lookback)
Stage 3: summarize
| summarize
CompromiseCount = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
Sources = make_set(source_s),
AvgConfidence = avg(confidence_d)
by email_s, domain_s
Stage 4: where
| where CompromiseCount > 1
Stage 5: extend
| extend
Email = tostring(email_s),
Domain = tostring(domain_s)
Stage 6: project
| project
Email,
Domain,
CompromiseCount,
FirstSeen,
LastSeen,
Sources,
AvgConfidence
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CompromiseCount | gt |
| field:"CompromiseCount" kind:gt value:"1" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AvgConfidence | project |
CompromiseCount | project |
Domain | project |
Email | project |
FirstSeen | project |
LastSeen | project |
Sources | project |
Theom - Dev secrets unencrypted
#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.
"Creates Sentinel incidents for critical/high Theom risks, associated with ruleId TRIS0001 (Developer secrets have been observed in unencrypted data stores. Encrypt data at rest to comply with this CIS requirement)"
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Rule Body
id: f2490f5b-269c-471d-9ff4-475f62ea498e
name: Theom - Dev secrets unencrypted
description: |
"Creates Sentinel incidents for critical/high Theom risks, associated with ruleId TRIS0001 (Developer secrets have been observed in unencrypted data stores. Encrypt data at rest to comply with this CIS requirement)"
severity: High
status : Available
requiredDataConnectors:
- connectorId: Theom
dataTypes:
- TheomAlerts_CL
queryPeriod: 5m
queryFrequency: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1552
query: |
TheomAlerts_CL
| where customProps_RuleId_s == "TRIS0001" and (priority_s == "P1" or priority_s == "P2")
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: "Theom Alert ID: {{id_s}} "
alertDescriptionFormat: "\nSummary: {{summary_s}} \nAdditional info: {{details_s}}\nPlease investigate further on Theom UI at {{deepLink_s}}\n"
entityMappings:
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: customProps_AssetName_s
- entityType: URL
fieldMappings:
- identifier: Url
columnName: deepLink_s
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
TheomAlerts_CL
Stage 2: where
| where customProps_RuleId_s == "TRIS0001" and (priority_s == "P1" or priority_s == "P2")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
customProps_RuleId_s | eq |
| field:"customProps_RuleId_s" kind:eq value:"TRIS0001" |
priority_s | eq |
| field:"priority_s" kind:eq |
TI map IP entity to AppServiceHTTPLogs
#Identifies a match in AppServiceHTTPLogs from any IP IOC from TI
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: f9949656-473f-4503-bf43-a9d9890f7d08
name: TI map IP entity to AppServiceHTTPLogs
description: |
Identifies a match in AppServiceHTTPLogs from any IP IOC from TI
severity: Medium
requiredDataConnectors:
- 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; // Look back 1 hour for AppServiceHTTPLogs
let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
// Fetch threat intelligence indicators related to IP addresses
let IP_Indicators = ThreatIntelligenceIndicator
// Filter out indicators without relevant IP address fields
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
| where TimeGenerated >= ago(ioc_lookBack)
// Filtering out rows where the Confidence Score is less than 50 as they would not have an Alert Priority label.
| where ConfidenceScore > 50
// Select the IP entity based on availability of different IP fields
| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
// Determine AlertPriority based on ConfidenceScore
| extend AlertPriority = case(ConfidenceScore > 82, "High",
ConfidenceScore > 74, "Medium",
"Low")
// Exclude local addresses using the ipv4_is_private operator and filtering out specific address prefixes
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now();
// Perform a join between IP indicators and AppServiceHTTPLogs to identify potential malicious activity
IP_Indicators
// Use innerunique to keep performance fast and result set low, as we only need one match to indicate potential malicious activity that needs investigation
| join kind=innerunique (
AppServiceHTTPLogs | where TimeGenerated >= ago(dt_lookBack)
| where isnotempty(CIp)
| extend WebApp = split(_ResourceId, '/')[8]
| extend AppService_TimeGenerated = TimeGenerated // Rename time column for clarity
)
on $left.TI_ipEntity == $right.CIp
// Filter out logs that occurred after the expiration of the corresponding indicator
| where AppService_TimeGenerated < ExpirationDateTime
// Group the results by IndicatorId and CIp, and keep the log entry with the latest timestamp
| summarize AppService_TimeGenerated = arg_max(AppService_TimeGenerated, *) by IndicatorId, CIp
// Select the desired output fields
| project AppService_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, TI_ipEntity, CsUsername, WebApp = split(_ResourceId, '/')[8], CIp, CsHost, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress, _ResourceId, Type, AlertPriority
// Extract hostname and DNS domain from the CsHost field
| extend HostName = tostring(split(CsHost, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(CsHost, '.'), 1, -1), '.'))
// Rename the timestamp field
| extend timestamp = AppService_TimeGenerated
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
- entityType: Account
fieldMappings:
- identifier: Name
columnName: CsUsername
- entityType: IP
fieldMappings:
- identifier: Address
columnName: CIp
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
- entityType: AzureResource
fieldMappings:
- identifier: ResourceId
columnName: _ResourceId
alertDetailsOverride:
alertSeverityColumnName: AlertPriority
version: 1.5.3
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_Indicators is inlined into the numbered stages below.
Stages 1 to 8 define let IP_Indicators as the rule's main pipeline source. Stages 9 to 14 use it.
Stage 1: source
ThreatIntelligenceIndicator
Stage 2: where
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
Stage 3: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 4: where
| where ConfidenceScore > 50
Stage 5: extend (4 consecutive steps)
| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
| extend AlertPriority = case(ConfidenceScore > 82, "High",
ConfidenceScore > 74, "Medium",
"Low")
TI_ipEntity =isnotempty(NetworkIP)NetworkIPNetworkDestinationIPStage 6: where
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
Stage 7: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
Stage 8: where
| where Active == true and ExpirationDateTime > now()
Stage 9: join
IP_Indicators
| join kind=innerunique (
AppServiceHTTPLogs | where TimeGenerated >= ago(dt_lookBack)
| where isnotempty(CIp)
| extend WebApp = split(_ResourceId, '/')[8]
| extend AppService_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.CIp
Stage 10: where
where AppService_TimeGenerated < ExpirationDateTime
Stage 11: summarize
summarize AppService_TimeGenerated by IndicatorId, CIp
Stage 12: project
project ActivityGroupNames, AlertPriority, AppService_TimeGenerated, CIp, ConfidenceScore, CsHost, CsUsername, Description, EmailSourceIpAddress, ExpirationDateTime, IndicatorId, NetworkDestinationIP, NetworkIP, NetworkSourceIP, TI_ipEntity, ThreatType, Type, Url, WebApp, _ResourceId
Stage 13: extend
extend DnsDomain, HostName
Stage 14: extend
extend timestamp
Indicators
These rows show field, operator, and value matches.
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
TI_ipEntity | cidr_match | 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8 | excludes:TI_ipEntity |
TI_ipEntity | starts_with | 127. | excludes:TI_ipEntity field:"TI_ipEntity" value:"127." |
TI_ipEntity | starts_with | :: | excludes:TI_ipEntity field:"TI_ipEntity" value:"::" |
TI_ipEntity | starts_with | fe80 | excludes:TI_ipEntity field:"TI_ipEntity" value:"fe80" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityGroupNames | project |
AlertPriority | project |
AppService_TimeGenerated | project |
CIp | project |
ConfidenceScore | project |
CsHost | project |
CsUsername | project |
Description | project |
EmailSourceIpAddress | project |
ExpirationDateTime | project |
IndicatorId | project |
NetworkDestinationIP | project |
NetworkIP | project |
NetworkSourceIP | project |
TI_ipEntity | project |
ThreatType | project |
Type | project |
Url | project |
WebApp | project |
_ResourceId | project |
DnsDomain | extend |
HostName | extend |
timestamp | extend |
Valimail Enforce - Unusual Rate of Configuration Changes or User Additions
#This query searches for a single user performing more than 3 configuration changes or user additions within a 1-hour window on any domain. An unusual burst of changes may indicate a compromised admin account, unauthorized automation, or insider threat.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation | |
| Stealth | |
| Defense Impairment | |
| Impact |
Rule Body
id: 3cbb78d9-81ac-42c9-b3cd-7e6baea7d9ff
name: Valimail Enforce - Unusual Rate of Configuration Changes or User Additions
description: |
This query searches for a single user performing more than 3 configuration changes or user
additions within a 1-hour window on any domain. An unusual burst of changes may
indicate a compromised admin account, unauthorized automation, or insider threat.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: ValimailEnforce
dataTypes:
- ValimailEnforceEvents_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
- DefenseEvasion
- PrivilegeEscalation
relevantTechniques:
- T1562
- T1531
- T1078
query: |
let threshold = 3;
let timeWindow = 1h;
ValimailEnforceEvents_CL
| where EventCategory in (
"DMARCPolicy",
"SPFConfiguration",
"DKIMConfiguration",
"DomainManagement",
"UserManagement"
)
| summarize
ChangeCount = count(),
HighValueCount = countif(IsHighValueEvent == true),
Domains = make_set(Subject),
DomainCount = dcount(Subject),
Actions = make_set(EventType),
Categories = make_set(EventCategory),
FirstSeen = min(PerformedAt),
LastSeen = max(PerformedAt)
by User, bin(PerformedAt, timeWindow)
| where ChangeCount > threshold
| extend
AccountName = tostring(split(User, "@")[0]),
AccountDomain = tostring(split(User, "@")[1]),
DomainName = tostring(Domains[0]),
ChangesPerMin = round(todouble(ChangeCount) / 60.0, 2)
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountDomain
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: DomainName
alertDetailsOverride:
alertDisplayNameFormat: "Unusual change rate by {{User}}, {{ChangeCount}} changes in 1h across {{DomainCount}} domain(s)"
alertDescriptionFormat: |
User '{{User}}' made {{ChangeCount}} configuration changes across {{DomainCount}} domain(s) within one hour.
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1d
matchingMethod: Selected
groupByEntities:
- Account
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 3;
let timeWindow = 1h;
Stage 1: source
ValimailEnforceEvents_CL
Stage 2: where
| where EventCategory in (
"DMARCPolicy",
"SPFConfiguration",
"DKIMConfiguration",
"DomainManagement",
"UserManagement"
)
Stage 3: summarize
| summarize
ChangeCount = count(),
HighValueCount = countif(IsHighValueEvent == true),
Domains = make_set(Subject),
DomainCount = dcount(Subject),
Actions = make_set(EventType),
Categories = make_set(EventCategory),
FirstSeen = min(PerformedAt),
LastSeen = max(PerformedAt)
by User, bin(PerformedAt, timeWindow)
Stage 4: where
| where ChangeCount > threshold
Stage 5: extend
| extend
AccountName = tostring(split(User, "@")[0]),
AccountDomain = tostring(split(User, "@")[1]),
DomainName = tostring(Domains[0]),
ChangesPerMin = round(todouble(ChangeCount) / 60.0, 2)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ChangeCount | gt |
| field:"ChangeCount" kind:gt value:"3" |
EventCategory | in |
| field:"EventCategory" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Actions | summarize |
Categories | summarize |
ChangeCount | summarize |
DomainCount | summarize |
Domains | summarize |
FirstSeen | summarize |
HighValueCount | summarize |
LastSeen | summarize |
User | summarize |
AccountDomain | extend |
AccountName | extend |
ChangesPerMin | extend |
DomainName | extend |
Versasec CMS - Multiple Failed Login Attempts
#Detects when Operator login failed to often.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Rule Body
id: B1DB8B7E-9D74-48C3-9683-74483CBEFF4E
name: Versasec CMS - Multiple Failed Login Attempts
description: |
Detects when Operator login failed to often.
severity: High
status: Available
requiredDataConnectors:
- connectorId: VersasecCms
dataTypes:
- VersasecCmsSysLogs
queryFrequency: 5m
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 5
tactics:
- CredentialAccess
relevantTechniques:
- T1110 # Brute Force
query: |
let threshold = 5;
VersasecCmsSysLogs
| where EventId == 2
| sort by ComputerName asc, TimeGenerated asc
| extend TimeDiff = datetime_diff('minute', TimeGenerated, prev(TimeGenerated))
| where TimeDiff <= threshold and ComputerName == prev(ComputerName)
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: ComputerName
eventGroupingSettings:
aggregationKind: SingleAlert
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: 5m
matchingMethod: AllEntities
version: 1.0.1
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 5;
Stage 1: source
VersasecCmsSysLogs
Stage 2: where
| where EventId == 2
Stage 3: sort
| sort by ComputerName asc, TimeGenerated asc
Stage 4: extend
| extend TimeDiff = datetime_diff('minute', TimeGenerated, prev(TimeGenerated))
Stage 5: where
| where TimeDiff <= threshold and ComputerName == prev(ComputerName)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventId | eq |
| field:"EventId" kind:eq value:"2" |
TimeDiff | le |
| field:"TimeDiff" kind:le value:"5" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
TimeDiff | extend |
VMware ESXi - VM stopped
#'Detects when VM was stopped.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 43889f30-7bce-4d8a-93bb-29c9615ca8dd
name: VMware ESXi - VM stopped
description: |
'Detects when VM was stopped.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1529
query: |
VMwareESXi
| where SyslogMessage has ('VmPoweredOffEvent')
| extend SrcUsername = extract(@'\[info\]\s+\[(.*?)\]', 1, SyslogMessage)
| extend DstHostname = extract(@'\[\d+\]\s+\[(.*?)\s+on', 1, SyslogMessage)
| extend HostCustomEntity = DstHostname, AccountCustomEntity = SrcUsername
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: HostCustomEntity
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
VMwareESXi
Stage 2: where
| where SyslogMessage has ('VmPoweredOffEvent')
Stage 3: extend (3 consecutive steps)
| extend SrcUsername = extract(@'\[info\]\s+\[(.*?)\]', 1, SyslogMessage)
| extend DstHostname = extract(@'\[\d+\]\s+\[(.*?)\s+on', 1, SyslogMessage)
| extend HostCustomEntity = DstHostname, AccountCustomEntity = SrcUsername
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
SyslogMessage | match |
| field:"SyslogMessage" kind:match value:"VmPoweredOffEvent" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcUsername | extend |
DstHostname | extend |
AccountCustomEntity | extend |
HostCustomEntity | extend |