Analytic rule catalog
Synqly Alert Event
Back
| Id | 3192085a-e97e-440f-acb7-9227622949a4 |
| Rulename | Synqly Alert Event |
| Description | Creates one Microsoft Sentinel alert for each qualifying Synqly-attributed ASIM Alert Event row. Native ASIM values take precedence, with retained OCSF data used to recover investigation context when normalized fields are empty. Replayed source rows can create additional alerts, and Microsoft Sentinel or Defender XDR may correlate related alerts into a shared incident. |
| Severity | Medium |
| Required data connectors | SynqlyIntegrationConnector |
| Kind | Scheduled |
| Query frequency | 5m |
| Query period | 5m |
| Trigger threshold | 0 |
| Trigger operator | gt |
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/SynqlyIntegrationConnector/Analytic%20Rules/SynqlyAlertEventToSentinelAlert.yaml |
| Version | 1.0.0 |
| Arm template | 3192085a-e97e-440f-acb7-9227622949a4.json |
ASimAlertEventLogs
| where tostring(AdditionalFields._ConnectorId) == "SynqlySentinelASIMConnector"
| extend OCSF = coalesce(todynamic(AdditionalFields.OCSF), dynamic({}))
| mv-apply Evidence = array_concat(coalesce(todynamic(OCSF.evidences), dynamic([])), dynamic([{}])) on (
mv-apply EvidenceHash = array_concat(coalesce(todynamic(Evidence.data.file.hashes), dynamic([])), coalesce(todynamic(Evidence.data.process.hashes), dynamic([])), dynamic([{}])) on (
summarize
OcsfSHA256 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == "SHA256"),
OcsfSHA1 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == "SHA1"),
OcsfMD5 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == "MD5")
)
| summarize
OcsfUserName = take_anyif(tostring(Evidence.data.process.user.name), isnotempty(tostring(Evidence.data.process.user.name))),
OcsfUserSid = take_anyif(tostring(Evidence.data.process.user.uid), isnotempty(tostring(Evidence.data.process.user.uid))),
OcsfUserDomain = take_anyif(tostring(Evidence.data.process.user.domain), isnotempty(tostring(Evidence.data.process.user.domain))),
OcsfProcessId = take_anyif(tostring(Evidence.data.process.pid), isnotempty(tostring(Evidence.data.process.pid))),
OcsfProcessCommandLine = take_anyif(tostring(Evidence.data.process.cmd_line), isnotempty(tostring(Evidence.data.process.cmd_line))),
OcsfFileName = take_anyif(tostring(Evidence.data.file.name), isnotempty(tostring(Evidence.data.file.name))),
OcsfFilePath = take_anyif(tostring(Evidence.data.file.path), isnotempty(tostring(Evidence.data.file.path))),
OcsfSourceHost = take_anyif(tostring(Evidence.data.network.src_endpoint.host.hostname), isnotempty(tostring(Evidence.data.network.src_endpoint.host.hostname))),
OcsfSourceIp = take_anyif(tostring(Evidence.data.network.src_endpoint.ip), isnotempty(tostring(Evidence.data.network.src_endpoint.ip))),
OcsfDestinationIp = take_anyif(tostring(Evidence.data.network.dst_endpoint.ip), isnotempty(tostring(Evidence.data.network.dst_endpoint.ip))),
OcsfDestinationDomain = take_anyif(tostring(Evidence.data.network.dst_endpoint.domain), isnotempty(tostring(Evidence.data.network.dst_endpoint.domain))),
OcsfSHA256 = take_anyif(OcsfSHA256, isnotempty(OcsfSHA256)),
OcsfSHA1 = take_anyif(OcsfSHA1, isnotempty(OcsfSHA1)),
OcsfMD5 = take_anyif(OcsfMD5, isnotempty(OcsfMD5))
)
| mv-apply Attack = array_concat(coalesce(todynamic(OCSF.attacks), dynamic([])), dynamic([{}])) on (
extend TacticId = tostring(Attack.tactic.uid), TechniqueId = tostring(Attack.technique.uid), SubTechniqueId = tostring(Attack.sub_technique.uid)
| extend TacticName = case(
TacticId == "TA0043", "Reconnaissance", TacticId == "TA0042", "ResourceDevelopment",
TacticId == "TA0001", "InitialAccess", TacticId == "TA0002", "Execution",
TacticId == "TA0003", "Persistence", TacticId == "TA0004", "PrivilegeEscalation",
TacticId == "TA0005", "DefenseEvasion", TacticId == "TA0006", "CredentialAccess",
TacticId == "TA0007", "Discovery", TacticId == "TA0008", "LateralMovement",
TacticId == "TA0009", "Collection", TacticId == "TA0010", "Exfiltration",
TacticId == "TA0011", "CommandAndControl", TacticId == "TA0040", "Impact", "")
| summarize
OcsfTactics = make_set_if(TacticName, isnotempty(TacticName), 14),
OcsfTechniques = make_set_if(TechniqueId, isnotempty(TechniqueId), 50),
OcsfSubTechniques = make_set_if(SubTechniqueId, isnotempty(SubTechniqueId), 50)
)
| extend
ActorUser = todynamic(OCSF.actor.process.user),
Malware = todynamic(OCSF.malware[0]),
NativeConfidenceValue = toreal(ThreatConfidence),
OcsfConfidenceValue = toreal(OCSF.confidence_score),
NativeConfidenceLabel = tolower(trim(" ", tostring(ThreatOriginalConfidence))),
OcsfConfidenceLabel = tolower(trim(" ", tostring(OCSF.confidence))),
NativeSourceId = coalesce(tostring(EventUid), tostring(AlertId), tostring(EventOriginalUid)),
OcsfSourceId = coalesce(tostring(OCSF.finding_info.uid), tostring(OCSF.uid)),
SourceIdMaterial = tostring(pack_array(tostring(OCSF.class_uid), tostring(OCSF.type_uid), tostring(OCSF.activity_id), tostring(EventStartTime), tostring(EventEndTime), tostring(EventVendor), tostring(EventProduct), tostring(EventMessage), tostring(OCSF["time"])))
| extend
SynqlyAlertId = coalesce(NativeSourceId, OcsfSourceId, strcat("generated-", hash_sha256(SourceIdMaterial))),
SynqlyAlertName = coalesce(tostring(AlertName), tostring(OCSF.finding_info["title"]), tostring(OCSF.class_name), "Synqly normalized alert event"),
SynqlyAlertDescription = coalesce(tostring(EventMessage), tostring(AlertDescription), tostring(OCSF.message), "Synqly normalized alert event"),
SeverityValue = tolower(coalesce(tostring(EventSeverity), tostring(EventOriginalSeverity), tostring(OCSF.severity))),
SynqlyTactics = coalesce(tostring(AttackTactics), strcat_array(OcsfTactics, ",")),
AllAttackTechniques = coalesce(tostring(AttackTechniques), strcat_array(array_concat(OcsfTechniques, OcsfSubTechniques), ",")),
SynqlyConfidenceScore = coalesce(
iff(NativeConfidenceValue between (0.0 .. 100.0), NativeConfidenceValue / 100.0, real(null)),
iff(OcsfConfidenceValue between (0.0 .. 100.0), OcsfConfidenceValue / 100.0, real(null))),
SynqlyConfidenceLevel = case(
NativeConfidenceLabel == "high", "High",
NativeConfidenceLabel == "low", "Low",
NativeConfidenceLabel == "unknown", "Unknown",
isnotempty(NativeConfidenceLabel), "",
OcsfConfidenceLabel == "high", "High",
OcsfConfidenceLabel == "low", "Low",
OcsfConfidenceLabel == "unknown", "Unknown",
""),
SynqlyRemediation = coalesce(tostring(AttackRemediationSteps), strcat_array(todynamic(OCSF.remediation.kb_articles), "; ")),
SynqlyAlertLink = coalesce(tostring(EventReportUrl), tostring(OCSF.finding_info.src_url)),
SourceVendor = coalesce(tostring(EventVendor), tostring(OCSF.metadata.product.vendor_name)),
SourceProduct = coalesce(tostring(EventProduct), tostring(OCSF.metadata.product.name)),
ProductVersion = coalesce(tostring(EventProductVersion), tostring(OCSF.metadata.product.version)),
OriginalSeverity = coalesce(tostring(EventOriginalSeverity), tostring(OCSF.severity)),
SourceRuleId = coalesce(
tostring(Rule),
tostring(OCSF.finding_info.analytic.uid),
iff(isnotempty(tostring(RuleName)) and not(tostring(RuleName) matches regex @"^\d+$"), tostring(RuleName), "")),
AccountName = coalesce(tostring(Username), tostring(User), tostring(ActorUser.name), OcsfUserName),
AccountSid = coalesce(iff(tolower(tostring(UserIdType)) contains "sid", tostring(UserId), ""), tostring(ActorUser.uid), OcsfUserSid),
AccountDomain = coalesce(tostring(UserScope), tostring(ActorUser.domain), OcsfUserDomain),
NativeDeviceHost = coalesce(tostring(DvcHostname), tostring(DvcFQDN)),
NativeDeviceHostDomain = coalesce(tostring(DvcDomain), extract(@"^[^.]+\.(.+)$", 1, tostring(DvcFQDN)), extract(@"^[^.]+\.(.+)$", 1, tostring(DvcHostname))),
DeviceIp = tostring(DvcIpAddr),
DestinationDomain = OcsfDestinationDomain,
EntityUrl = coalesce(tostring(Url), tostring(OCSF.finding_info.src_url)),
FileValue = coalesce(tostring(FilePath), OcsfFilePath),
ProcessIdValue = coalesce(tostring(ProcessId), OcsfProcessId),
ProcessCommandLineValue = coalesce(tostring(ProcessCommandLine), OcsfProcessCommandLine),
MalwareName = coalesce(tostring(ThreatName), tostring(Malware.name)),
FileHashValue = coalesce(tostring(FileSHA256), OcsfSHA256, tostring(FileSHA1), OcsfSHA1, tostring(FileMD5), OcsfMD5)
| extend
SourceIp = OcsfSourceIp,
DestinationIp = OcsfDestinationIp,
HostValue = coalesce(NativeDeviceHost, OcsfSourceHost),
HostDomainValue = iff(isnotempty(NativeDeviceHost), NativeDeviceHostDomain, extract(@"^[^.]+\.(.+)$", 1, OcsfSourceHost))
| extend
PrimaryIp = coalesce(SourceIp, DestinationIp, tostring(DvcIpAddr)),
PrimaryIpRole = case(isnotempty(SourceIp), "Source", isnotempty(DestinationIp), "Destination", isnotempty(tostring(DvcIpAddr)), "Device", "")
| extend
SynqlyAlertSeverity = case(
SeverityValue == "informational", "Informational",
SeverityValue == "low", "Low",
SeverityValue == "medium", "Medium",
SeverityValue in ("high", "critical", "fatal"), "High",
"Medium"),
HostNameValue = coalesce(extract(@"^([^.]+)", 1, HostValue), HostValue),
FileHashAlgorithm = case(
isnotempty(FileSHA256) or isnotempty(OcsfSHA256), "SHA256",
isnotempty(FileSHA1) or isnotempty(OcsfSHA1), "SHA1",
isnotempty(FileMD5) or isnotempty(OcsfMD5), "MD5",
"")
| project
TimeGenerated,
SynqlyAlertId,
SynqlyAlertName,
SynqlyAlertDescription,
SynqlyAlertSeverity,
SynqlyTactics,
AllAttackTechniques,
SynqlyConfidenceLevel,
SynqlyConfidenceScore,
SynqlyRemediation,
SynqlyAlertLink,
SourceVendor,
SourceProduct,
ProductVersion,
OriginalSeverity,
SourceRuleId,
AccountName,
AccountSid,
AccountDomain,
HostNameValue,
HostDomainValue,
SourceIp,
DestinationIp,
DeviceIp,
PrimaryIp,
PrimaryIpRole,
DestinationDomain,
EntityUrl,
FileValue,
FileHashAlgorithm,
FileHashValue,
ProcessIdValue,
ProcessCommandLineValue,
MalwareName
suppressionDuration: 1h
name: Synqly Alert Event
suppressionEnabled: false
triggerOperator: gt
query: |
ASimAlertEventLogs
| where tostring(AdditionalFields._ConnectorId) == "SynqlySentinelASIMConnector"
| extend OCSF = coalesce(todynamic(AdditionalFields.OCSF), dynamic({}))
| mv-apply Evidence = array_concat(coalesce(todynamic(OCSF.evidences), dynamic([])), dynamic([{}])) on (
mv-apply EvidenceHash = array_concat(coalesce(todynamic(Evidence.data.file.hashes), dynamic([])), coalesce(todynamic(Evidence.data.process.hashes), dynamic([])), dynamic([{}])) on (
summarize
OcsfSHA256 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == "SHA256"),
OcsfSHA1 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == "SHA1"),
OcsfMD5 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == "MD5")
)
| summarize
OcsfUserName = take_anyif(tostring(Evidence.data.process.user.name), isnotempty(tostring(Evidence.data.process.user.name))),
OcsfUserSid = take_anyif(tostring(Evidence.data.process.user.uid), isnotempty(tostring(Evidence.data.process.user.uid))),
OcsfUserDomain = take_anyif(tostring(Evidence.data.process.user.domain), isnotempty(tostring(Evidence.data.process.user.domain))),
OcsfProcessId = take_anyif(tostring(Evidence.data.process.pid), isnotempty(tostring(Evidence.data.process.pid))),
OcsfProcessCommandLine = take_anyif(tostring(Evidence.data.process.cmd_line), isnotempty(tostring(Evidence.data.process.cmd_line))),
OcsfFileName = take_anyif(tostring(Evidence.data.file.name), isnotempty(tostring(Evidence.data.file.name))),
OcsfFilePath = take_anyif(tostring(Evidence.data.file.path), isnotempty(tostring(Evidence.data.file.path))),
OcsfSourceHost = take_anyif(tostring(Evidence.data.network.src_endpoint.host.hostname), isnotempty(tostring(Evidence.data.network.src_endpoint.host.hostname))),
OcsfSourceIp = take_anyif(tostring(Evidence.data.network.src_endpoint.ip), isnotempty(tostring(Evidence.data.network.src_endpoint.ip))),
OcsfDestinationIp = take_anyif(tostring(Evidence.data.network.dst_endpoint.ip), isnotempty(tostring(Evidence.data.network.dst_endpoint.ip))),
OcsfDestinationDomain = take_anyif(tostring(Evidence.data.network.dst_endpoint.domain), isnotempty(tostring(Evidence.data.network.dst_endpoint.domain))),
OcsfSHA256 = take_anyif(OcsfSHA256, isnotempty(OcsfSHA256)),
OcsfSHA1 = take_anyif(OcsfSHA1, isnotempty(OcsfSHA1)),
OcsfMD5 = take_anyif(OcsfMD5, isnotempty(OcsfMD5))
)
| mv-apply Attack = array_concat(coalesce(todynamic(OCSF.attacks), dynamic([])), dynamic([{}])) on (
extend TacticId = tostring(Attack.tactic.uid), TechniqueId = tostring(Attack.technique.uid), SubTechniqueId = tostring(Attack.sub_technique.uid)
| extend TacticName = case(
TacticId == "TA0043", "Reconnaissance", TacticId == "TA0042", "ResourceDevelopment",
TacticId == "TA0001", "InitialAccess", TacticId == "TA0002", "Execution",
TacticId == "TA0003", "Persistence", TacticId == "TA0004", "PrivilegeEscalation",
TacticId == "TA0005", "DefenseEvasion", TacticId == "TA0006", "CredentialAccess",
TacticId == "TA0007", "Discovery", TacticId == "TA0008", "LateralMovement",
TacticId == "TA0009", "Collection", TacticId == "TA0010", "Exfiltration",
TacticId == "TA0011", "CommandAndControl", TacticId == "TA0040", "Impact", "")
| summarize
OcsfTactics = make_set_if(TacticName, isnotempty(TacticName), 14),
OcsfTechniques = make_set_if(TechniqueId, isnotempty(TechniqueId), 50),
OcsfSubTechniques = make_set_if(SubTechniqueId, isnotempty(SubTechniqueId), 50)
)
| extend
ActorUser = todynamic(OCSF.actor.process.user),
Malware = todynamic(OCSF.malware[0]),
NativeConfidenceValue = toreal(ThreatConfidence),
OcsfConfidenceValue = toreal(OCSF.confidence_score),
NativeConfidenceLabel = tolower(trim(" ", tostring(ThreatOriginalConfidence))),
OcsfConfidenceLabel = tolower(trim(" ", tostring(OCSF.confidence))),
NativeSourceId = coalesce(tostring(EventUid), tostring(AlertId), tostring(EventOriginalUid)),
OcsfSourceId = coalesce(tostring(OCSF.finding_info.uid), tostring(OCSF.uid)),
SourceIdMaterial = tostring(pack_array(tostring(OCSF.class_uid), tostring(OCSF.type_uid), tostring(OCSF.activity_id), tostring(EventStartTime), tostring(EventEndTime), tostring(EventVendor), tostring(EventProduct), tostring(EventMessage), tostring(OCSF["time"])))
| extend
SynqlyAlertId = coalesce(NativeSourceId, OcsfSourceId, strcat("generated-", hash_sha256(SourceIdMaterial))),
SynqlyAlertName = coalesce(tostring(AlertName), tostring(OCSF.finding_info["title"]), tostring(OCSF.class_name), "Synqly normalized alert event"),
SynqlyAlertDescription = coalesce(tostring(EventMessage), tostring(AlertDescription), tostring(OCSF.message), "Synqly normalized alert event"),
SeverityValue = tolower(coalesce(tostring(EventSeverity), tostring(EventOriginalSeverity), tostring(OCSF.severity))),
SynqlyTactics = coalesce(tostring(AttackTactics), strcat_array(OcsfTactics, ",")),
AllAttackTechniques = coalesce(tostring(AttackTechniques), strcat_array(array_concat(OcsfTechniques, OcsfSubTechniques), ",")),
SynqlyConfidenceScore = coalesce(
iff(NativeConfidenceValue between (0.0 .. 100.0), NativeConfidenceValue / 100.0, real(null)),
iff(OcsfConfidenceValue between (0.0 .. 100.0), OcsfConfidenceValue / 100.0, real(null))),
SynqlyConfidenceLevel = case(
NativeConfidenceLabel == "high", "High",
NativeConfidenceLabel == "low", "Low",
NativeConfidenceLabel == "unknown", "Unknown",
isnotempty(NativeConfidenceLabel), "",
OcsfConfidenceLabel == "high", "High",
OcsfConfidenceLabel == "low", "Low",
OcsfConfidenceLabel == "unknown", "Unknown",
""),
SynqlyRemediation = coalesce(tostring(AttackRemediationSteps), strcat_array(todynamic(OCSF.remediation.kb_articles), "; ")),
SynqlyAlertLink = coalesce(tostring(EventReportUrl), tostring(OCSF.finding_info.src_url)),
SourceVendor = coalesce(tostring(EventVendor), tostring(OCSF.metadata.product.vendor_name)),
SourceProduct = coalesce(tostring(EventProduct), tostring(OCSF.metadata.product.name)),
ProductVersion = coalesce(tostring(EventProductVersion), tostring(OCSF.metadata.product.version)),
OriginalSeverity = coalesce(tostring(EventOriginalSeverity), tostring(OCSF.severity)),
SourceRuleId = coalesce(
tostring(Rule),
tostring(OCSF.finding_info.analytic.uid),
iff(isnotempty(tostring(RuleName)) and not(tostring(RuleName) matches regex @"^\d+$"), tostring(RuleName), "")),
AccountName = coalesce(tostring(Username), tostring(User), tostring(ActorUser.name), OcsfUserName),
AccountSid = coalesce(iff(tolower(tostring(UserIdType)) contains "sid", tostring(UserId), ""), tostring(ActorUser.uid), OcsfUserSid),
AccountDomain = coalesce(tostring(UserScope), tostring(ActorUser.domain), OcsfUserDomain),
NativeDeviceHost = coalesce(tostring(DvcHostname), tostring(DvcFQDN)),
NativeDeviceHostDomain = coalesce(tostring(DvcDomain), extract(@"^[^.]+\.(.+)$", 1, tostring(DvcFQDN)), extract(@"^[^.]+\.(.+)$", 1, tostring(DvcHostname))),
DeviceIp = tostring(DvcIpAddr),
DestinationDomain = OcsfDestinationDomain,
EntityUrl = coalesce(tostring(Url), tostring(OCSF.finding_info.src_url)),
FileValue = coalesce(tostring(FilePath), OcsfFilePath),
ProcessIdValue = coalesce(tostring(ProcessId), OcsfProcessId),
ProcessCommandLineValue = coalesce(tostring(ProcessCommandLine), OcsfProcessCommandLine),
MalwareName = coalesce(tostring(ThreatName), tostring(Malware.name)),
FileHashValue = coalesce(tostring(FileSHA256), OcsfSHA256, tostring(FileSHA1), OcsfSHA1, tostring(FileMD5), OcsfMD5)
| extend
SourceIp = OcsfSourceIp,
DestinationIp = OcsfDestinationIp,
HostValue = coalesce(NativeDeviceHost, OcsfSourceHost),
HostDomainValue = iff(isnotempty(NativeDeviceHost), NativeDeviceHostDomain, extract(@"^[^.]+\.(.+)$", 1, OcsfSourceHost))
| extend
PrimaryIp = coalesce(SourceIp, DestinationIp, tostring(DvcIpAddr)),
PrimaryIpRole = case(isnotempty(SourceIp), "Source", isnotempty(DestinationIp), "Destination", isnotempty(tostring(DvcIpAddr)), "Device", "")
| extend
SynqlyAlertSeverity = case(
SeverityValue == "informational", "Informational",
SeverityValue == "low", "Low",
SeverityValue == "medium", "Medium",
SeverityValue in ("high", "critical", "fatal"), "High",
"Medium"),
HostNameValue = coalesce(extract(@"^([^.]+)", 1, HostValue), HostValue),
FileHashAlgorithm = case(
isnotempty(FileSHA256) or isnotempty(OcsfSHA256), "SHA256",
isnotempty(FileSHA1) or isnotempty(OcsfSHA1), "SHA1",
isnotempty(FileMD5) or isnotempty(OcsfMD5), "MD5",
"")
| project
TimeGenerated,
SynqlyAlertId,
SynqlyAlertName,
SynqlyAlertDescription,
SynqlyAlertSeverity,
SynqlyTactics,
AllAttackTechniques,
SynqlyConfidenceLevel,
SynqlyConfidenceScore,
SynqlyRemediation,
SynqlyAlertLink,
SourceVendor,
SourceProduct,
ProductVersion,
OriginalSeverity,
SourceRuleId,
AccountName,
AccountSid,
AccountDomain,
HostNameValue,
HostDomainValue,
SourceIp,
DestinationIp,
DeviceIp,
PrimaryIp,
PrimaryIpRole,
DestinationDomain,
EntityUrl,
FileValue,
FileHashAlgorithm,
FileHashValue,
ProcessIdValue,
ProcessCommandLineValue,
MalwareName
queryFrequency: 5m
description: |
Creates one Microsoft Sentinel alert for each qualifying Synqly-attributed ASIM Alert Event row. Native ASIM values take precedence, with retained OCSF data used to recover investigation context when normalized fields are empty. Replayed source rows can create additional alerts, and Microsoft Sentinel or Defender XDR may correlate related alerts into a shared incident.
id: 3192085a-e97e-440f-acb7-9227622949a4
triggerThreshold: 0
queryPeriod: 5m
version: 1.0.0
kind: Scheduled
customDetails:
SourceUrl: EntityUrl
AttackTechniques: AllAttackTechniques
DestinationDomain: DestinationDomain
DeviceIp: DeviceIp
ProductVersion: ProductVersion
SourceVendor: SourceVendor
SourceRuleId: SourceRuleId
MalwareName: MalwareName
DestinationIp: DestinationIp
SourceAlertId: SynqlyAlertId
PrimaryIpRole: PrimaryIpRole
SourceProduct: SourceProduct
FilePath: FileValue
OriginalSeverity: OriginalSeverity
SourceIp: SourceIp
status: Available
eventGroupingSettings:
aggregationKind: AlertPerResult
severity: Medium
requiredDataConnectors:
- connectorId: SynqlyIntegrationConnector
dataTypes:
- ASimAlertEventLogs
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/SynqlyIntegrationConnector/Analytic%20Rules/SynqlyAlertEventToSentinelAlert.yaml
incidentConfiguration:
groupingConfiguration:
lookbackDuration: PT5M
reopenClosedIncident: false
matchingMethod: AllEntities
enabled: false
createIncident: true
alertDetailsOverride:
alertDynamicProperties:
- value: AllAttackTechniques
alertProperty: Techniques
- value: SynqlyConfidenceLevel
alertProperty: ConfidenceLevel
- value: SynqlyConfidenceScore
alertProperty: ConfidenceScore
- value: SynqlyRemediation
alertProperty: RemediationSteps
- value: SynqlyAlertLink
alertProperty: AlertLink
alertDisplayNameFormat: '{{SynqlyAlertName}}'
alertTacticsColumnName: SynqlyTactics
alertSeverityColumnName: SynqlyAlertSeverity
alertDescriptionFormat: '{{SynqlyAlertDescription}}'
relevantTechniques: []
tactics: []
entityMappings:
- fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: Sid
columnName: AccountSid
- identifier: NTDomain
columnName: AccountDomain
entityType: Account
- fieldMappings:
- identifier: HostName
columnName: HostNameValue
- identifier: DnsDomain
columnName: HostDomainValue
entityType: Host
- fieldMappings:
- identifier: Address
columnName: PrimaryIp
entityType: IP
- fieldMappings:
- identifier: Algorithm
columnName: FileHashAlgorithm
- identifier: Value
columnName: FileHashValue
entityType: FileHash
- fieldMappings:
- identifier: ProcessId
columnName: ProcessIdValue
- identifier: CommandLine
columnName: ProcessCommandLineValue
entityType: Process
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2024-01-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/3192085a-e97e-440f-acb7-9227622949a4')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/3192085a-e97e-440f-acb7-9227622949a4')]",
"properties": {
"alertDetailsOverride": {
"alertDescriptionFormat": "{{SynqlyAlertDescription}}",
"alertDisplayNameFormat": "{{SynqlyAlertName}}",
"alertDynamicProperties": [
{
"alertProperty": "Techniques",
"value": "AllAttackTechniques"
},
{
"alertProperty": "ConfidenceLevel",
"value": "SynqlyConfidenceLevel"
},
{
"alertProperty": "ConfidenceScore",
"value": "SynqlyConfidenceScore"
},
{
"alertProperty": "RemediationSteps",
"value": "SynqlyRemediation"
},
{
"alertProperty": "AlertLink",
"value": "SynqlyAlertLink"
}
],
"alertSeverityColumnName": "SynqlyAlertSeverity",
"alertTacticsColumnName": "SynqlyTactics"
},
"alertRuleTemplateName": "3192085a-e97e-440f-acb7-9227622949a4",
"customDetails": {
"AttackTechniques": "AllAttackTechniques",
"DestinationDomain": "DestinationDomain",
"DestinationIp": "DestinationIp",
"DeviceIp": "DeviceIp",
"FilePath": "FileValue",
"MalwareName": "MalwareName",
"OriginalSeverity": "OriginalSeverity",
"PrimaryIpRole": "PrimaryIpRole",
"ProductVersion": "ProductVersion",
"SourceAlertId": "SynqlyAlertId",
"SourceIp": "SourceIp",
"SourceProduct": "SourceProduct",
"SourceRuleId": "SourceRuleId",
"SourceUrl": "EntityUrl",
"SourceVendor": "SourceVendor"
},
"description": "Creates one Microsoft Sentinel alert for each qualifying Synqly-attributed ASIM Alert Event row. Native ASIM values take precedence, with retained OCSF data used to recover investigation context when normalized fields are empty. Replayed source rows can create additional alerts, and Microsoft Sentinel or Defender XDR may correlate related alerts into a shared incident.\n",
"displayName": "Synqly Alert Event",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "AccountName",
"identifier": "Name"
},
{
"columnName": "AccountSid",
"identifier": "Sid"
},
{
"columnName": "AccountDomain",
"identifier": "NTDomain"
}
]
},
{
"entityType": "Host",
"fieldMappings": [
{
"columnName": "HostNameValue",
"identifier": "HostName"
},
{
"columnName": "HostDomainValue",
"identifier": "DnsDomain"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "PrimaryIp",
"identifier": "Address"
}
]
},
{
"entityType": "FileHash",
"fieldMappings": [
{
"columnName": "FileHashAlgorithm",
"identifier": "Algorithm"
},
{
"columnName": "FileHashValue",
"identifier": "Value"
}
]
},
{
"entityType": "Process",
"fieldMappings": [
{
"columnName": "ProcessIdValue",
"identifier": "ProcessId"
},
{
"columnName": "ProcessCommandLineValue",
"identifier": "CommandLine"
}
]
}
],
"eventGroupingSettings": {
"aggregationKind": "AlertPerResult"
},
"incidentConfiguration": {
"createIncident": true,
"groupingConfiguration": {
"enabled": false,
"lookbackDuration": "PT5M",
"matchingMethod": "AllEntities",
"reopenClosedIncident": false
}
},
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/SynqlyIntegrationConnector/Analytic%20Rules/SynqlyAlertEventToSentinelAlert.yaml",
"query": "ASimAlertEventLogs\n| where tostring(AdditionalFields._ConnectorId) == \"SynqlySentinelASIMConnector\"\n| extend OCSF = coalesce(todynamic(AdditionalFields.OCSF), dynamic({}))\n| mv-apply Evidence = array_concat(coalesce(todynamic(OCSF.evidences), dynamic([])), dynamic([{}])) on (\n mv-apply EvidenceHash = array_concat(coalesce(todynamic(Evidence.data.file.hashes), dynamic([])), coalesce(todynamic(Evidence.data.process.hashes), dynamic([])), dynamic([{}])) on (\n summarize\n OcsfSHA256 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == \"SHA256\"),\n OcsfSHA1 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == \"SHA1\"),\n OcsfMD5 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == \"MD5\")\n )\n | summarize\n OcsfUserName = take_anyif(tostring(Evidence.data.process.user.name), isnotempty(tostring(Evidence.data.process.user.name))),\n OcsfUserSid = take_anyif(tostring(Evidence.data.process.user.uid), isnotempty(tostring(Evidence.data.process.user.uid))),\n OcsfUserDomain = take_anyif(tostring(Evidence.data.process.user.domain), isnotempty(tostring(Evidence.data.process.user.domain))),\n OcsfProcessId = take_anyif(tostring(Evidence.data.process.pid), isnotempty(tostring(Evidence.data.process.pid))),\n OcsfProcessCommandLine = take_anyif(tostring(Evidence.data.process.cmd_line), isnotempty(tostring(Evidence.data.process.cmd_line))),\n OcsfFileName = take_anyif(tostring(Evidence.data.file.name), isnotempty(tostring(Evidence.data.file.name))),\n OcsfFilePath = take_anyif(tostring(Evidence.data.file.path), isnotempty(tostring(Evidence.data.file.path))),\n OcsfSourceHost = take_anyif(tostring(Evidence.data.network.src_endpoint.host.hostname), isnotempty(tostring(Evidence.data.network.src_endpoint.host.hostname))),\n OcsfSourceIp = take_anyif(tostring(Evidence.data.network.src_endpoint.ip), isnotempty(tostring(Evidence.data.network.src_endpoint.ip))),\n OcsfDestinationIp = take_anyif(tostring(Evidence.data.network.dst_endpoint.ip), isnotempty(tostring(Evidence.data.network.dst_endpoint.ip))),\n OcsfDestinationDomain = take_anyif(tostring(Evidence.data.network.dst_endpoint.domain), isnotempty(tostring(Evidence.data.network.dst_endpoint.domain))),\n OcsfSHA256 = take_anyif(OcsfSHA256, isnotempty(OcsfSHA256)),\n OcsfSHA1 = take_anyif(OcsfSHA1, isnotempty(OcsfSHA1)),\n OcsfMD5 = take_anyif(OcsfMD5, isnotempty(OcsfMD5))\n)\n| mv-apply Attack = array_concat(coalesce(todynamic(OCSF.attacks), dynamic([])), dynamic([{}])) on (\n extend TacticId = tostring(Attack.tactic.uid), TechniqueId = tostring(Attack.technique.uid), SubTechniqueId = tostring(Attack.sub_technique.uid)\n | extend TacticName = case(\n TacticId == \"TA0043\", \"Reconnaissance\", TacticId == \"TA0042\", \"ResourceDevelopment\",\n TacticId == \"TA0001\", \"InitialAccess\", TacticId == \"TA0002\", \"Execution\",\n TacticId == \"TA0003\", \"Persistence\", TacticId == \"TA0004\", \"PrivilegeEscalation\",\n TacticId == \"TA0005\", \"DefenseEvasion\", TacticId == \"TA0006\", \"CredentialAccess\",\n TacticId == \"TA0007\", \"Discovery\", TacticId == \"TA0008\", \"LateralMovement\",\n TacticId == \"TA0009\", \"Collection\", TacticId == \"TA0010\", \"Exfiltration\",\n TacticId == \"TA0011\", \"CommandAndControl\", TacticId == \"TA0040\", \"Impact\", \"\")\n | summarize\n OcsfTactics = make_set_if(TacticName, isnotempty(TacticName), 14),\n OcsfTechniques = make_set_if(TechniqueId, isnotempty(TechniqueId), 50),\n OcsfSubTechniques = make_set_if(SubTechniqueId, isnotempty(SubTechniqueId), 50)\n)\n| extend\n ActorUser = todynamic(OCSF.actor.process.user),\n Malware = todynamic(OCSF.malware[0]),\n NativeConfidenceValue = toreal(ThreatConfidence),\n OcsfConfidenceValue = toreal(OCSF.confidence_score),\n NativeConfidenceLabel = tolower(trim(\" \", tostring(ThreatOriginalConfidence))),\n OcsfConfidenceLabel = tolower(trim(\" \", tostring(OCSF.confidence))),\n NativeSourceId = coalesce(tostring(EventUid), tostring(AlertId), tostring(EventOriginalUid)),\n OcsfSourceId = coalesce(tostring(OCSF.finding_info.uid), tostring(OCSF.uid)),\n SourceIdMaterial = tostring(pack_array(tostring(OCSF.class_uid), tostring(OCSF.type_uid), tostring(OCSF.activity_id), tostring(EventStartTime), tostring(EventEndTime), tostring(EventVendor), tostring(EventProduct), tostring(EventMessage), tostring(OCSF[\"time\"])))\n| extend\n SynqlyAlertId = coalesce(NativeSourceId, OcsfSourceId, strcat(\"generated-\", hash_sha256(SourceIdMaterial))),\n SynqlyAlertName = coalesce(tostring(AlertName), tostring(OCSF.finding_info[\"title\"]), tostring(OCSF.class_name), \"Synqly normalized alert event\"),\n SynqlyAlertDescription = coalesce(tostring(EventMessage), tostring(AlertDescription), tostring(OCSF.message), \"Synqly normalized alert event\"),\n SeverityValue = tolower(coalesce(tostring(EventSeverity), tostring(EventOriginalSeverity), tostring(OCSF.severity))),\n SynqlyTactics = coalesce(tostring(AttackTactics), strcat_array(OcsfTactics, \",\")),\n AllAttackTechniques = coalesce(tostring(AttackTechniques), strcat_array(array_concat(OcsfTechniques, OcsfSubTechniques), \",\")),\n SynqlyConfidenceScore = coalesce(\n iff(NativeConfidenceValue between (0.0 .. 100.0), NativeConfidenceValue / 100.0, real(null)),\n iff(OcsfConfidenceValue between (0.0 .. 100.0), OcsfConfidenceValue / 100.0, real(null))),\n SynqlyConfidenceLevel = case(\n NativeConfidenceLabel == \"high\", \"High\",\n NativeConfidenceLabel == \"low\", \"Low\",\n NativeConfidenceLabel == \"unknown\", \"Unknown\",\n isnotempty(NativeConfidenceLabel), \"\",\n OcsfConfidenceLabel == \"high\", \"High\",\n OcsfConfidenceLabel == \"low\", \"Low\",\n OcsfConfidenceLabel == \"unknown\", \"Unknown\",\n \"\"),\n SynqlyRemediation = coalesce(tostring(AttackRemediationSteps), strcat_array(todynamic(OCSF.remediation.kb_articles), \"; \")),\n SynqlyAlertLink = coalesce(tostring(EventReportUrl), tostring(OCSF.finding_info.src_url)),\n SourceVendor = coalesce(tostring(EventVendor), tostring(OCSF.metadata.product.vendor_name)),\n SourceProduct = coalesce(tostring(EventProduct), tostring(OCSF.metadata.product.name)),\n ProductVersion = coalesce(tostring(EventProductVersion), tostring(OCSF.metadata.product.version)),\n OriginalSeverity = coalesce(tostring(EventOriginalSeverity), tostring(OCSF.severity)),\n SourceRuleId = coalesce(\n tostring(Rule),\n tostring(OCSF.finding_info.analytic.uid),\n iff(isnotempty(tostring(RuleName)) and not(tostring(RuleName) matches regex @\"^\\d+$\"), tostring(RuleName), \"\")),\n AccountName = coalesce(tostring(Username), tostring(User), tostring(ActorUser.name), OcsfUserName),\n AccountSid = coalesce(iff(tolower(tostring(UserIdType)) contains \"sid\", tostring(UserId), \"\"), tostring(ActorUser.uid), OcsfUserSid),\n AccountDomain = coalesce(tostring(UserScope), tostring(ActorUser.domain), OcsfUserDomain),\n NativeDeviceHost = coalesce(tostring(DvcHostname), tostring(DvcFQDN)),\n NativeDeviceHostDomain = coalesce(tostring(DvcDomain), extract(@\"^[^.]+\\.(.+)$\", 1, tostring(DvcFQDN)), extract(@\"^[^.]+\\.(.+)$\", 1, tostring(DvcHostname))),\n DeviceIp = tostring(DvcIpAddr),\n DestinationDomain = OcsfDestinationDomain,\n EntityUrl = coalesce(tostring(Url), tostring(OCSF.finding_info.src_url)),\n FileValue = coalesce(tostring(FilePath), OcsfFilePath),\n ProcessIdValue = coalesce(tostring(ProcessId), OcsfProcessId),\n ProcessCommandLineValue = coalesce(tostring(ProcessCommandLine), OcsfProcessCommandLine),\n MalwareName = coalesce(tostring(ThreatName), tostring(Malware.name)),\n FileHashValue = coalesce(tostring(FileSHA256), OcsfSHA256, tostring(FileSHA1), OcsfSHA1, tostring(FileMD5), OcsfMD5)\n| extend\n SourceIp = OcsfSourceIp,\n DestinationIp = OcsfDestinationIp,\n HostValue = coalesce(NativeDeviceHost, OcsfSourceHost),\n HostDomainValue = iff(isnotempty(NativeDeviceHost), NativeDeviceHostDomain, extract(@\"^[^.]+\\.(.+)$\", 1, OcsfSourceHost))\n| extend\n PrimaryIp = coalesce(SourceIp, DestinationIp, tostring(DvcIpAddr)),\n PrimaryIpRole = case(isnotempty(SourceIp), \"Source\", isnotempty(DestinationIp), \"Destination\", isnotempty(tostring(DvcIpAddr)), \"Device\", \"\")\n| extend\n SynqlyAlertSeverity = case(\n SeverityValue == \"informational\", \"Informational\",\n SeverityValue == \"low\", \"Low\",\n SeverityValue == \"medium\", \"Medium\",\n SeverityValue in (\"high\", \"critical\", \"fatal\"), \"High\",\n \"Medium\"),\n HostNameValue = coalesce(extract(@\"^([^.]+)\", 1, HostValue), HostValue),\n FileHashAlgorithm = case(\n isnotempty(FileSHA256) or isnotempty(OcsfSHA256), \"SHA256\",\n isnotempty(FileSHA1) or isnotempty(OcsfSHA1), \"SHA1\",\n isnotempty(FileMD5) or isnotempty(OcsfMD5), \"MD5\",\n \"\")\n| project\n TimeGenerated,\n SynqlyAlertId,\n SynqlyAlertName,\n SynqlyAlertDescription,\n SynqlyAlertSeverity,\n SynqlyTactics,\n AllAttackTechniques,\n SynqlyConfidenceLevel,\n SynqlyConfidenceScore,\n SynqlyRemediation,\n SynqlyAlertLink,\n SourceVendor,\n SourceProduct,\n ProductVersion,\n OriginalSeverity,\n SourceRuleId,\n AccountName,\n AccountSid,\n AccountDomain,\n HostNameValue,\n HostDomainValue,\n SourceIp,\n DestinationIp,\n DeviceIp,\n PrimaryIp,\n PrimaryIpRole,\n DestinationDomain,\n EntityUrl,\n FileValue,\n FileHashAlgorithm,\n FileHashValue,\n ProcessIdValue,\n ProcessCommandLineValue,\n MalwareName\n",
"queryFrequency": "PT5M",
"queryPeriod": "PT5M",
"severity": "Medium",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [],
"techniques": [],
"templateVersion": "1.0.0",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}