Detection rules › Kusto
Kusto rules: 2k
CiscoISE - Backup failed
#'Detects when backup activity failed.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 4eddd44a-25e4-41af-930d-0c17218bec74
name: CiscoISE - Backup failed
description: |
'Detects when backup activity failed.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1490
query: |
let lbtime = 6h;
CiscoISEEvent
| where TimeGenerated > ago(lbtime)
| where EventId in ('60095', '60098')
| project TimeGenerated, DvcHostname, DvcIpAddr
| extend HostCustomEntity = DvcHostname
| extend IPCustomEntity = DvcIpAddr
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: HostCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Parameters
let lbtime = 6h;
Stage 1: source
CiscoISEEvent
Stage 2: where
| where TimeGenerated > ago(lbtime)
Stage 3: where
| where EventId in ('60095', '60098')
Stage 4: project
| project TimeGenerated, DvcHostname, DvcIpAddr
Stage 5: extend
| extend HostCustomEntity = DvcHostname
Stage 6: extend
| extend IPCustomEntity = DvcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventId | in |
| field:"EventId" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DvcHostname | project |
DvcIpAddr | project |
TimeGenerated | project |
HostCustomEntity | extend |
IPCustomEntity | extend |
Digital Guardian - Exfiltration using DNS protocol
#'Detects exfiltration using DNS protocol.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Rule Body
id: 39e25deb-49bb-4cdb-89c1-c466d596e2bd
name: Digital Guardian - Exfiltration using DNS protocol
description: |
'Detects exfiltration using DNS protocol.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
relevantTechniques:
- T1048
query: |
DigitalGuardianDLPEvent
| where DstPortNumber == 53
| extend AccountCustomEntity = SrcUserName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
DigitalGuardianDLPEvent
Stage 2: where
| where DstPortNumber == 53
Stage 3: extend
| extend AccountCustomEntity = SrcUserName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DstPortNumber | eq |
| field:"DstPortNumber" kind:eq value:"53" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
Distributed Password cracking attempts in Microsoft Entra ID
#Identifies distributed password cracking attempts from the Microsoft Entra ID SigninLogs. The query looks for unusually high number of failed password attempts coming from multiple locations for a user account. References: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes 50053 Account is locked because the user tried to sign in too many times with an incorrect user ID or password. 50055 Invalid password, entered expired password. 50056 Invalid or null password - Password does not exist in store for this user. 50126 Invalid username or password, or invalid on-premises username or password.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Telemetry Coverage
Rule Body
id: bfb1c90f-8006-4325-98be-c7fffbc254d6
name: Distributed Password cracking attempts in Microsoft Entra ID
description: |
'Identifies distributed password cracking attempts from the Microsoft Entra ID SigninLogs.
The query looks for unusually high number of failed password attempts coming from multiple locations for a user account.
References: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes
50053 Account is locked because the user tried to sign in too many times with an incorrect user ID or password.
50055 Invalid password, entered expired password.
50056 Invalid or null password - Password does not exist in store for this user.
50126 Invalid username or password, or invalid on-premises username or password.'
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: AzureActiveDirectory
dataTypes:
- AADNonInteractiveUserSignInLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- CredentialAccess
relevantTechniques:
- T1110
query: |
let s_threshold = 30;
let l_threshold = 3;
let aadFunc = (tableName:string){
table(tableName)
| where OperationName =~ "Sign-in activity"
// Error codes that we want to look at as they are related to the use of incorrect password.
| where ResultType in ("50126", "50053" , "50055", "50056")
| extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(DeviceDetail), LocationDetails = todynamic(LocationDetails)
| extend OS = DeviceDetail.operatingSystem, Browser = DeviceDetail.browser
| extend StatusCode = tostring(Status.errorCode), StatusDetails = tostring(Status.additionalDetails)
| extend LocationString = strcat(tostring(LocationDetails.countryOrRegion), "/", tostring(LocationDetails.state), "/", tostring(LocationDetails.city))
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), LocationCount=dcount(LocationString), Location = make_set(LocationString,100),
IPAddress = make_set(IPAddress,100), IPAddressCount = dcount(IPAddress), AppDisplayName = make_set(AppDisplayName,100), ResultDescription = make_set(ResultDescription,50),
Browser = make_set(Browser,20), OS = make_set(OS,20), SigninCount = count() by UserPrincipalName, Type
// Setting a generic threshold - Can be different for different environment
| where SigninCount > s_threshold and LocationCount >= l_threshold
| extend Location = tostring(Location), IPAddress = tostring(IPAddress), AppDisplayName = tostring(AppDisplayName), ResultDescription = tostring(ResultDescription), Browser = tostring(Browser), OS = tostring(OS)
| extend Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])};
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
union isfuzzy=true aadSignin, aadNonInt
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserPrincipalName
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
version: 1.0.5
kind: Scheduled
Stages and Predicates
Parameters
let s_threshold = 30;
let l_threshold = 3;
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
Let Binding: aadFunc
let aadFunc = (tableName:string){
table(tableName)
| where OperationName =~ "Sign-in activity"
| where ResultType in ("50126", "50053" , "50055", "50056")
| extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(DeviceDetail), LocationDetails = todynamic(LocationDetails)
| extend OS = DeviceDetail.operatingSystem, Browser = DeviceDetail.browser
| extend StatusCode = tostring(Status.errorCode), StatusDetails = tostring(Status.additionalDetails)
| extend LocationString = strcat(tostring(LocationDetails.countryOrRegion), "/", tostring(LocationDetails.state), "/", tostring(LocationDetails.city))
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), LocationCount=dcount(LocationString), Location = make_set(LocationString,100),
IPAddress = make_set(IPAddress,100), IPAddressCount = dcount(IPAddress), AppDisplayName = make_set(AppDisplayName,100), ResultDescription = make_set(ResultDescription,50),
Browser = make_set(Browser,20), OS = make_set(OS,20), SigninCount = count() by UserPrincipalName, Type
| where SigninCount > s_threshold and LocationCount >= l_threshold
| extend Location = tostring(Location), IPAddress = tostring(IPAddress), AppDisplayName = tostring(AppDisplayName), ResultDescription = tostring(ResultDescription), Browser = tostring(Browser), OS = tostring(OS)
| extend Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])};
Union isfuzzy=true (2 Sources)
Each leg below queries one source; the rule matches if any leg does. Sources: aadSignin, aadNonInt
Leg 1: aadSignin
Leg 2: aadNonInt
Excessive Failed Authentication from Invalid Inputs
#'Creates an incident in the event that a user generates an excessive amount of failed authentications due to invalid inputs, indications of a potential brute force.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Rule Body
id: c775a46b-21b1-46d7-afa6-37e3e577a27b
name: Excessive Failed Authentication from Invalid Inputs
description: |
'Creates an incident in the event that a user generates an excessive amount of failed authentications due to invalid inputs, indications of a potential brute force.'
severity: Medium
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1110
query: |
let threshold = 15;
SymantecVIP
| where isnotempty(RADIUSAuth)
| where RADIUSAuth =~ "Reject"
| summarize Total = count() by bin(TimeGenerated, 15m), User, ClientIP
| where Total > threshold
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: User
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIP
version: 1.0.4
status: Available
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 15;
Stage 1: source
SymantecVIP
Stage 2: where
| where isnotempty(RADIUSAuth)
Stage 3: where
| where RADIUSAuth =~ "Reject"
Stage 4: summarize
| summarize Total = count() by bin(TimeGenerated, 15m), User, ClientIP
Stage 5: where
| where Total > threshold
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
RADIUSAuth | eq |
| field:"RADIUSAuth" kind:eq value:"Reject" |
RADIUSAuth | is_not_null | field:"RADIUSAuth" kind:is_not_null | |
Total | gt |
| field:"Total" kind:gt value:"15" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ClientIP | summarize |
Total | summarize |
User | summarize |
Idira - Sensitive Safe/Permission/Entitlement Changes (with customData)
#Alerts on control-plane modifications: safes, permissions, roles, entitlements, policy changes. Leverages customData fields such as changeType/role/permission/policy/entitlement to reduce misses.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation | No specific technique |
Rule Body
id: 30938118-8812-4b5f-afa4-a8d4ba2b5d86
kind: Scheduled
name: Idira - Sensitive Safe/Permission/Entitlement Changes (with customData)
description: >
Alerts on control-plane modifications: safes, permissions, roles, entitlements, policy changes.
Leverages customData fields such as changeType/role/permission/policy/entitlement to reduce misses.
severity: Low
tactics:
- PrivilegeEscalation
queryFrequency: 10M
queryPeriod: 1D
triggerOperator: GreaterThan
triggerThreshold: 0
query: |
let indicators = dynamic(["SafeMember","Permission","Access","ACL","Owner","Role","Entitlement","Policy"]);
let changeVerbs = dynamic(["grant","revoke","assign","elevate","enable","disable","remove","delete","update"]);
let actions = dynamic(["policy-add", "policy-change"]);
CyberArk_AuditEvents_CL
| extend cd = parse_json(tostring(customData))
| extend cd_changeType = tolower(tostring(cd.changeType)),
cd_role = tostring(cd.role),
cd_perm = tostring(cd.permission),
cd_policy = tostring(cd.policy),
cd_entitle = tostring(cd.entitlement),
cd_description= tostring(cd.description),
cd_action = tostring(cd.action)
| where
action has_any(actions) and
(auditType has_any (indicators)
or message has_any (indicators)
or cd_description has_any (indicators)
or cd_action has_any (indicators)
or cd_role !~ "" or cd_perm !~ "" or cd_policy !~ "" or cd_entitle !~ ""
or cd_changeType in (changeVerbs))
| project TimeGenerated, CyberArkTenantId, serviceName, username, userId, safe,
action, actionType, auditType,
cd_action, cd_description, cd_changeType, cd_role, cd_perm, cd_policy, cd_entitle,
target, message
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: username
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: target
suppressionEnabled: false
version: 1.0.0
Stages and Predicates
Parameters
let indicators = dynamic(["SafeMember","Permission","Access","ACL","Owner","Role","Entitlement","Policy"]);
let changeVerbs = dynamic(["grant","revoke","assign","elevate","enable","disable","remove","delete","update"]);
let actions = dynamic(["policy-add", "policy-change"]);
Stage 1: source
CyberArk_AuditEvents_CL
Stage 2: extend
| extend cd = parse_json(tostring(customData))
Stage 3: extend
| extend cd_changeType = tolower(tostring(cd.changeType)),
cd_role = tostring(cd.role),
cd_perm = tostring(cd.permission),
cd_policy = tostring(cd.policy),
cd_entitle = tostring(cd.entitlement),
cd_description= tostring(cd.description),
cd_action = tostring(cd.action)
Stage 4: where
| where
action has_any(actions) and
(auditType has_any (indicators)
or message has_any (indicators)
or cd_description has_any (indicators)
or cd_action has_any (indicators)
or cd_role !~ "" or cd_perm !~ "" or cd_policy !~ "" or cd_entitle !~ ""
or cd_changeType in (changeVerbs))
Stage 5: project
| project TimeGenerated, CyberArkTenantId, serviceName, username, userId, safe,
action, actionType, auditType,
cd_action, cd_description, cd_changeType, cd_role, cd_perm, cd_policy, cd_entitle,
target, message
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | match |
| field:"action" kind:match |
auditType | match |
| field:"auditType" kind:match |
cd_action | match |
| field:"cd_action" kind:match |
cd_changeType | in |
| field:"cd_changeType" kind:in |
cd_description | match |
| field:"cd_description" kind:match |
message | match |
| field:"message" kind:match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
CyberArkTenantId | project |
TimeGenerated | project |
action | project |
actionType | project |
auditType | project |
cd_action | project |
cd_changeType | project |
cd_description | project |
cd_entitle | project |
cd_perm | project |
cd_policy | project |
cd_role | project |
message | project |
safe | project |
serviceName | project |
target | project |
userId | project |
username | project |
Internet Access (Microsoft Defender for IoT)
#'This alert leverages Defender for IoT to detect an OT device communicating with Internet which is possibly an indication of improper configuration of an application or malicious activity on the network.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Lateral Movement |
Rule Body
id: 9ff3b13b-287a-4ed0-8f6b-7e7b66cbbcbd
name: Internet Access (Microsoft Defender for IoT)
description: |
'This alert leverages Defender for IoT to detect an OT device communicating with Internet which is possibly an indication of improper configuration of an application or malicious activity on the network.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: IoT
dataTypes:
- SecurityAlert (ASC for IoT)
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- LateralMovement
relevantTechniques:
- T0886
query: |
SecurityAlert
| where ProviderName == "IoTSecurity"
| where AlertName has "Internet"
| 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 "Internet"
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 value:"Internet" |
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 |
Malicious web application requests linked with Microsoft Defender for Endpoint (formerly Microsoft Defender ATP) alerts
#'Takes Microsoft Defender for Endpoint (formerly Microsoft Defender ATP) alerts where web scripts are present in the evidence and correlates with requests made to those scripts in the WCSIISLog to surface new alerts for potentially malicious web request activity. The lookback for alerts is set to 1h and the lookback for W3CIISLogs is set to 7d. A sample set of popular web script extensions has been provided in scriptExtensions that should be tailored to your environment.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Rule Body
id: fbfbf530-506b-49a4-81ad-4030885a195c
name: Malicious web application requests linked with Microsoft Defender for Endpoint (formerly Microsoft Defender ATP) alerts
description: |
'Takes Microsoft Defender for Endpoint (formerly Microsoft Defender ATP) alerts where web scripts are present in the evidence and correlates with requests made to those scripts in the WCSIISLog to surface new alerts for potentially malicious web request activity.
The lookback for alerts is set to 1h and the lookback for W3CIISLogs is set to 7d. A sample set of popular web script extensions has been provided in scriptExtensions that should be tailored to your environment.'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftDefenderAdvancedThreatProtection
dataTypes:
- SecurityAlert
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
queryFrequency: 1h
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1505
query: |
let alertTimeWindow = 1h;
let logTimeWindow = 7d;
// Define script extensions that suit your web application environment - a sample are provided below
let scriptExtensions = dynamic([".php", ".jsp", ".js", ".aspx", ".asmx", ".asax", ".cfm", ".shtml"]);
let alertData = materialize(SecurityAlert
| where TimeGenerated > ago(alertTimeWindow)
| where ProviderName == "MDATP"
// Parse and expand the alert JSON
| extend alertData = parse_json(Entities)
| mvexpand alertData);
let fileData = alertData
// Extract web script files from MDATP alerts - our malicious web scripts - candidate webshells
| where alertData.Type =~ "file"
| where alertData.Name has_any(scriptExtensions)
| extend FileName = tostring(alertData.Name), Directory = tostring(alertData.Directory);
let hostData = alertData
// Extract server details from alerts and map to alert id
| where alertData.Type =~ "host"
| project HostName = tostring(alertData.HostName), DnsDomain = tostring(alertData.DnsDomain), SystemAlertId
| distinct HostName, DnsDomain, SystemAlertId;
// Join the files on their impacted servers
let webshellData = fileData
| join kind=inner (hostData) on SystemAlertId
| project TimeGenerated, FileName, Directory, HostName, DnsDomain;
webshellData
| join (
// Find requests that were made to this file on the impacted server in the W3CIISLog table
W3CIISLog
| where TimeGenerated > ago(logTimeWindow)
// Restrict to accesses to script extensions
| where csUriStem has_any(scriptExtensions)
| extend splitUriStem = split(csUriStem, "/")
| extend FileName = splitUriStem[-1], HostName = sComputerName
// Summarize potential attacker activity
| summarize count(), StartTime=min(TimeGenerated), EndTime=max(TimeGenerated), RequestUserAgents=make_set(csUserAgent), ReqestMethods=make_set(csMethod), RequestStatusCodes=make_set(scStatus), RequestCookies=make_set(csCookie), RequestReferers=make_set(csReferer), RequestQueryStrings=make_set(csUriQuery) by AttackerIP=cIP, SiteName=sSiteName, ShellLocation=csUriStem, tostring(FileName), HostName
) on FileName, HostName
| project StartTime, EndTime, AttackerIP, RequestUserAgents, Computer = HostName, SiteName, ShellLocation, ReqestMethods, RequestStatusCodes, RequestCookies, RequestReferers, RequestQueryStrings, RequestCount = count_
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: HostNameDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: AttackerIP
version: 1.0.4
kind: Scheduled
metadata:
source:
kind: Scheduled
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Others" ]
Stages and Predicates
Parameters
let alertTimeWindow = 1h;
let logTimeWindow = 7d;
let scriptExtensions = dynamic([".php", ".jsp", ".js", ".aspx", ".asmx", ".asax", ".cfm", ".shtml"]);
let alertData, let fileData and let webshellData are inlined into the numbered stages below.
Let Binding: hostData
let hostData = alertData
| where alertData.Type =~ "host"
| project HostName = tostring(alertData.HostName), DnsDomain = tostring(alertData.DnsDomain), SystemAlertId
| distinct HostName, DnsDomain, SystemAlertId;
Stages 1 to 10 define let webshellData as the rule's main pipeline source. Stages 11 to 15 use it.
Stage 1: source
SecurityAlert
Stage 2: where
where TimeGenerated > ago(3600s)
Stage 3: where
where ProviderName == "MDATP"
Stage 4: extend
extend alertData
Stage 5: mv-expand
mv-expand alertData
Stage 6: where
where alertData.Type =~ "file"
Stage 7: where
where (alertData.Name has ".php" or alertData.Name has ".jsp" or alertData.Name has ".js" or alertData.Name has ".aspx" or alertData.Name has ".asmx" or alertData.Name has ".asax" or alertData.Name has ".cfm" or alertData.Name has ".shtml")
Stage 8: extend
extend Directory, FileName
Stage 9: join
| join kind=inner (hostData) on SystemAlertId
Stage 10: project
project Directory, DnsDomain, FileName, HostName, TimeGenerated
Stage 11: join
webshellData
| join (
W3CIISLog
| where TimeGenerated > ago(logTimeWindow)
| where csUriStem has_any(scriptExtensions)
| extend splitUriStem = split(csUriStem, "/")
| extend FileName = splitUriStem[-1], HostName = sComputerName
| summarize count(), StartTime=min(TimeGenerated), EndTime=max(TimeGenerated), RequestUserAgents=make_set(csUserAgent), ReqestMethods=make_set(csMethod), RequestStatusCodes=make_set(scStatus), RequestCookies=make_set(csCookie), RequestReferers=make_set(csReferer), RequestQueryStrings=make_set(csUriQuery) by AttackerIP=cIP, SiteName=sSiteName, ShellLocation=csUriStem, tostring(FileName), HostName
) on FileName, HostName
Stage 12: project
project AttackerIP, Computer, EndTime, ReqestMethods, RequestCookies, RequestCount, RequestQueryStrings, RequestReferers, RequestStatusCodes, RequestUserAgents, ShellLocation, SiteName, StartTime
Stage 13: extend
extend DomainIndex, HostName
Stage 14: extend
extend HostNameDomain
HostNameDomain =DomainIndex != -1substring(Computer, (DomainIndex + 1))ComputerStage 15: summarize aggregation inside the join branch
summarize by AttackerIP, SiteName, ShellLocation, HostName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ProviderName | eq |
| field:"ProviderName" kind:eq value:"MDATP" |
alertData.Name | match |
| field:"alertData.Name" kind:match |
alertData.Type | eq |
| field:"alertData.Type" kind:eq |
csUriStem | match |
| field:"csUriStem" kind:match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AttackerIP | summarize |
HostName | summarize |
ShellLocation | summarize |
SiteName | summarize |
McAfee ePO - Deployment failed
#Detects when errors occur during deployment new changes/policies.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| McAfee-ePolicy-Orchestrator | Event ID 2412: Deployment failed |
Rule Body
id: 155243f4-d962-4717-8a7b-b15b6d112660
name: McAfee ePO - Deployment failed
description: |
'Detects when errors occur during deployment new changes/policies.'
severity: High
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- DefenseEvasion
relevantTechniques:
- T1562
query: |
McAfeeEPOEvent
| where EventId == '2412'
| extend IPCustomEntity = DvcIpAddr, HostCustomEntity = DvcHostname
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: HostCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
McAfeeEPOEvent
Stage 2: where
| where EventId == '2412'
Stage 3: extend
| extend IPCustomEntity = DvcIpAddr, HostCustomEntity = DvcHostname
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventId | eq |
| field:"EventId" kind:eq value:"2412" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HostCustomEntity | extend |
IPCustomEntity | extend |
Netskope - Unsanctioned/Risky Cloud App Access (Shadow IT)
#Alerts when users access unsanctioned or risky cloud applications based on Cloud Confidence Level (CCL) and app tags. Detects Shadow IT usage.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Exfiltration |
Rule Body
id: cdc01279-d6ea-41b1-a32d-49d726be95b8
name: Netskope - Unsanctioned/Risky Cloud App Access (Shadow IT)
description: |
Alerts when users access unsanctioned or risky cloud applications based on Cloud Confidence Level (CCL) and app tags. Detects Shadow IT usage.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: NetskopeWebTxConnector
dataTypes:
- NetskopeWebTransactions_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Exfiltration
relevantTechniques:
- T1199
- T1567
query: |
NetskopeWebTransactions_CL
| where TimeGenerated > ago(1h)
| where isnotempty(CsUsername) and isnotempty(XCsApp)
| where XCsAppTags contains 'Unsanctioned'
or XCsAppCcl =~ 'poor'
or XCsAppCcl =~ 'low'
or XCsAppCci < 50
| summarize
EventCount = count(),
TotalBytesMB = round(sum(Bytes) / 1048576.0, 2),
Activities = make_set(XCsAppActivity),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by
CsUsername,
XCsApp,
XCsAppCategory,
XCsAppCcl,
XCsAppCci,
XCsAppTags,
XCCountry,
XCDevice
| extend RiskLevel = case(
XCsAppCci < 30, 'Critical',
XCsAppCci < 50, 'High',
XCsAppCci < 70, 'Medium',
'Low')
| project
TimeGenerated = LastSeen,
User = CsUsername,
RiskyApplication = XCsApp,
AppCategory = XCsAppCategory,
CloudConfidenceLevel = XCsAppCcl,
CloudConfidenceIndex = XCsAppCci,
AppTags = XCsAppTags,
RiskLevel,
Country = XCCountry,
Device = XCDevice,
Activities,
EventCount,
DataTransferMB = TotalBytesMB
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: User
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: RiskyApplication
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
NetskopeWebTransactions_CL
Stage 2: where
| where TimeGenerated > ago(1h)
Stage 3: where
| where isnotempty(CsUsername) and isnotempty(XCsApp)
Stage 4: where
| where XCsAppTags contains 'Unsanctioned'
or XCsAppCcl =~ 'poor'
or XCsAppCcl =~ 'low'
or XCsAppCci < 50
Stage 5: summarize
| summarize
EventCount = count(),
TotalBytesMB = round(sum(Bytes) / 1048576.0, 2),
Activities = make_set(XCsAppActivity),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by
CsUsername,
XCsApp,
XCsAppCategory,
XCsAppCcl,
XCsAppCci,
XCsAppTags,
XCCountry,
XCDevice
Stage 6: extend
| extend RiskLevel = case(
XCsAppCci < 30, 'Critical',
XCsAppCci < 50, 'High',
XCsAppCci < 70, 'Medium',
'Low')
RiskLevel =XCsAppCci < 30'Critical'XCsAppCci < 50'High'XCsAppCci < 70'Medium''Low'Stage 7: project
| project
TimeGenerated = LastSeen,
User = CsUsername,
RiskyApplication = XCsApp,
AppCategory = XCsAppCategory,
CloudConfidenceLevel = XCsAppCcl,
CloudConfidenceIndex = XCsAppCci,
AppTags = XCsAppTags,
RiskLevel,
Country = XCCountry,
Device = XCDevice,
Activities,
EventCount,
DataTransferMB = TotalBytesMB
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CsUsername | is_not_null | field:"CsUsername" kind:is_not_null | |
XCsApp | is_not_null | field:"XCsApp" kind:is_not_null | |
XCsAppCci | lt |
| field:"XCsAppCci" kind:lt value:"50" |
XCsAppCcl | eq |
| field:"XCsAppCcl" kind:eq |
XCsAppTags | contains |
| field:"XCsAppTags" kind:contains value:"Unsanctioned" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Activities | project |
AppCategory | project |
AppTags | project |
CloudConfidenceIndex | project |
CloudConfidenceLevel | project |
Country | project |
DataTransferMB | project |
Device | project |
EventCount | project |
RiskLevel | project |
RiskyApplication | project |
TimeGenerated | project |
User | project |
Pathlock TDnR - SAP Security Audit Log Events
#Detects security-relevant events from the SAP Security Audit Log (SM20), forwarded by Pathlock Threat Detection and Response. The Security Audit Log captures critical security events including failed logons, authorization failures, and restricted transactions - anomalies here are strong indicators of attack activity or insider threats.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment | |
| Discovery |
Rule Body
id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c56
name: Pathlock TDnR - SAP Security Audit Log Events
kind: Scheduled
description: >-
Detects security-relevant events from the SAP Security Audit Log (SM20), forwarded by Pathlock
Threat Detection and Response. The Security Audit Log captures critical security events including
failed logons, authorization failures, and restricted transactions - anomalies here are
strong indicators of attack activity or insider threats.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Pathlock_TDnR
dataTypes:
- Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
- DefenseEvasion
relevantTechniques:
- T1082
- T1562
query: |
Pathlock_TDnR_CL
| where DataSource == "SECURITY_AUDIT_LOG"
| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Bname
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: Hostname
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIp
eventGroupingSettings:
aggregationKind: SingleAlert
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 5h
matchingMethod: AnyAlert
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.0
Stages and Predicates
Stage 1: source
Pathlock_TDnR_CL
Stage 2: where
| where DataSource == "SECURITY_AUDIT_LOG"
Stage 3: project
| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DataSource | eq |
| field:"DataSource" kind:eq value:"SECURITY_AUDIT_LOG" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AffectedUser | project |
Area | project |
Bname | project |
CentralTs | project |
DataSource | project |
DestIp | project |
Eventid | project |
Hostname | project |
Instance | project |
LogLine | project |
MessageV1 | project |
MessageV2 | project |
MessageV3 | project |
MessageV4 | project |
MsgId | project |
MsgNo | project |
MsgType | project |
Report | project |
SrcIp | project |
Subid | project |
Sysid | project |
Tcode | project |
TimeGenerated | project |
Potential NTLM Relay Attack to Domain Controller
#Below query detects NTLM authentication coming from Domain Controller machine accounts. This is not an expected behavior and it's an indication of NTLM relay attack.
If NTLM Relaying is done towards a Linux machine, this query won't detect that. The attacker must have access to a Linux device in that case though.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access | No specific technique |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceLogonEvents action any: Logon activity |
Rule Body
// Author : Cyb3rMonk(https://twitter.com/Cyb3rMonk, https://mergene.medium.com)
//
// Link to original post:
// https://posts.bluraven.io/detecting-petitpotam-and-other-domain-controller-account-takeovers-d3364bd9ee0a
//
// Description : NTLM authentication coming from Domain Controller machine accounts. This is not an expected behavior and it's an indication of NTLM relay attack.
// If NTLM Relaying is done towards a Linux machine, this query won't detect that. The attacker must have access to a Linux device in that case though.
//
// Query parameters:
//
// put FQDNs of the DCs into the list
let DCs = dynamic(["yourdc1.yourdomain.local","yourdc2.yourdomain.local"]);
DeviceLogonEvents
| where Protocol == "NTLM"
| where AccountName endswith "$"
| where DCs has replace_string(AccountName,"$","")
Stages and Predicates
Parameters
let DCs = dynamic(["yourdc1.yourdomain.local","yourdc2.yourdomain.local"]);
Stage 1: source
DeviceLogonEvents
Stage 2: where
| where Protocol == "NTLM"
Stage 3: where
| where AccountName endswith "$"
Stage 4: where
| where DCs has replace_string(AccountName,"$","")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AccountName | ends_with |
| field:"user" kind:ends_with value:"$" |
Protocol | eq |
| field:"Protocol" kind:eq value:"NTLM" |
Restore Point Marked as Clean
#Detects when a restore point is marked as clean.
Rule Body
id: c4c6cb50-9fc3-4ca9-a2df-22d527240309
name: Restore Point Marked as Clean
description: Detects when a restore point is marked as clean.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 42230\n| project\n Date\
\ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n DataSource = original_host,\n\
\ EventId = instanceId,\n UserName = user,\n MessageDetails = Description,\n\
\ Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
Date: Date
VbrHostName: DataSource
EventId: EventId
MessageDetails: MessageDetails
Severity: Severity
Stages and Predicates
Stage 1: source
Veeam_GetSecurityEvents
Stage 2: where
| where instanceId == 42230
Stage 3: project
| project
Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
DataSource = original_host,
EventId = instanceId,
UserName = user,
MessageDetails = Description,
Severity = SeverityDescription
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"42230" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
EventId | project |
MessageDetails | project |
Severity | project |
UserName | project |
Several deny actions registered
#'Identifies attack pattern when attacker tries to move, or scan, from resource to resource on the network and creates an incident when a source has more than 1 registered deny action in Azure Firewall.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery | |
| Lateral Movement | |
| Command & Control |
Rule Body
id: f8dad4e9-3f19-4d70-ab7f-8f19ccd43a3e
name: Several deny actions registered
description: |
'Identifies attack pattern when attacker tries to move, or scan, from resource to resource on the network and creates an incident when a source has more than 1 registered deny action in Azure Firewall.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AzureFirewall
dataTypes:
- AzureDiagnostics
- AZFWApplicationRule
- AZFWNetworkRule
- AZFWFlowTrace
- AZFWIdpsSignature
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
- LateralMovement
- CommandAndControl
relevantTechniques:
- T1046
- T1071
- T1210
query: |
let threshold = 2;
union isfuzzy=true(
AZFWApplicationRule
| where Action == "Deny"
| summarize StartTime = min(TimeGenerated), count() by SourceIp, Fqdn, Action, Protocol
| where count_ >= threshold),
(AZFWNetworkRule
| where Action == "Deny"
| extend Fqdn = DestinationIp
| summarize StartTime = min(TimeGenerated), count_ = dcount(DestinationPort), Protocols = make_set(Protocol) by SourceIp, Fqdn, Action
| where count_ >= threshold
| extend Protocol = strcat_array(Protocols, "/")),
(AZFWFlowTrace
| where Action == "Deny"
| extend Fqdn = DestinationIp
| summarize StartTime = min(TimeGenerated), count() by SourceIp, Fqdn, Action, Protocol
| where count_ >= threshold),
(AZFWIdpsSignature
| where Action == "Deny"
| extend Fqdn = DestinationIp
| summarize StartTime = min(TimeGenerated), count() by SourceIp, Fqdn, Action, Protocol
| where count_ >= threshold),
(AzureDiagnostics
| where OperationName in ("AzureFirewallApplicationRuleLog","AzureFirewallNetworkRuleLog")
| extend msg_s_replaced0 = replace(@"\s\s",@" ",msg_s)
| extend msg_s_replaced1 = replace(@"\.\s",@" ",msg_s_replaced0)
| extend msg_a = split(msg_s_replaced1," ")
| extend srcAddr_a = split(msg_a[3],":") , destAddr_a = split(msg_a[5],":")
| extend Protocol = tostring(msg_a[0]), SourceIp = tostring(srcAddr_a[0]), srcPort = tostring(srcAddr_a[1]), DestinationIp = tostring(destAddr_a[0]), destPort = tostring(destAddr_a[1]), Action = tostring(msg_a[7])
| where Action == "Deny"
| extend Fqdn = iff(DestinationIp matches regex "\\d+\\.\\d+\\.\\d+\\.\\d+","",DestinationIp)
| summarize StartTime = min(TimeGenerated), count() by SourceIp, Fqdn, Action, Protocol
| where count_ >= threshold)
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIp
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Fqdn
customDetails:
NetworkProtocol: Protocol
alertDetailsOverride:
alertDisplayNameFormat: 'Multiple Deny Actions Detected from {{SourceIp}} to {{Fqdn}}'
alertDescriptionFormat: 'Source IP {{SourceIp}} has triggered multiple deny actions to destination {{Fqdn}} using protocol {{Protocol}}, which may indicate malicious activity such as lateral movement or reconnaissance.'
version: 1.1.3
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 2;
Union isfuzzy=true (5 Sources)
Each leg below queries one source; the rule matches if any leg does. Sources: AZFWApplicationRule, AZFWNetworkRule, AZFWFlowTrace, AZFWIdpsSignature, AzureDiagnostics
Leg 1: AZFWApplicationRule
AZFWApplicationRule
| where Action == "Deny"
| summarize StartTime = min(TimeGenerated), count() by SourceIp, Fqdn, Action, Protocol
| where count_ >= threshold
Leg 2: AZFWNetworkRule
AZFWNetworkRule
| where Action == "Deny"
| extend Fqdn = DestinationIp
| summarize StartTime = min(TimeGenerated), count_ = dcount(DestinationPort), Protocols = make_set(Protocol) by SourceIp, Fqdn, Action
| where count_ >= threshold
| extend Protocol = strcat_array(Protocols, "/")
Leg 3: AZFWFlowTrace
AZFWFlowTrace
| where Action == "Deny"
| extend Fqdn = DestinationIp
| summarize StartTime = min(TimeGenerated), count() by SourceIp, Fqdn, Action, Protocol
| where count_ >= threshold
Leg 4: AZFWIdpsSignature
AZFWIdpsSignature
| where Action == "Deny"
| extend Fqdn = DestinationIp
| summarize StartTime = min(TimeGenerated), count() by SourceIp, Fqdn, Action, Protocol
| where count_ >= threshold
Leg 5: AzureDiagnostics
AzureDiagnostics
| where OperationName in ("AzureFirewallApplicationRuleLog","AzureFirewallNetworkRuleLog")
| extend msg_s_replaced0 = replace(@"\s\s",@" ",msg_s)
| extend msg_s_replaced1 = replace(@"\.\s",@" ",msg_s_replaced0)
| extend msg_a = split(msg_s_replaced1," ")
| extend srcAddr_a = split(msg_a[3],":") , destAddr_a = split(msg_a[5],":")
| extend Protocol = tostring(msg_a[0]), SourceIp = tostring(srcAddr_a[0]), srcPort = tostring(srcAddr_a[1]), DestinationIp = tostring(destAddr_a[0]), destPort = tostring(destAddr_a[1]), Action = tostring(msg_a[7])
| where Action == "Deny"
| extend Fqdn = iff(DestinationIp matches regex "\\d+\\.\\d+\\.\\d+\\.\\d+","",DestinationIp)
| summarize StartTime = min(TimeGenerated), count() by SourceIp, Fqdn, Action, Protocol
| where count_ >= threshold
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | eq |
| field:"Action" kind:eq value:"Deny" |
OperationName | in |
| field:"OperationName" kind:in |
count_ | ge |
| field:"count_" kind:ge value:"2" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Action | summarize |
Fqdn | summarize |
Protocol | summarize |
SourceIp | summarize |
StartTime | summarize |
Tenable.ad Indicators of Exposures
#'Searches for triggered Indicators of Exposures.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Rule Body
id: 55de1072-e93f-40f9-a14d-f7356d217cf6
name: Tenable.ad Indicators of Exposures
description: |
'Searches for triggered Indicators of Exposures.'
severity: Low
requiredDataConnectors:
- connectorId: Tenable.ad
dataTypes:
- Tenable_ad_CL
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1110
query: |
// For the query to work properly, make sure you have imported the afad_parser.kql parser into the workspace
// Retrieve the parser here: https://raw.githubusercontent.com/tenable/Azure-Sentinel/Tenable.ad-connector/Solutions/TenableAD/Parsers/afad_parser.kql
// Then, create the Kusto Function with alias afad_parser
let SeverityTable=datatable(Severity:string,Level:int) [
"low", 1,
"medium", 2,
"high", 3,
"critical", 4
];
afad_parser
| where MessageType == 0
| lookup kind=leftouter SeverityTable on Severity
| order by Level
| extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
version: 1.0.1
kind: Scheduled
Stages and Predicates
Let Binding: SeverityTable
let SeverityTable = datatable(Severity:string,Level:int) [
"low", 1,
"medium", 2,
"high", 3,
"critical", 4
];
Stage 1: source
afad_parser
Stage 2: where
| where MessageType == 0
Stage 3: kusto:lookup
lookup kind=leftouter (SeverityTable) on Severity
Stage 4: sort
sort by Level
Stage 5: extend
extend DnsDomain, HostName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
MessageType | eq |
| field:"MessageType" kind:eq value:"0" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DnsDomain | extend |
HostName | extend |
ThreatConnect TI map IP entity to Network Session Events (ASIM Network Session schema)
#ThreatConnect Specific: 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: ee1fd303-2081-47b7-8f02-e38bfd0868e6
name: ThreatConnect TI map IP entity to Network Session Events (ASIM Network Session schema)
version: 1.0.2
kind: Scheduled
description: |-
ThreatConnect Specific:
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
requiredDataConnectors:
- connectorId: ThreatIntelligence
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)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where ExpirationDateTime > now() and Active == true
| where SourceSystem startswith "ThreatConnect-"
| extend TI_ipEntity = coalesce(NetworkIP, NetworkDestinationIP, NetworkSourceIP,EmailSourceIpAddress,"NO_IP")
| where TI_ipEntity != "NO_IP"
);
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
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 4h
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: SingleAlert
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.
customDetails:
IndicatorId: IndicatorId
IoCConfidenceScore: ConfidenceScore
IoCDescription: Description
IoCExpirationTime: ExpirationDateTime
EventEndTime: imNWS_maxtime
ActivityGroupNames: ActivityGroupNames
ThreatType: ThreatType
IoCIPDirection: IoCDirection
EventStartTime: imNWS_mintime
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IoCIP
suppressionDuration: 1h
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 10 use it.
Stage 1: source
ThreatIntelligenceIndicator
Stage 2: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 3: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
Stage 4: where
| where ExpirationDateTime > now() and Active == true
Stage 5: where
| where SourceSystem startswith "ThreatConnect-"
Stage 6: extend
| extend TI_ipEntity = coalesce(NetworkIP, NetworkDestinationIP, NetworkSourceIP,EmailSourceIpAddress,"NO_IP")
Stage 7: where
| where TI_ipEntity != "NO_IP"
Stage 8: join
join kind=innerunique (_Im_NetworkSession) on $left.TI_ipEntity, $right.IoCIP
Stage 9: where
where imNWS_mintime < ExpirationDateTime
Stage 10: 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" |
SourceSystem | starts_with |
| field:"SourceSystem" kind:starts_with value:"ThreatConnect-" |
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 |
TI Map File Hash to CommonSecurityLog Event
#'Identifies a match in CommonSecurityLog Event data from any FileHash IOC from TI'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 432996e9-8a93-4407-985f-13707b318a0b
name: TI Map File Hash to CommonSecurityLog Event
description: |
'Identifies a match in CommonSecurityLog Event data from any FileHash IOC from TI'
severity: Medium
requiredDataConnectors:
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelIndicators
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let fileHashIndicators = ThreatIntelIndicators
//extract key part of kv pair
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType == "file"
| extend FileHashType = replace("'", "", substring(ObservableKey, indexof(ObservableKey, "hashes.") + 7, strlen(ObservableKey) - indexof(ObservableKey, "hashes.") - 7))
| extend FileHashValue = toupper(ObservableValue)
| extend IndicatorId = tostring(split(Id, "--")[2])
| extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil));
// Handle matches against both lower case and uppercase versions of the hash:
(fileHashIndicators | extend FileHashValue = tolower(FileHashValue)
| union (fileHashIndicators | extend FileHashValue = toupper(FileHashValue)))
| project-reorder *, FileHashType, FileHashValue, Type
// 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 (
CommonSecurityLog | where TimeGenerated >= ago(dt_lookBack)
| extend CommonSecurityLog_TimeGenerated = TimeGenerated
)
on $left.FileHashValue == $right.FileHash
| where CommonSecurityLog_TimeGenerated < ValidUntil
| summarize CommonSecurityLog_TimeGenerated = arg_max(CommonSecurityLog_TimeGenerated, *) by IndicatorId, FileHashValue
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| project CommonSecurityLog_TimeGenerated, Description, ActivityGroupNames, Id, ValidUntil, Confidence,
SourceIP, SourcePort, DestinationIP, DestinationPort, SourceUserID, SourceUserName, DeviceName, DeviceAction,
RequestURL, DestinationUserName, DestinationUserID, ApplicationProtocol, Activity, FileHashValue, FileHashType, Url
| extend HostName = tostring(split(DeviceName, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(DeviceName, '.'), 1, -1), '.'))
| extend Name = tostring(split(SourceUserName, '@', 0)[0]), UPNSuffix = tostring(split(SourceUserName, '@', 1)[0])
| extend timestamp = CommonSecurityLog_TimeGenerated
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: SourceUserName
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
- entityType: FileHash
fieldMappings:
- identifier: Value
columnName: FileHashValue
- identifier: Algorithm
columnName: FileHashType
version: 1.3.10
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let fileHashIndicators is inlined into the numbered stages below.
Stages 1 to 7 define let fileHashIndicators as the rule's main pipeline source. Stages 8 to 24 combine it with further branches.
Stage 1: source
ThreatIntelIndicators
Stage 2: extend
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
Stage 3: where
| where IndicatorType == "file"
Stage 4: extend (4 consecutive steps)
| extend FileHashType = replace("'", "", substring(ObservableKey, indexof(ObservableKey, "hashes.") + 7, strlen(ObservableKey) - indexof(ObservableKey, "hashes.") - 7))
| extend FileHashValue = toupper(ObservableValue)
| extend IndicatorId = tostring(split(Id, "--")[2])
| extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
Stage 5: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 6: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
Stage 7: where
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
Stage 8: extend
fileHashIndicators
| extend FileHashValue = tolower(FileHashValue)
Stage 9: union
union of 1 branches
Stage 10: source
ThreatIntelIndicators
Stage 11: extend
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
Stage 12: where
| where IndicatorType == "file"
Stage 13: extend (4 consecutive steps)
| extend FileHashType = replace("'", "", substring(ObservableKey, indexof(ObservableKey, "hashes.") + 7, strlen(ObservableKey) - indexof(ObservableKey, "hashes.") - 7))
| extend FileHashValue = toupper(ObservableValue)
| extend IndicatorId = tostring(split(Id, "--")[2])
| extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
Stage 14: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 15: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
Stage 16: where
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
Stage 17: extend
| extend FileHashValue = toupper(FileHashValue)
Stage 18: project-reorder
project-reorder
Stage 19: join
| join kind=innerunique (
CommonSecurityLog | where TimeGenerated >= ago(dt_lookBack)
| extend CommonSecurityLog_TimeGenerated = TimeGenerated
)
on $left.FileHashValue == $right.FileHash
Stage 20: where
where CommonSecurityLog_TimeGenerated < ValidUntil
Stage 21: summarize
summarize CommonSecurityLog_TimeGenerated by IndicatorId, FileHashValue
Stage 22: extend (4 consecutive steps)
extend ActivityGroupNames, Description
Stage 23: project
project Activity, ActivityGroupNames, ApplicationProtocol, CommonSecurityLog_TimeGenerated, Confidence, Description, DestinationIP, DestinationPort, DestinationUserID, DestinationUserName, DeviceAction, DeviceName, FileHashType, FileHashValue, Id, RequestURL, SourceIP, SourcePort, SourceUserID, SourceUserName, Url, ValidUntil
Stage 24: extend (3 consecutive steps)
extend DnsDomain, HostName, Name, UPNSuffix, timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
IndicatorType | eq |
| field:"IndicatorType" kind:eq value:"file" |
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 |
|---|---|
Activity | project |
ActivityGroupNames | project |
ApplicationProtocol | project |
CommonSecurityLog_TimeGenerated | project |
Confidence | project |
Description | project |
DestinationIP | project |
DestinationPort | project |
DestinationUserID | project |
DestinationUserName | project |
DeviceAction | project |
DeviceName | project |
FileHashType | project |
FileHashValue | project |
Id | project |
RequestURL | project |
SourceIP | project |
SourcePort | project |
SourceUserID | project |
SourceUserName | project |
Url | project |
ValidUntil | project |
DnsDomain | extend |
HostName | extend |
Name | extend |
UPNSuffix | extend |
timestamp | extend |
TI Map URL Entity to PaloAlto Data
#'This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in PaloAlto Data.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 32b437c4-dddb-45b3-9aae-5188e80624b0
name: TI Map URL Entity to PaloAlto Data
description: |
'This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in PaloAlto Data.'
severity: Medium
requiredDataConnectors:
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelIndicators
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
ThreatIntelIndicators
//extract key part of kv pair
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType == "url"
| extend Url = ObservableValue
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
// Picking up only IOC's that contain the entities we want
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| project-reorder *, Tags, TrafficLightProtocolLevel, Url, Type
// 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 (
CommonSecurityLog
| extend IngestionTime = ingestion_time()
| where IngestionTime > ago(dt_lookBack)
// Select on Palo Alto logs
| where DeviceVendor =~ "Palo Alto Networks"
| where DeviceEventClassID =~ 'url'
//Uncomment the line below to only alert on allowed connections
//| where DeviceAction !~ "block-url"
//Select logs where URL data is populated
| extend PA_Url = column_ifexists("RequestURL", "None")
| extend PA_Url = iif(isempty(PA_Url), extract("([^\"]+)", 1, tolower(AdditionalExtensions)), trim('"', PA_Url))
| extend PA_Url = iif(PA_Url !startswith "http://" and ApplicationProtocol !~ "ssl", strcat('http://', PA_Url), iif(PA_Url !startswith "https://" and ApplicationProtocol =~ "ssl", strcat('https://', PA_Url), PA_Url))
| where isnotempty(PA_Url)
| extend CommonSecurityLog_TimeGenerated = TimeGenerated
) on $left.Url == $right.PA_Url
| where CommonSecurityLog_TimeGenerated < ValidUntil
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| summarize CommonSecurityLog_TimeGenerated = arg_max(CommonSecurityLog_TimeGenerated, *) by Id, PA_Url
| project timestamp = CommonSecurityLog_TimeGenerated, Description, ActivityGroupNames, Id, Type, ValidUntil, Confidence, DeviceAction, SourceIP, PA_Url, DeviceName
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DeviceName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
- entityType: URL
fieldMappings:
- identifier: Url
columnName: PA_Url
version: 1.2.7
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
Stage 1: source
ThreatIntelIndicators
Stage 2: extend
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
Stage 3: where
| where IndicatorType == "url"
Stage 4: extend
| extend Url = ObservableValue
Stage 5: extend
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
Stage 6: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 7: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
Stage 8: where
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
Stage 9: project-reorder
| project-reorder *, Tags, TrafficLightProtocolLevel, Url, Type
Stage 10: join
| join kind=innerunique (
CommonSecurityLog
| extend IngestionTime = ingestion_time()
| where IngestionTime > ago(dt_lookBack)
| where DeviceVendor =~ "Palo Alto Networks"
| where DeviceEventClassID =~ 'url'
| extend PA_Url = column_ifexists("RequestURL", "None")
| extend PA_Url = iif(isempty(PA_Url), extract("([^\"]+)", 1, tolower(AdditionalExtensions)), trim('"', PA_Url))
| extend PA_Url = iif(PA_Url !startswith "http://" and ApplicationProtocol !~ "ssl", strcat('http://', PA_Url), iif(PA_Url !startswith "https://" and ApplicationProtocol =~ "ssl", strcat('https://', PA_Url), PA_Url))
| where isnotempty(PA_Url)
| extend CommonSecurityLog_TimeGenerated = TimeGenerated
) on $left.Url == $right.PA_Url
Stage 11: where
where CommonSecurityLog_TimeGenerated < ValidUntil
Stage 12: extend
extend Description
Stage 13: extend
extend ActivityGroupNames
Stage 14: summarize
summarize CommonSecurityLog_TimeGenerated by Id, PA_Url
Stage 15: project
project ActivityGroupNames, Confidence, Description, DeviceAction, DeviceName, Id, PA_Url, SourceIP, Type, ValidUntil, timestamp
Indicators
These rows show field, operator, and value matches.
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityGroupNames | project |
Confidence | project |
Description | project |
DeviceAction | project |
DeviceName | project |
Id | project |
PA_Url | project |
SourceIP | project |
Type | project |
ValidUntil | project |
timestamp | project |
VMware SD-WAN Edge - Device Congestion Alert - Packet Drops
#The VMware Edge Cloud Orchestrator reported an edge congestion event, where the Edge is dropping a large number of packets on one of its interfaces. This could indicate an ongoing Denial of Service attack against an appliance. Please make sure that Network Flood Protection is turned on.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: a88ead0a-f022-48d6-8f53-e5a164c4c72e
name: VMware SD-WAN Edge - Device Congestion Alert - Packet Drops
version: 1.0.0
kind: Scheduled
description: The VMware Edge Cloud Orchestrator reported an edge congestion event, where the Edge is dropping a large number of packets on one of its interfaces. This could indicate an ongoing Denial of Service attack against an appliance. Please make sure that Network Flood Protection is turned on.
severity: Medium
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
requiredDataConnectors:
- connectorId: VMwareSDWAN
dataTypes:
- SDWAN
tactics:
- Impact
relevantTechniques:
- T1498
query: |-
VMware_VECO_EventLogs_CL
| where event == "EDGE_CONGESTED"
| where message contains "high number of packet drops"
| extend edgeSerialNumber = extract("edgeSerialNumber: (.+)$", 1, detail)
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 5h
matchingMethod: AllEntities
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
eventGroupingSettings:
aggregationKind: SingleAlert
customDetails:
edgeSerialNumber: edgeSerialNumber
suppressionDuration: 5h
Stages and Predicates
Stage 1: source
VMware_VECO_EventLogs_CL
Stage 2: where
| where event == "EDGE_CONGESTED"
Stage 3: where
| where message contains "high number of packet drops"
Stage 4: extend
| extend edgeSerialNumber = extract("edgeSerialNumber: (.+)$", 1, detail)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event | eq |
| field:"event" kind:eq value:"EDGE_CONGESTED" |
message | contains |
| field:"message" kind:contains value:"high number of packet drops" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
edgeSerialNumber | extend |