Back
Id3192085a-e97e-440f-acb7-9227622949a4
RulenameSynqly Alert Event
DescriptionCreates 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.
SeverityMedium
Required data connectorsSynqlyIntegrationConnector
KindScheduled
Query frequency5m
Query period5m
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/SynqlyIntegrationConnector/Analytic%20Rules/SynqlyAlertEventToSentinelAlert.yaml
Version1.0.0
Arm template3192085a-e97e-440f-acb7-9227622949a4.json
Deploy To Azure
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"
    }
  ]
}