Detection rules › Kusto
Kusto rules: 0k
Bitglass - User login from new geo location
#'Detects user login from new geo location.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 34401e66-9fe9-476b-a443-3a3f89e4f3b0
name: Bitglass - User login from new geo location
description: |
'Detects user login from new geo location.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Bitglass
dataTypes:
- Bitglass
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
Bitglass
| where EventType =~ 'access'
| where EventMessage =~ 'Login'
| summarize g_loc = makeset(SrcGeoLocation) by User
| join (Bitglass
| where EventType =~ 'access'
| where EventMessage =~ 'Login') on User
| where g_loc !contains SrcGeoLocation
| extend AccountCustomEntity = User, FileCustomEntity = SrcFileName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: File
fieldMappings:
- identifier: Name
columnName: FileCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Bitglass
Stage 2: where
| where EventType =~ 'access'
Stage 3: where
| where EventMessage =~ 'Login'
Stage 4: summarize
| summarize g_loc = makeset(SrcGeoLocation) by User
Stage 5: join
| join (Bitglass
| where EventType =~ 'access'
| where EventMessage =~ 'Login') on User
Stage 6: where
where g_loc !contains SrcGeoLocation
Stage 7: extend
extend AccountCustomEntity, FileCustomEntity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventMessage | eq |
| field:"EventMessage" kind:eq value:"Login" |
EventType | eq |
| field:"EventType" kind:eq value:"access" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
User | summarize |
g_loc | summarize |
AccountCustomEntity | extend |
FileCustomEntity | extend |
GWorkspace - User access has been changed
#Detects user access change.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Google Workspace | CHANGE_USER_ACCESS: Change User Access |
| Google Workspace | change_user_access: Change User Access |
Rule Body
id: 92fae638-5da8-11ec-bf63-0242ac130002
name: GWorkspace - User access has been changed
description: |
'Detects user access change.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: GoogleWorkspaceReportsAPI
dataTypes:
- GWorkspaceActivityReports
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1098
query: |
GWorkspaceActivityReports
| where EventMessage has "change_user_access"
| extend AccountCustomEntity = ActorEmail
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
GWorkspaceActivityReports
Stage 2: where
| where EventMessage has "change_user_access"
Stage 3: extend
| extend AccountCustomEntity = ActorEmail
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventMessage | match |
| field:"EventMessage" kind:match value:"change_user_access" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
NGINX - Known malicious user agent
#'Detects known malicious user agents'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: a10c6551-bbf2-492c-aa8a-fe6efd8c9cc1
name: NGINX - Known malicious user agent
description: |
'Detects known malicious user agents'
severity: High
status: Available
requiredDataConnectors:
- connectorId: CustomLogsAma
dataTypes:
- NGINX_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
- T1133
query: |
let mal_ua_list = dynamic(['Nikto', '(hydra)', '.nasl', 'absinthe', 'advanced email extractor', 'arachni/', 'autogetcontent', 'bilbo', 'BFAC', 'brutus', 'brutus/aet', 'bsqlbf', 'cgichk', 'cisco-torch', 'commix', 'core-project/1.0', 'crimscanner/', 'datacha0s', 'dirbuster', 'domino hunter', 'dotdotpwn', 'email extractor', 'fhscan core 1.', 'floodgate', 'get-minimal', 'gootkit auto-rooter scanner', 'grabber', 'grendel-scan', 'havij', 'inspath', 'internet ninja', 'jaascois', 'zmeu', 'masscan', 'metis', 'morfeus', 'mysqloit', 'n-stealth', 'nessus', 'netsparker', 'nmap nse', 'nmap scripting engine', 'nmap-nse', 'nsauditor', 'openvas', 'pangolin', 'paros', 'pmafind', 'prog.customcrawler', 'qualys was', 's.t.a.l.k.e.r.', 'security scan', 'springenwerk', 'sql power injector', 'sqlmap', 'sqlninja', 'teh forest lobster', 'this is an exploit', 'toata dragostea', 'toata dragostea mea pentru diavola', 'uil2pn', 'user-agent:', 'vega/', 'voideye', 'w3af.sf.net', 'w3af.sourceforge.net', 'w3af.org', 'webbandit', 'webinspect', 'webshag', 'webtrends security analyzer', 'webvulnscan', 'whatweb', 'whcc/', 'wordpress hash grabber', 'xmlrpc exploit', 'WPScan', 'XSpider', 'SF/', 'FooBar/42', 'ScanAlert', 'Webscanner', 'Webster', 'fantomCrew', 'fantomBrowser', 'visvo', 'magereport', 'ltx71', 'websiteprotection', 'BigCliqueBOT', '(BOT for JCE)']);
NGINXHTTPServer
| where HttpUserAgentOriginal has_any (mal_ua_list)
| extend IPCustomEntity = SrcIpAddr, MalwareCustomEntity = HttpUserAgentOriginal
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
- entityType: Malware
fieldMappings:
- identifier: Name
columnName: MalwareCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Let Binding: mal_ua_list
let mal_ua_list = dynamic(['Nikto', '(hydra)', '.nasl', 'absinthe', 'advanced email extractor', 'arachni/', 'autogetcontent', 'bilbo', 'BFAC', 'brutus', 'brutus/aet', 'bsqlbf', 'cgichk', 'cisco-torch', 'commix', 'core-project/1.0', 'crimscanner/', 'datacha0s', 'dirbuster', 'domino hunter', 'dotdotpwn', 'email extractor', 'fhscan core 1.', 'floodgate', 'get-minimal', 'gootkit auto-rooter scanner', 'grabber', 'grendel-scan', 'havij', 'inspath', 'internet ninja', 'jaascois', 'zmeu', 'masscan', 'metis', 'morfeus', 'mysqloit', 'n-stealth', 'nessus', 'netsparker', 'nmap nse', 'nmap scripting engine', 'nmap-nse', 'nsauditor', 'openvas', 'pangolin', 'paros', 'pmafind', 'prog.customcrawler', 'qualys was', 's.t.a.l.k.e.r.', 'security scan', 'springenwerk', 'sql power injector', 'sqlmap', 'sqlninja', 'teh forest lobster', 'this is an exploit', 'toata dragostea', 'toata dragostea mea pentru diavola', 'uil2pn', 'user-agent:', 'vega/', 'voideye', 'w3af.sf.net', 'w3af.sourceforge.net', 'w3af.org', 'webbandit', 'webinspect', 'webshag', 'webtrends security analyzer', 'webvulnscan', 'whatweb', 'whcc/', 'wordpress hash grabber', 'xmlrpc exploit', 'WPScan', 'XSpider', 'SF/', 'FooBar/42', 'ScanAlert', 'Webscanner', 'Webster', 'fantomCrew', 'fantomBrowser', 'visvo', 'magereport', 'ltx71', 'websiteprotection', 'BigCliqueBOT', '(BOT for JCE)']);
Stage 1: source
NGINXHTTPServer
Stage 2: where
| where HttpUserAgentOriginal has_any (mal_ua_list)
Stage 3: extend
| extend IPCustomEntity = SrcIpAddr, MalwareCustomEntity = HttpUserAgentOriginal
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
HttpUserAgentOriginal | match |
| field:"HttpUserAgentOriginal" kind:match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
IPCustomEntity | extend |
MalwareCustomEntity | extend |
NIST SP 800-53 Posture Changed
#'This alert is desinged to monitor Azure policies aligned with the NIST SP 800-53 Regulatory Compliance initative. The alert triggers when policy compliance falls below 70% within a 1 week timeframe.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Rule Body
id: dd834c97-4638-4bb3-a4e3-807e8b0580dc
name: NIST SP 800-53 Posture Changed
description: |
'This alert is desinged to monitor Azure policies aligned with the NIST SP 800-53 Regulatory Compliance initative. The alert triggers when 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:
let Last_Evaluated=SecurityRecommendation
| join kind=fullouter(SecurityRegulatoryCompliance| where ComplianceStandard == "NIST-SP-800-53-R4") on RecommendationName
| summarize arg_max(TimeGenerated, *) by AssessedResourceId, RecommendationName
| extend ComplianceDomain=iff(ComplianceControl contains "AC.", "Access Control", iff(ComplianceControl contains "AT.", "Security Awareness & Training", iff(ComplianceControl contains "AU.", "Audit & Accountability", iff(ComplianceControl contains "CA.", "Security Assessment", iff(ComplianceControl contains "CM.", "Configuration Management", iff(ComplianceControl contains "CP.", "Contingency Planning", iff(ComplianceControl contains "IA.", "Identification & Authentication", iff(ComplianceControl contains "IR.", "Incident Response", iff(ComplianceControl contains "MA.", "System Maintenance", iff(ComplianceControl contains "MP.", "Media Protection", iff(ComplianceControl contains "PE.", "Physical Protection", iff(ComplianceControl contains "PL.", "Security Planning", iff(ComplianceControl contains "PS.", "Personnel Security",iff(ComplianceControl contains "RA.", "Risk Assessment",iff(ComplianceControl contains "SA.", "System & Services Acquisition",iff(ComplianceControl contains "SC.", "System & Communications Protection",iff(ComplianceControl contains "SI.", "System & Information Integrity","Other")))))))))))))))))
| summarize Failed = countif(RecommendationState == "Unhealthy"), Passed = countif(RecommendationState == "Healthy"), Total = countif(RecommendationState == "Healthy" or RecommendationState == "Unhealthy") by ComplianceDomain, TimeGenerated;
SecurityRecommendation
| join kind=fullouter(SecurityRegulatoryCompliance| where ComplianceStandard == "NIST-SP-800-53-R4") on RecommendationName
| summarize arg_max(TimeGenerated, *) by AssessedResourceId, RecommendationName
| extend ComplianceDomain=iff(ComplianceControl contains "AC.", "Access Control", iff(ComplianceControl contains "AT.", "Security Awareness & Training", iff(ComplianceControl contains "AU.", "Audit & Accountability", iff(ComplianceControl contains "CA.", "Security Assessment", iff(ComplianceControl contains "CM.", "Configuration Management", iff(ComplianceControl contains "CP.", "Contingency Planning", iff(ComplianceControl contains "IA.", "Identification & Authentication", iff(ComplianceControl contains "IR.", "Incident Response", iff(ComplianceControl contains "MA.", "System Maintenance", iff(ComplianceControl contains "MP.", "Media Protection", iff(ComplianceControl contains "PE.", "Physical Protection", iff(ComplianceControl contains "PL.", "Security Planning", iff(ComplianceControl contains "PS.", "Personnel Security",iff(ComplianceControl contains "RA.", "Risk Assessment",iff(ComplianceControl contains "SA.", "System & Services Acquisition",iff(ComplianceControl contains "SC.", "System & Communications Protection",iff(ComplianceControl contains "SI.", "System & Information Integrity","Other")))))))))))))))))
| summarize Failed = countif(RecommendationState == "Unhealthy"), Passed = countif(RecommendationState == "Healthy"), Total = countif(RecommendationState == "Healthy" or RecommendationState == "Unhealthy") by ComplianceDomain
| extend PassedControlsPercentage = (Passed/todouble(Total))*100
| join (Last_Evaluated) on ComplianceDomain
| project ComplianceDomain, Total, PassedControlsPercentage, Passed, Failed, LastEvaluated=TimeGenerated
| summarize arg_max(LastEvaluated, *) by ComplianceDomain, Total, PassedControlsPercentage, Passed, Failed
| where PassedControlsPercentage < 70
| sort by PassedControlsPercentage, Passed desc
| extend RemediationLink = strcat('https://portal.azure.com/#blade/Microsoft_Azure_Security/SecurityMenuBlade/22')
| extend URLCustomEntity = RemediationLink
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URLCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Let Binding: Last_Evaluated
let Last_Evaluated = SecurityRecommendation | join kind=fullouter(SecurityRegulatoryCompliance| where ComplianceStandard == "NIST-SP-800-53-R4") on RecommendationName | summarize arg_max(TimeGenerated, *) by AssessedResourceId, RecommendationName | extend ComplianceDomain=iff(ComplianceControl contains "AC.", "Access Control", iff(ComplianceControl contains "AT.", "Security Awareness & Training", iff(ComplianceControl contains "AU.", "Audit & Accountability", iff(ComplianceControl contains "CA.", "Security Assessment", iff(ComplianceControl contains "CM.", "Configuration Management", iff(ComplianceControl contains "CP.", "Contingency Planning", iff(ComplianceControl contains "IA.", "Identification & Authentication", iff(ComplianceControl contains "IR.", "Incident Response", iff(ComplianceControl contains "MA.", "System Maintenance", iff(ComplianceControl contains "MP.", "Media Protection", iff(ComplianceControl contains "PE.", "Physical Protection", iff(ComplianceControl contains "PL.", "Security Planning", iff(ComplianceControl contains "PS.", "Personnel Security",iff(ComplianceControl contains "RA.", "Risk Assessment",iff(ComplianceControl contains "SA.", "System & Services Acquisition",iff(ComplianceControl contains "SC.", "System & Communications Protection",iff(ComplianceControl contains "SI.", "System & Information Integrity","Other"))))))))))))))))) | summarize Failed = countif(RecommendationState == "Unhealthy"), Passed = countif(RecommendationState == "Healthy"), Total = countif(RecommendationState == "Healthy" or RecommendationState == "Unhealthy") by ComplianceDomain, TimeGenerated;
Stage 1: source
SecurityRecommendation
Stage 2: join
join kind=fullouter (SecurityRegulatoryCompliance) on RecommendationName
Stage 3: summarize
summarize by AssessedResourceId, RecommendationName
Stage 4: extend
extend ComplianceDomain
ComplianceDomain =ComplianceControl contains "AC.""Access Control"iff((ComplianceControl contains "AT."), "Security Awareness & Training", iff((ComplianceControl contains "AU."), "Audit & Accountability", iff((ComplianceControl contains "CA."), "Security Assessment", iff((ComplianceControl contains "CM."), "Configuration Management", iff((ComplianceControl contains "CP."), "Contingency Planning", iff((ComplianceControl contains "IA."), "Identification & Authentication", iff((ComplianceControl contains "IR."), "Incident Response", iff((ComplianceControl contains "MA."), "System Maintenance", iff((ComplianceControl contains "MP."), "Media Protection", iff((ComplianceControl contains "PE."), "Physical Protection", iff((ComplianceControl contains "PL."), "Security Planning", iff((ComplianceControl contains "PS."), "Personnel Security", iff((ComplianceControl contains "RA."), "Risk Assessment", iff((ComplianceControl contains "SA."), "System & Services Acquisition", iff((ComplianceControl contains "SC."), "System & Communications Protection", iff((ComplianceControl contains "SI."), "System & Information Integrity", "Other"))))))))))))))))Stage 5: summarize
summarize Failed, Passed, Total by ComplianceDomain
Stage 6: extend
extend PassedControlsPercentage
Stage 7: join
join (Last_Evaluated) on ComplianceDomain
Stage 8: project
project ComplianceDomain, Failed, LastEvaluated, Passed, PassedControlsPercentage, Total
Stage 9: summarize
summarize by ComplianceDomain, Total, PassedControlsPercentage, Passed, Failed
Stage 10: where
where PassedControlsPercentage < 70
Stage 11: sort
sort by Passed, PassedControlsPercentage
Stage 12: extend
extend RemediationLink
Stage 13: extend
extend URLCustomEntity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ComplianceStandard | eq |
| field:"ComplianceStandard" kind:eq value:"NIST-SP-800-53-R4" |
PassedControlsPercentage | lt |
| field:"PassedControlsPercentage" kind:lt value:"70" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ComplianceDomain | summarize |
Failed | summarize |
Passed | summarize |
PassedControlsPercentage | summarize |
Total | summarize |
RemediationLink | extend |
URLCustomEntity | extend |
OracleDBAudit - Connection to database from external IP
#'Detects when connection to database is from external IP source.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Collection | |
| Exfiltration |
Rule Body
id: 54aa2c17-acfd-4e3a-a1c4-99c88cf34ebe
name: OracleDBAudit - Connection to database from external IP
description: |
'Detects when connection to database is from external IP source.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Collection
- Exfiltration
relevantTechniques:
- T1190
- T1133
- T1078
- T1119
- T1029
query: |
OracleDatabaseAuditEvent
| where isnotempty(SrcIpAddr)
| where isnotempty(Action)
| where DbAction =~ 'connect'
| where ipv4_is_private(SrcIpAddr) == 'false'
| extend AccountCustomEntity = DstUserName
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
OracleDatabaseAuditEvent
Stage 2: where
| where isnotempty(SrcIpAddr)
Stage 3: where
| where isnotempty(Action)
Stage 4: where
| where DbAction =~ 'connect'
Stage 5: where
| where ipv4_is_private(SrcIpAddr) == 'false'
Stage 6: extend
| extend AccountCustomEntity = DstUserName
Stage 7: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | is_not_null | field:"Action" kind:is_not_null | |
DbAction | eq |
| field:"DbAction" kind:eq value:"connect" |
SrcIpAddr | is_not_null | field:"src_ip" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
SrcIpAddr | cidr_match | 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8 | excludes:SrcIpAddr |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
IPCustomEntity | extend |
PROD (TM002.1) - OBJECT - Created or Deleted a Tier Level Object
#A Tier Model object was created or deleted
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Telemetry Coverage
Rule Body
id: b58adf25-fb44-4296-9b6c-0e6b97139dad
name: 'PROD (TM002.1) - OBJECT - Created or Deleted a Tier Level Object'
description: |
A Tier Model object was created or deleted
severity: Low
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- InitialAccess
relevantTechniques:
- T1078.002
query: |
SecurityEvent
| where (EventID == 5137
or EventID == 5141)
and (EventData matches regex @"(?i)OU=Domain Controllers"
or EventData matches regex @"(?i)OU=Tier 0 Accounts"
or EventData matches regex @"(?i)OU=Tier 1 Accounts"
or EventData matches regex @"(?i)OU=Tier 2 Accounts"
or EventData matches regex @"(?i)OU=Tier 0 Service Accounts"
or EventData matches regex @"(?i)OU=Tier 1 Service Accounts"
or EventData matches regex @"(?i)OU=Tier 2 Service Accounts"
or EventData matches regex @"(?i)OU=Tier 0 Groups"
or EventData matches regex @"(?i)OU=Tier 1 Groups"
or EventData matches regex @"(?i)OU=Tier 2 Groups"
or EventData matches regex @"(?i)OU=Tier 0 PAW"
or EventData matches regex @"(?i)OU=Tier 1 PAW"
or EventData matches regex @"(?i)OU=Tier 2 PAW"
or EventData matches regex @"(?i)OU=Tier 0 Member Servers"
or EventData matches regex @"(?i)OU=Tier 1 Member Servers"
or EventData matches regex @"(?i)CN=Builtin"
or EventData matches regex @"(?i)CN=Computers"
or EventData matches regex @"(?i)CN=Users")
| extend ObjectName_e = extract("<Data Name=\"ObjectDN\">([^<]+)</Data>", 1, EventData)
| extend ObjectName = extract("(?i)CN=([^,]+)", 1, ObjectName_e)
| extend ObjectTier_e = extract("<Data Name=\"ObjectDN\">([^<]+)</Data>", 1, EventData)
| extend ObjectTier = extract("(?i)OU=(Tier [^ ,]+)", 1, ObjectTier_e)
| extend ObjectTier = iff(ObjectTier == "", "Outside Tier Model", ObjectTier)
| extend ObjectTier = iff(ObjectTier_e has "OU=Domain Controllers", "Tier 0", ObjectTier)
| extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
| where ObjectClass !has "organizationalUnit" and ObjectClass !has "msFVE-RecoveryInformation"
| extend ActivityType = extract("([a-zA-Z]+)\\.$", 1, Activity)
| extend SubjectUserName = extract("<Data Name=\"SubjectUserName\">([^<]+)</Data>", 1, EventData)
| extend SubjectDomainName = extract("<Data Name=\"SubjectDomainName\">([^<]+)</Data>", 1, EventData)
| extend Account = strcat(SubjectDomainName, "\\", SubjectUserName)
| extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)
| project TimeGenerated
, ObjectName
, ObjectTier
, ObjectClass
, ActivityType
, Account
, Domain
, Computer
, Channel
, EventID
, EventData
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: ObjectName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: Computer
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Account
customDetails:
ObjectName: ObjectName
ObjectTier: ObjectTier
ObjectClass: ObjectClass
ActivityType: ActivityType
Account: Account
Domain: Domain
Computer: Computer
Channel: Channel
EventID: EventID
EventData: EventData
eventGroupingSettings:
aggregationKind: AlertPerResult
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5M
matchingMethod: Selected
groupByEntities:
- Account
groupByCustomDetails:
- ObjectName
alertDetailsOverride:
alertDisplayNameFormat: '(TM002.1) A {{ObjectTier}} {{ObjectClass}} was {{ActivityType}}'
alertDescriptionFormat: 'Tier Model TM002 - A Tier Model object was created or deleted.'
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
SecurityEvent
Stage 2: where
| where (EventID == 5137
or EventID == 5141)
and (EventData matches regex @"(?i)OU=Domain Controllers"
or EventData matches regex @"(?i)OU=Tier 0 Accounts"
or EventData matches regex @"(?i)OU=Tier 1 Accounts"
or EventData matches regex @"(?i)OU=Tier 2 Accounts"
or EventData matches regex @"(?i)OU=Tier 0 Service Accounts"
or EventData matches regex @"(?i)OU=Tier 1 Service Accounts"
or EventData matches regex @"(?i)OU=Tier 2 Service Accounts"
or EventData matches regex @"(?i)OU=Tier 0 Groups"
or EventData matches regex @"(?i)OU=Tier 1 Groups"
or EventData matches regex @"(?i)OU=Tier 2 Groups"
or EventData matches regex @"(?i)OU=Tier 0 PAW"
or EventData matches regex @"(?i)OU=Tier 1 PAW"
or EventData matches regex @"(?i)OU=Tier 2 PAW"
or EventData matches regex @"(?i)OU=Tier 0 Member Servers"
or EventData matches regex @"(?i)OU=Tier 1 Member Servers"
or EventData matches regex @"(?i)CN=Builtin"
or EventData matches regex @"(?i)CN=Computers"
or EventData matches regex @"(?i)CN=Users")
Stage 3: extend (7 consecutive steps)
| extend ObjectName_e = extract("<Data Name=\"ObjectDN\">([^<]+)</Data>", 1, EventData)
| extend ObjectName = extract("(?i)CN=([^,]+)", 1, ObjectName_e)
| extend ObjectTier_e = extract("<Data Name=\"ObjectDN\">([^<]+)</Data>", 1, EventData)
| extend ObjectTier = extract("(?i)OU=(Tier [^ ,]+)", 1, ObjectTier_e)
| extend ObjectTier = iff(ObjectTier == "", "Outside Tier Model", ObjectTier)
| extend ObjectTier = iff(ObjectTier_e has "OU=Domain Controllers", "Tier 0", ObjectTier)
| extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
Stage 4: where
| where ObjectClass !has "organizationalUnit" and ObjectClass !has "msFVE-RecoveryInformation"
Stage 5: extend (5 consecutive steps)
| extend ActivityType = extract("([a-zA-Z]+)\\.$", 1, Activity)
| extend SubjectUserName = extract("<Data Name=\"SubjectUserName\">([^<]+)</Data>", 1, EventData)
| extend SubjectDomainName = extract("<Data Name=\"SubjectDomainName\">([^<]+)</Data>", 1, EventData)
| extend Account = strcat(SubjectDomainName, "\\", SubjectUserName)
| extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)
Stage 6: project
| project TimeGenerated
, ObjectName
, ObjectTier
, ObjectClass
, ActivityType
, Account
, Domain
, Computer
, Channel
, EventID
, EventData
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventData | regex_match |
| field:"EventData" kind:regex_match |
EventID | eq |
| field:"EventID" kind:eq |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
ObjectClass | match | msFVE-RecoveryInformation | excludes:ObjectClass field:"ObjectClass" value:"msFVE-RecoveryInformation" |
ObjectClass | match | organizationalUnit | excludes:ObjectClass field:"ObjectClass" value:"organizationalUnit" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account | project |
ActivityType | project |
Channel | project |
Computer | project |
Domain | project |
EventData | project |
EventID | project |
ObjectClass | project |
ObjectName | project |
ObjectTier | project |
TimeGenerated | project |
SlackAudit - Public link created for file which can contain sensitive information.
#'Detects public links created for files that may contain sensitive data such as passwords, authentication tokens, secret keys, or private configuration files. Tune exclusions using the SlackAuditSensitiveFile_Allowlist_File and SlackAuditSensitiveFile_Allowlist_Account watchlists when known benign files or accounts generate expected public-link activity.'
MITRE ATT&CK Coverage
Rule Body
id: 279316e8-8965-47d2-9788-b94dc352c853
name: SlackAudit - Public link created for file which can contain sensitive information.
description: |
'Detects public links created for files that may contain sensitive data such as passwords, authentication tokens,
secret keys, or private configuration files. Tune exclusions using the SlackAuditSensitiveFile_Allowlist_File and SlackAuditSensitiveFile_Allowlist_Account
watchlists when known benign files or accounts generate expected public-link activity.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SlackAuditAPI
dataTypes:
- SlackAudit_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
relevantTechniques:
- T1048
- T1567.002
query: |
let AllowedFiles = toscalar(_GetWatchlist('SlackAuditSensitiveFile_Allowlist_File') | summarize make_set(tostring(SearchKey)));
let AllowedUsers = toscalar(_GetWatchlist('SlackAuditSensitiveFile_Allowlist_Account') | summarize make_set(tostring(SearchKey)));
SlackAudit
| where Action =~ 'file_public_link_created'
| extend FileNameLower = tolower(EntityFileName), UserLower = tolower(SrcUserName)
| where EntityFileName in~ ('id_rsa')
or FileNameLower has_any ('password', 'secret', 'token', 'credential', 'private key', 'api key')
or FileNameLower has_any ('.ssh', '.npmrc', '.muttrc', '.gitconfig', '.netrc', 'package.json', 'Gemfile', 'bower.json', 'config.gypi', 'travis.yml', 'config.json')
| where isempty(AllowedFiles) or EntityFileName !in~ (AllowedFiles)
| where isempty(AllowedUsers) or UserLower !in~ (AllowedUsers)
| extend AccountCustomEntity = SrcUserName
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
alertDetailsOverride:
alertDisplayNameFormat: Slack public link created for sensitive file {{EntityFileName}} by {{SrcUserName}}
alertDescriptionFormat: Public link created for {{EntityFileName}} by {{SrcUserName}} from {{SrcIpAddr}}
customDetails:
FileName: EntityFileName
Action: Action
SourceUser: SrcUserName
SourceIP: SrcIpAddr
ActorEntity: AccountCustomEntity
IpEntity: IPCustomEntity
version: 1.0.1
kind: Scheduled
Stages and Predicates
Let Binding: AllowedFiles
let AllowedFiles = toscalar(_GetWatchlist('SlackAuditSensitiveFile_Allowlist_File') | summarize make_set(tostring(SearchKey)));
Let Binding: AllowedUsers
let AllowedUsers = toscalar(_GetWatchlist('SlackAuditSensitiveFile_Allowlist_Account') | summarize make_set(tostring(SearchKey)));
Stage 1: source
SlackAudit
Stage 2: where
| where Action =~ 'file_public_link_created'
Stage 3: extend
| extend FileNameLower = tolower(EntityFileName), UserLower = tolower(SrcUserName)
Stage 4: where
| where EntityFileName in~ ('id_rsa')
or FileNameLower has_any ('password', 'secret', 'token', 'credential', 'private key', 'api key')
or FileNameLower has_any ('.ssh', '.npmrc', '.muttrc', '.gitconfig', '.netrc', 'package.json', 'Gemfile', 'bower.json', 'config.gypi', 'travis.yml', 'config.json')
Stage 5: where
| where isempty(AllowedFiles) or EntityFileName !in~ (AllowedFiles)
Stage 6: where
| where isempty(AllowedUsers) or UserLower !in~ (AllowedUsers)
Stage 7: extend
| extend AccountCustomEntity = SrcUserName
Stage 8: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | eq |
| field:"Action" kind:eq value:"file_public_link_created" |
AllowedFiles | is_null | field:"AllowedFiles" kind:is_null | |
AllowedUsers | is_null | field:"AllowedUsers" kind:is_null | |
EntityFileName | in |
| field:"EntityFileName" kind:in value:"id_rsa" |
FileNameLower | match |
| field:"FileNameLower" kind:match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
FileNameLower | extend |
UserLower | extend |
AccountCustomEntity | extend |
IPCustomEntity | extend |
Suspicious Network Connections - Supply Chain Attack
#Below query detects unusual network conenctions from servers that have 3rd party software installed.
You can further improve the query by using a list of servers that have privileges across the whole domain.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control | No specific technique |
Telemetry Coverage
Rule Body
// Author: Cyb3rMonk(https://twitter.com/Cyb3rMonk, https://mergene.medium.com)
//
// Query parameters:
let lookback = 14d;
// Generate list of all Servers
let server_list =
DeviceInfo
| where Timestamp > ago(lookback)
| where isnotempty(OSPlatform)
| where DeviceType <> "Workstation" and OSPlatform <> "macOS"
| summarize make_set(DeviceName)
;
// Generate list of servers that have 3rd party software installed.
// Criteria: if a software is installed on less than 10 servers, it's probably a 3rd party software.
// There are probably(hopefully) just a few servers or server groups that have privileges across the whole domain.
// You can change the threshold according to your environment.
let ServersWithThirdPartyApps = materialize (
DeviceNetworkEvents
| where Timestamp > ago(lookback)
| where DeviceName in (server_list)
| where ActionType <> "ListeningConnectionCreated"
| where RemoteIPType !in ("Private","Loopback") and (not (RemoteIPType == "FourToSixMapping" and RemoteIP startswith "::ffff:"))
| summarize dcount(DeviceName) by InitiatingProcessVersionInfoCompanyName
| where dcount_DeviceName < 10
| join kind=inner (
DeviceNetworkEvents
| where Timestamp > ago(lookback)
| where DeviceName in (server_list)
| where ActionType <> "ListeningConnectionCreated"
| where RemoteIPType !in ("Private","Loopback") and (not (RemoteIPType == "FourToSixMapping" and RemoteIP startswith "::ffff:"))
)
on InitiatingProcessVersionInfoCompanyName
| summarize make_set(DeviceName)
)
;
// Get network connection statistics
let baseline = materialize (
DeviceNetworkEvents
| where Timestamp > ago(lookback)
| where DeviceName in (ServersWithThirdPartyApps)
| where ActionType <> "ListeningConnectionCreated"
| where RemoteIPType !in ("Private","Loopback") and (not (RemoteIPType == "FourToSixMapping" and RemoteIP startswith "::ffff:"))
| summarize hint.strategy=shuffle Count=count(), starttime = min(Timestamp), endtime = max(Timestamp) by DeviceName, RemoteIP, RemotePort
)
;
// Get destination IP
let Destinations = baseline | summarize make_set(RemoteIP);
// Filter connections that was not seen before last 1d
// Generate prevalence info, URL info(if available) and enrich results
// Filter based on prevalence and URL information and display everything by hostname
baseline
| where starttime > ago(1d)
| lookup kind=leftouter (
DeviceNetworkEvents
| where Timestamp > ago(5d)
| where RemoteIP in (Destinations)
| summarize hint.strategy=shuffle Prevalence = dcount(DeviceId), URLs=make_set(RemoteUrl) by RemoteIP
)
on RemoteIP
| where Prevalence < 6 or isempty( Prevalence)
// If you want to see all the events in distinct rows, remove the below 2 lines
// filter out results based on trusted URLs if you like.
| extend Details = pack('RemoteIP',RemoteIP, 'RemotePort',RemotePort, 'Count',Count, 'Prevalence',Prevalence, 'URLs',URLs)
| summarize make_set(Details) by DeviceName
Stages and Predicates
Parameters
let lookback = 14d;
let baseline is inlined into the numbered stages below.
Let Binding: server_list
let server_list = DeviceInfo
| where Timestamp > ago(lookback)
| where isnotempty(OSPlatform)
| where DeviceType <> "Workstation" and OSPlatform <> "macOS"
| summarize make_set(DeviceName);
Let Binding: ServersWithThirdPartyApps
let ServersWithThirdPartyApps = materialize (
DeviceNetworkEvents
| where Timestamp > ago(lookback)
| where DeviceName in (server_list)
| where ActionType <> "ListeningConnectionCreated"
| where RemoteIPType !in ("Private","Loopback") and (not (RemoteIPType == "FourToSixMapping" and RemoteIP startswith "::ffff:"))
| summarize dcount(DeviceName) by InitiatingProcessVersionInfoCompanyName
| where dcount_DeviceName < 10
| join kind=inner (
DeviceNetworkEvents
| where Timestamp > ago(lookback)
| where DeviceName in (server_list)
| where ActionType <> "ListeningConnectionCreated"
| where RemoteIPType !in ("Private","Loopback") and (not (RemoteIPType == "FourToSixMapping" and RemoteIP startswith "::ffff:"))
)
on InitiatingProcessVersionInfoCompanyName
| summarize make_set(DeviceName)
);
Let Binding: Destinations
let Destinations = baseline | summarize make_set(RemoteIP);
Stage 1: source
let server_list
Stage 2: source
let ServersWithThirdPartyApps
Stage 3: source
let baseline
Stage 4: source
let Destinations
Stage 5: source
DeviceNetworkEvents
Stage 6: where
where Timestamp > ago(1209600s)
Stage 7: where
where DeviceName == "ServersWithThirdPartyApps"
Stage 8: where
where ActionType != "ListeningConnectionCreated"
Stage 9: where
where not ((RemoteIP startswith "::ffff:" and RemoteIPType == "FourToSixMapping")) and not (RemoteIPType in ("Loopback", "Private"))
Stage 10: summarize
summarize Count, endtime, starttime by DeviceName, RemoteIP, RemotePort
Stage 11: where
where starttime > ago(86400s)
Stage 12: kusto:lookup
lookup kind=leftouter (DeviceNetworkEvents) on RemoteIP
Stage 13: where
where (isempty(Prevalence) or Prevalence < 6)
Stage 14: extend
extend Details
Stage 15: summarize
summarize by DeviceName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | ne |
| field:"ActionType" kind:ne value:"ListeningConnectionCreated" |
DeviceName | in |
| field:"DeviceName" kind:in value:"ServersWithThirdPartyApps" |
Prevalence | is_null | field:"Prevalence" kind:is_null | |
Prevalence | lt |
| field:"Prevalence" kind:lt value:"6" |
RemoteIP | in |
| field:"RemoteIP" kind:in value:"Destinations" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
RemoteIP | starts_with | ::ffff: | excludes:RemoteIP field:"RemoteIP" value:"::ffff:" |
RemoteIPType | eq | FourToSixMapping | excludes:RemoteIPType field:"RemoteIPType" value:"FourToSixMapping" |
RemoteIPType | in | Loopback, Private | excludes:RemoteIPType field:"RemoteIPType" value:"Loopback" field:"RemoteIPType" value:"Private" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeviceName | summarize |
TI Map IP entity to DNS Events (ASIM DNS schema)
#This rule identifies DNS requests for which response IP address is a known IoC. This analytic rule uses ASIM and supports any built-in or custom source that supports the ASIM DNS schema.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event ID 22: DNSEvent (DNS query) |
Rule Body
id: b306fba8-1a28-449f-aa24-30362e16d4f5
name: TI Map IP entity to DNS Events (ASIM DNS schema)
description: |
'This rule identifies DNS requests for which response IP address is a known IoC. This analytic rule uses [ASIM](https://aka.ms/AboutASIM) and supports any built-in or custom source that supports the ASIM DNS schema.'
severity: Medium
requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelIndicators
- connectorId: DNS
dataTypes:
- DnsEvents
- connectorId: AzureFirewall
dataTypes:
- AzureDiagnostics
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog
- connectorId: InfobloxNIOS
dataTypes:
- Syslog
- connectorId: GCPDNSDataConnector
dataTypes:
- GCP_DNS_CL
- connectorId: NXLogDnsLogs
dataTypes:
- NXLog_DNS_Server_CL
- connectorId: CiscoUmbrellaDataConnector
dataTypes:
- Cisco_Umbrella_dns_CL
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: Corelight
dataTypes:
- Corelight_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
tags:
- ParentAlert: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/ThreatIntelligenceIndicator/IPEntity_DnsEvents.yaml
version: 1.0.0
- Schema: ASIMDns
SchemaVersion: 0.1.1
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_TI =
ThreatIntelIndicators
| where TimeGenerated >= ago(ioc_lookBack)
//extract key part of kv pair
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType == "ipv4-addr"
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| extend NetworkSourceIP = ObservableValue
| extend IoC = NetworkSourceIP
| extend IndicatorId = tostring(split(Id, "--")[2])
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil));
IP_TI
| project-reorder *, IsActive, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, IoC
| join kind=innerunique // 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
(
_Im_Dns(starttime=ago(dt_lookBack))
| where isnotempty(DnsResponseName)
| summarize imDns_mintime=min(TimeGenerated), imDns_maxtime=max(TimeGenerated) by SrcIpAddr, DnsQuery, DnsResponseName, Dvc, EventProduct, EventVendor
| extend addresses = extract_all (@'(\d+\.\d+\.\d+\.\d+)', DnsResponseName)
| mv-expand IoC = addresses to typeof(string)
)
on IoC
| where imDns_mintime < ValidUntil
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| extend ThreatType = tostring(Data.indicator_types[0])
| project imDns_mintime, imDns_maxtime, Description, ActivityGroupNames, IndicatorId, ThreatType, LatestIndicatorTime, ValidUntil, Confidence,
SrcIpAddr, IoC, Dvc, EventVendor, EventProduct, DnsQuery, DnsResponseName, Type
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Dvc
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IoC
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
customDetails:
LatestIndicatorTime: LatestIndicatorTime
Description: Description
ActivityGroupNames: ActivityGroupNames
IndicatorId: IndicatorId
ThreatType: ThreatType
ExpirationDateTime: ValidUntil
ConfidenceScore: Confidence
DNSRequestTime: imDns_mintime
SourceIPAddress: SrcIpAddr
DnsQuery: DnsQuery
alertDetailsOverride:
alertDisplayNameFormat: The response {{IoC}} to DNS query matched an IoC
alertDescriptionFormat: The response address {{IoC}} to a DNS query matched a known indicator of compromise of {{ThreatType}}. Consult the threat intelligence blade for more information on the indicator.
version: 1.2.11
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_TI is inlined into the numbered stages below.
Stages 1 to 7 define let IP_TI as the rule's main pipeline source. Stages 8 to 12 use it.
Stage 1: source
ThreatIntelIndicators
Stage 2: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 3: extend
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
Stage 4: where
| where IndicatorType == "ipv4-addr"
Stage 5: extend (4 consecutive steps)
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| extend NetworkSourceIP = ObservableValue
| extend IoC = NetworkSourceIP
| extend IndicatorId = tostring(split(Id, "--")[2])
Stage 6: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
Stage 7: where
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
Stage 8: project-reorder
IP_TI
| project-reorder *, IsActive, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, IoC
Stage 9: join
| join kind=innerunique
(
_Im_Dns(starttime=ago(dt_lookBack))
| where isnotempty(DnsResponseName)
| summarize imDns_mintime=min(TimeGenerated), imDns_maxtime=max(TimeGenerated) by SrcIpAddr, DnsQuery, DnsResponseName, Dvc, EventProduct, EventVendor
| extend addresses = extract_all (@'(\d+\.\d+\.\d+\.\d+)', DnsResponseName)
| mv-expand IoC = addresses to typeof(string)
)
on IoC
Stage 10: where
where imDns_mintime < ValidUntil
Stage 11: extend (3 consecutive steps)
extend ActivityGroupNames, Description, ThreatType
Stage 12: project
project ActivityGroupNames, Confidence, Description, DnsQuery, DnsResponseName, Dvc, EventProduct, EventVendor, IndicatorId, IoC, LatestIndicatorTime, SrcIpAddr, ThreatType, Type, ValidUntil, imDns_maxtime, imDns_mintime
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DnsResponseName | is_not_null | field:"DnsResponseName" kind:is_not_null | |
IndicatorType | eq |
| field:"IndicatorType" kind:eq value:"ipv4-addr" |
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
ValidUntil | is_null | field:"ValidUntil" kind:is_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityGroupNames | project |
Confidence | project |
Description | project |
DnsQuery | project |
DnsResponseName | project |
Dvc | project |
EventProduct | project |
EventVendor | project |
IndicatorId | project |
IoC | project |
LatestIndicatorTime | project |
SrcIpAddr | project |
ThreatType | project |
Type | project |
ValidUntil | project |
imDns_maxtime | project |
imDns_mintime | project |
TI map IP entity to Network Session Events (ASIM Network Session schema)
#This rule identifies a match Network Sessions for which the source or destination IP address is a known IoC. This analytic rule uses ASIM and supports any built-in or custom source that supports the ASIM NetworkSession schema
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry Coverage
Rule Body
id: e2399891-383c-4caf-ae67-68a008b9f89e
name: TI map IP entity to Network Session Events (ASIM Network Session schema)
description: |
'This rule identifies a match Network Sessions for which the source or destination IP address is a known IoC. This analytic rule uses [ASIM](https://aka.ms/AboutASIM) and supports any built-in or custom source that supports the ASIM NetworkSession schema'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AWSS3
dataTypes:
- AWSVPCFlow
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceNetworkEvents
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog
- connectorId: MicrosoftSysmonForLinux
dataTypes:
- Syslog
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
- connectorId: AzureFirewall
dataTypes:
- AzureDiagnostics
- connectorId: AzureNSG
dataTypes:
- AzureDiagnostics
- connectorId: CiscoASA
dataTypes:
- CommonSecurityLog
- connectorId: Corelight
dataTypes:
- Corelight_CL
- connectorId: AIVectraStream
dataTypes:
- VectraStream
- connectorId: CheckPoint
dataTypes:
- CommonSecurityLog
- connectorId: Fortinet
dataTypes:
- CommonSecurityLog
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: CiscoMeraki
dataTypes:
- Syslog
- CiscoMerakiNativePoller
- 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;
let IP_TI = materialize (
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack)
| extend TI_ipEntity = coalesce(NetworkIP, NetworkDestinationIP, NetworkSourceIP,EmailSourceIpAddress,"NO_IP")
| where TI_ipEntity != "NO_IP"
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
);
IP_TI
// 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
(
_Im_NetworkSession (starttime=ago(dt_lookBack))
| where isnotempty(SrcIpAddr)
| summarize imNWS_mintime=min(TimeGenerated), imNWS_maxtime=max(TimeGenerated) by SrcIpAddr, DstIpAddr, Dvc, EventProduct, EventVendor
| lookup (IP_TI | project TI_ipEntity, Active) on $left.SrcIpAddr == $right.TI_ipEntity
| project-rename SrcMatch = Active
| lookup (IP_TI | project TI_ipEntity, Active) on $left.DstIpAddr == $right.TI_ipEntity
| project-rename DstMatch = Active
| where SrcMatch or DstMatch
| extend
IoCIP = iff(SrcMatch, SrcIpAddr, DstIpAddr),
IoCDirection = iff(SrcMatch, "Source", "Destination")
)on $left.TI_ipEntity == $right.IoCIP
| where imNWS_mintime < ExpirationDateTime
| project imNWS_mintime, imNWS_maxtime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, SrcIpAddr, DstIpAddr, IoCDirection, IoCIP, Dvc, EventVendor, EventProduct
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IoCIP
customDetails:
EventStartTime: imNWS_mintime
EventEndTime: imNWS_maxtime
IoCDescription: Description
ActivityGroupNames: ActivityGroupNames
IndicatorId: IndicatorId
ThreatType: ThreatType
IoCExpirationTime: ExpirationDateTime
IoCConfidenceScore: ConfidenceScore
IoCIPDirection: IoCDirection
alertDetailsOverride:
alertDisplayNameFormat: A network session {{IoCDirection}} address {{IoCIP}} matched an IoC.
alertDescriptionFormat: The {{IoCDirection}} address {{IoCIP}} of a network session matched a known indicator of compromise of {{ThreatType}}. Consult the threat intelligence blead for more information on the indicator.
tags:
- Schema: ASIMNetworkSession
SchemaVersion: 0.2.4
version: 1.2.6
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_TI is inlined into the numbered stages below.
Stages 1 to 6 define let IP_TI as the rule's main pipeline source. Stages 7 to 9 use it.
Stage 1: source
ThreatIntelligenceIndicator
Stage 2: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 3: extend
| extend TI_ipEntity = coalesce(NetworkIP, NetworkDestinationIP, NetworkSourceIP,EmailSourceIpAddress,"NO_IP")
Stage 4: where
| where TI_ipEntity != "NO_IP"
Stage 5: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
Stage 6: where
| where Active == true and ExpirationDateTime > now()
Stage 7: join
join kind=innerunique (_Im_NetworkSession) on $left.TI_ipEntity, $right.IoCIP
Stage 8: where
where imNWS_mintime < ExpirationDateTime
Stage 9: project
project ActivityGroupNames, ConfidenceScore, Description, DstIpAddr, Dvc, EventProduct, EventVendor, ExpirationDateTime, IndicatorId, IoCDirection, IoCIP, SrcIpAddr, ThreatType, imNWS_maxtime, imNWS_mintime
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Active | eq |
| field:"Active" kind:eq value:"true" |
DstMatch | eq |
| field:"DstMatch" kind:eq value:"true" |
SrcIpAddr | is_not_null | field:"SrcIpAddr" kind:is_not_null | |
SrcMatch | eq |
| field:"SrcMatch" kind:eq value:"true" |
TI_ipEntity | ne |
| field:"TI_ipEntity" kind:ne value:"NO_IP" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityGroupNames | project |
ConfidenceScore | project |
Description | project |
DstIpAddr | project |
Dvc | project |
EventProduct | project |
EventVendor | project |
ExpirationDateTime | project |
IndicatorId | project |
IoCDirection | project |
IoCIP | project |
SrcIpAddr | project |
ThreatType | project |
imNWS_maxtime | project |
imNWS_mintime | project |
Utimaco ESKM - Burst of KMIP DESTROY operations by a single user
#Identifies a single KMIP user issuing 20 or more DESTROY operations within 10 minutes on Utimaco ESKM. May indicate mass key/object deletion, insider sabotage, or ransomware-style cryptographic-material destruction.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 9b1a3b3e-7e16-4a3b-8a8f-7f1f2b1c0a03
name: Utimaco ESKM - Burst of KMIP DESTROY operations by a single user
description: |
Identifies a single KMIP user issuing 20 or more DESTROY operations within 10 minutes on Utimaco ESKM. May indicate mass key/object deletion, insider sabotage, or ransomware-style cryptographic-material destruction.
severity: High
status: Available
requiredDataConnectors:
- connectorId: UtimacoESKMConnector
dataTypes:
- UtimacoESKMKmipServerLogs_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1485
query: |
UtimacoESKMKmipServerLogs_CL
| where TimeGenerated > ago(10m)
| where Operation == "DESTROY"
| where Result == "SUCCESS"
| where isnotempty(User)
| summarize DestroyCount = count(), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ObjectTypes = make_set(ObjectType, 25), UUIDs = make_set(UUID, 100), SourceIPs = make_set(IP, 25) by User
| where DestroyCount >= 20
| extend AccountCustomEntity = User
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
UtimacoESKMKmipServerLogs_CL
Stage 2: where
| where TimeGenerated > ago(10m)
Stage 3: where
| where Operation == "DESTROY"
Stage 4: where
| where Result == "SUCCESS"
Stage 5: where
| where isnotempty(User)
Stage 6: summarize
| summarize DestroyCount = count(), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ObjectTypes = make_set(ObjectType, 25), UUIDs = make_set(UUID, 100), SourceIPs = make_set(IP, 25) by User
Stage 7: where
| where DestroyCount >= 20
Stage 8: extend
| extend AccountCustomEntity = User
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DestroyCount | ge |
| field:"DestroyCount" kind:ge value:"20" |
Operation | eq |
| field:"Operation" kind:eq value:"DESTROY" |
Result | eq |
| field:"Result" kind:eq value:"SUCCESS" |
User | is_not_null | field:"user" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DestroyCount | summarize |
EndTime | summarize |
ObjectTypes | summarize |
SourceIPs | summarize |
StartTime | summarize |
UUIDs | summarize |
User | summarize |
AccountCustomEntity | extend |
VMware SD-WAN Edge - IDS/IPS Alert triggered (Search API)
#The VMware SD-WAN Edge appliance captured a potentially malicious traffic flow. Please investigate the IOC information available. This analytics rule analyses Search API streams. Search API queries report only IDS/IPS Alerts. In case you would also need Network Flood Protection, please enable Syslog collection using AMA.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement |
Rule Body
id: 44f78dbf-9f29-4ec0-aaca-ab5bf0b559af
name: VMware SD-WAN Edge - IDS/IPS Alert triggered (Search API)
version: 1.0.0
kind: Scheduled
description: |-
The VMware SD-WAN Edge appliance captured a potentially malicious traffic flow. Please investigate the IOC information available.
This analytics rule analyses Search API streams. Search API queries report only IDS/IPS Alerts. In case you would also need Network Flood Protection, please enable Syslog collection using AMA.
severity: High
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
requiredDataConnectors:
- connectorId: VMwareSDWAN
dataTypes:
- SDWAN
tactics:
- LateralMovement
relevantTechniques:
- T1210
query: |
VMware_SDWAN_FirewallLogs_CL
| project TimeGenerated, signature, severity, category, signatureId, actionTaken, edgeName, sourceIp, protocol, sourcePort, destinationIp, destinationPort, domainName, attackSource, attackTarget, firewallPolicyName
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1h
matchingMethod: AllEntities
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDynamicProperties: []
customDetails:
IDPS_Signature: signature
IDPS_Event_Category: category
entityMappings:
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: domainName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: sourceIp
suppressionDuration: 5h
Stages and Predicates
Stage 1: source
VMware_SDWAN_FirewallLogs_CL
Stage 2: project
| project TimeGenerated, signature, severity, category, signatureId, actionTaken, edgeName, sourceIp, protocol, sourcePort, destinationIp, destinationPort, domainName, attackSource, attackTarget, firewallPolicyName
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
TimeGenerated | project |
actionTaken | project |
attackSource | project |
attackTarget | project |
category | project |
destinationIp | project |
destinationPort | project |
domainName | project |
edgeName | project |
firewallPolicyName | project |
protocol | project |
severity | project |
signature | project |
signatureId | project |
sourceIp | project |
sourcePort | project |
XbowMediumFindings
#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 an incident for each Medium severity finding reported by XBOW that is currently in an open state. These findings represent moderate security risks that should be addressed in a timely manner. Each alert is deduplicated per finding so re-ingestion of the same finding does not produce duplicate incidents.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | No specific technique |
| Credential Access | No specific technique |
| Discovery | No specific technique |
Rule Body
id: b3c5e2f9-6a8d-4127-9b2e-4f6a8c9d0e12
name: XbowMediumFindings
description: |
Creates an incident for each Medium severity finding reported by XBOW that is currently
in an open state. These findings represent moderate security risks that should be
addressed in a timely manner. Each alert is deduplicated per finding so re-ingestion
of the same finding does not produce duplicate incidents.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: XbowSecurityConnector
dataTypes:
- XbowFindings_CL
- XbowAssets_CL
queryFrequency: 30m
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
- Reconnaissance
- CredentialAccess
relevantTechniques: []
query: |
XbowFindings_CL
| where TimeGenerated > ago(1h)
| where tolower(Severity) == 'medium'
| where isempty(State) or tolower(State) == 'open'
| summarize arg_max(TimeGenerated, *) by FindingId
| join kind=leftouter (
XbowAssets_CL
| summarize arg_max(TimeGenerated, *) by AssetId
| project AssetId, StartUrl
) on AssetId
| project
TimeGenerated,
FindingId,
FindingName,
Severity,
State,
Summary,
Impact,
Mitigations,
Recipe,
AssetId,
AssetName,
OrganizationId,
CreatedAt,
StartUrl
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: 'XBOW Medium: {{FindingName}}'
alertDescriptionFormat: 'Medium severity finding on asset {{AssetName}} ({{AssetId}}). {{Summary}}'
customDetails:
FindingID: FindingId
FindingName: FindingName
Severity: Severity
State: State
AssetID: AssetId
AssetName: AssetName
OrganizationID: OrganizationId
CreatedAt: CreatedAt
Mitigations: Mitigations
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 24h
matchingMethod: Selected
groupByCustomDetails:
- FindingID
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: StartUrl
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
XbowFindings_CL
Stage 2: where
| where TimeGenerated > ago(1h)
Stage 3: where
| where tolower(Severity) == 'medium'
Stage 4: where
| where isempty(State) or tolower(State) == 'open'
Stage 5: summarize
| summarize arg_max(TimeGenerated, *) by FindingId
Stage 6: join
| join kind=leftouter (
XbowAssets_CL
| summarize arg_max(TimeGenerated, *) by AssetId
| project AssetId, StartUrl
) on AssetId
Stage 7: project
project AssetId, AssetName, CreatedAt, FindingId, FindingName, Impact, Mitigations, OrganizationId, Recipe, Severity, StartUrl, State, Summary, TimeGenerated
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Severity | eq |
| field:"Severity" kind:eq value:"medium" |
State | eq |
| field:"State" kind:eq value:"open" |
State | is_null | field:"State" kind:is_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AssetId | project |
AssetName | project |
CreatedAt | project |
FindingId | project |
FindingName | project |
Impact | project |
Mitigations | project |
OrganizationId | project |
Recipe | project |
Severity | project |
StartUrl | project |
State | project |
Summary | project |
TimeGenerated | project |