diff --git a/Solutions/Global Secure Access/Analytic Rules/Identity - AfterHoursActivity.yaml b/Solutions/Global Secure Access/Analytic Rules/Identity - AfterHoursActivity.yaml new file mode 100644 index 00000000000..7c4d1b07b26 --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Identity - AfterHoursActivity.yaml @@ -0,0 +1,40 @@ +id: 4c9f0a9e-44d7-4c9b-b7f0-f6a6e0d8f8fa +name: Detect Connections Outside Operational Hours +description: This query identifies connections that occur outside of the defined operational hours. It helps in monitoring and flagging any unusual activity that may occur during non-business hours, indicating potential security concerns or policy violations. +severity: High +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1h +queryPeriod: 24h +triggerOperator: gt +triggerThreshold: 0 +tactics: + - InitialAccess +relevantTechniques: + - T1078 + - T1133 +query: | + let starttime = todatetime('{{StartTimeISO}}'); + let endtime = todatetime('{{EndTimeISO}}'); + let operational_start_hour = 8; // Start of operational hours (8 AM) + let operational_end_hour = 18; // End of operational hours (6 PM) + NetworkAccessTraffic + | where TimeGenerated between(starttime .. endtime) + | extend HourOfDay = datetime_part('hour', TimeGenerated) + | where HourOfDay < operational_start_hour or HourOfDay >= operational_end_hour + | project TimeGenerated, UserPrincipalName, SourceIp, DestinationIp, DestinationPort, Action, DeviceId, DeviceOperatingSystem, ConnectionId + | extend IPCustomEntity = SourceIp, AccountCustomEntity = UserPrincipalName +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountCustomEntity + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IPCustomEntity +version: 1.0.0 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Identity - SharedSessions.yaml b/Solutions/Global Secure Access/Analytic Rules/Identity - SharedSessions.yaml new file mode 100644 index 00000000000..f9e25e2e0a8 --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Identity - SharedSessions.yaml @@ -0,0 +1,47 @@ +id: 57abf863-1c1e-46c6-85b2-35370b712c1e +name: Detect IP Address Changes and Overlapping Sessions +description: | + This query identifies network sessions based on DeviceId and UserPrincipalName, then checks for changed IP addresses and overlapping session times. +severity: High +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1h +queryPeriod: 24h +triggerOperator: gt +triggerThreshold: 0 +tactics: + - InitialAccess +relevantTechniques: + - T1078 + - T1133 +query: | + // Identify sessions + let sessions = + NetworkAccessTraffic + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), SourceIps = make_set(SourceIp) by DeviceId, UserPrincipalName, SessionId + | sort by StartTime asc; + // Check for changed IP addresses and overlapping session times + sessions + | extend PreviousSourceIps = prev(SourceIps, 1) + | extend PreviousEndTime = prev(EndTime, 1) + | extend PreviousDeviceId = prev(DeviceId, 1) + | extend PreviousUserPrincipalName = prev(UserPrincipalName, 1) + | where DeviceId == PreviousDeviceId and UserPrincipalName == PreviousUserPrincipalName + | where set_difference(SourceIps, PreviousSourceIps) != dynamic([]) // Check if the current and previous IP sets differ + | where PreviousEndTime > StartTime // Check for overlapping session times + | project DeviceId, UserPrincipalName, SourceIps, PreviousSourceIps, StartTime, EndTime, PreviousEndTime + | extend IPCustomEntity = tostring(array_slice(SourceIps, 0, 1)[0]), PreviousIPCustomEntity = tostring(array_slice(PreviousSourceIps, 0, 1)[0]), AccountCustomEntity = UserPrincipalName +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountCustomEntity + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IPCustomEntity +version: 1.0.0 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - External User added to Team and immediately uploads file.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - External User added to Team and immediately uploads file.yaml new file mode 100644 index 00000000000..e580abc4235 --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - External User added to Team and immediately uploads file.yaml @@ -0,0 +1,86 @@ +id: 4d38f80f-6b7d-4a1f-aeaf-e38df637e6ac +name: Accessed files shared by temporary external user +description: | + 'This detection identifies when an external user is added to a Team or Teams chat and shares a file which is accessed by many users (>10) and the users is removed within short period of time. This might be an indicator of suspicious activity.' +severity: Low +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1h +queryPeriod: 1h +triggerOperator: gt +triggerThreshold: 0 +tactics: + - InitialAccess +relevantTechniques: + - T1566 +query: | + let fileAccessThreshold = 10; + EnrichedMicrosoft365AuditLogs + | where Workload =~ "MicrosoftTeams" + | where Operation =~ "MemberAdded" + | extend MemberAdded = tostring(parse_json(tostring(AdditionalProperties)).Members[0].UPN) + | where MemberAdded contains "#EXT#" + | project TimeAdded = TimeGenerated, Operation, MemberAdded, UserWhoAdded = UserId, TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName) + | join kind=inner ( + EnrichedMicrosoft365AuditLogs + | where Workload =~ "MicrosoftTeams" + | where Operation =~ "MemberRemoved" + | extend MemberAdded = tostring(parse_json(tostring(AdditionalProperties)).Members[0].UPN) + | where MemberAdded contains "#EXT#" + | project TimeDeleted = TimeGenerated, Operation, MemberAdded, UserWhoDeleted = UserId, TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName) + ) on MemberAdded, TeamName + | where TimeDeleted > TimeAdded + | join kind=inner ( + EnrichedMicrosoft365AuditLogs + | where RecordType == "SharePointFileOperation" + | where Operation == "FileUploaded" + | extend MemberAdded = UserId, SourceRelativeUrl = tostring(parse_json(tostring(AdditionalProperties)).SourceRelativeUrl), TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName) + | where SourceRelativeUrl has "Microsoft Teams Chat Files" + | join kind=inner ( + EnrichedMicrosoft365AuditLogs + | where RecordType == "SharePointFileOperation" + | where Operation == "FileAccessed" + | extend SourceRelativeUrl = tostring(parse_json(tostring(AdditionalProperties)).SourceRelativeUrl), TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName) + | where SourceRelativeUrl has "Microsoft Teams Chat Files" + | summarize FileAccessCount = count() by ObjectId, TeamName + | where FileAccessCount > fileAccessThreshold + ) on ObjectId, TeamName + ) on MemberAdded, TeamName + | project-away MemberAdded1, MemberAdded2, ObjectId1, Operation1, Operation2 + | extend MemberAddedAccountName = tostring(split(MemberAdded, "@")[0]), MemberAddedAccountUPNSuffix = tostring(split(MemberAdded, "@")[1]) + | extend UserWhoAddedAccountName = tostring(split(UserWhoAdded, "@")[0]), UserWhoAddedAccountUPNSuffix = tostring(split(UserWhoAdded, "@")[1]) + | extend UserWhoDeletedAccountName = tostring(split(UserWhoDeleted, "@")[0]), UserWhoDeletedAccountUPNSuffix = tostring(split(UserWhoDeleted, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: MemberAdded + - identifier: Name + columnName: MemberAddedAccountName + - identifier: UPNSuffix + columnName: MemberAddedAccountUPNSuffix + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserWhoAdded + - identifier: Name + columnName: UserWhoAddedAccountName + - identifier: UPNSuffix + columnName: UserWhoAddedAccountUPNSuffix + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserWhoDeleted + - identifier: Name + columnName: UserWhoDeletedAccountName + - identifier: UPNSuffix + columnName: UserWhoDeletedAccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIP +version: 2.1.1 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - ExternalUserAddedRemovedInTeams.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - ExternalUserAddedRemovedInTeams.yaml new file mode 100644 index 00000000000..22c8a1db41d --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - ExternalUserAddedRemovedInTeams.yaml @@ -0,0 +1,71 @@ +id: 1a8f1297-23a4-4f09-a20b-90af8fc3641a +name: External User Added and Removed in Short Timeframe +description: | + This detection flags the occurrences of external user accounts that are added to a Team and then removed within one hour. +severity: Low +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1h +queryPeriod: 1h +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Persistence +relevantTechniques: + - T1136 +query: | + let TeamsAddDel = (Op:string){ + EnrichedMicrosoft365AuditLogs + | where Workload =~ "MicrosoftTeams" + | where Operation == Op + | where tostring(AdditionalProperties.Members) has ("#EXT#") + | mv-expand Members = parse_json(tostring(AdditionalProperties.Members)) + | extend UPN = tostring(Members.UPN) + | where UPN has ("#EXT#") + | project TimeGenerated, Operation, UPN, UserId, TeamName = tostring(AdditionalProperties.TeamName), ClientIP = SourceIp + }; + let TeamsAdd = TeamsAddDel("MemberAdded") + | project TimeAdded = TimeGenerated, Operation, MemberAdded = UPN, UserWhoAdded = UserId, TeamName, ClientIP; + let TeamsDel = TeamsAddDel("MemberRemoved") + | project TimeDeleted = TimeGenerated, Operation, MemberRemoved = UPN, UserWhoDeleted = UserId, TeamName, ClientIP; + TeamsAdd + | join kind = inner (TeamsDel) on $left.MemberAdded == $right.MemberRemoved + | where TimeDeleted > TimeAdded + | project TimeAdded, TimeDeleted, MemberAdded_Removed = MemberAdded, UserWhoAdded, UserWhoDeleted, TeamName + | extend MemberAdded_RemovedAccountName = tostring(split(MemberAdded_Removed, "@")[0]), MemberAddedAccountUPNSuffix = tostring(split(MemberAdded_Removed, "@")[1]) + | extend UserWhoAddedAccountName = tostring(split(UserWhoAdded, "@")[0]), UserWhoAddedAccountUPNSuffix = tostring(split(UserWhoAdded, "@")[1]) + | extend UserWhoDeletedAccountName = tostring(split(UserWhoDeleted, "@")[0]), UserWhoDeletedAccountUPNSuffix = tostring(split(UserWhoDeleted, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: MemberAdded_Removed + - identifier: Name + columnName: MemberAdded_RemovedAccountName + - identifier: UPNSuffix + columnName: MemberAddedAccountUPNSuffix + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserWhoAdded + - identifier: Name + columnName: UserWhoAddedAccountName + - identifier: UPNSuffix + columnName: UserWhoAddedAccountUPNSuffix + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserWhoDeleted + - identifier: Name + columnName: UserWhoDeletedAccountName + - identifier: UPNSuffix + columnName: UserWhoDeletedAccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIp +version: 2.1.2 +kind: Scheduled \ No newline at end of file diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - Mail_redirect_via_ExO_transport_rule.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - Mail_redirect_via_ExO_transport_rule.yaml new file mode 100644 index 00000000000..3e29130708c --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - Mail_redirect_via_ExO_transport_rule.yaml @@ -0,0 +1,55 @@ +id: edcfc2e0-3134-434c-8074-9101c530d419 +name: Mail redirect via ExO transport rule +description: | + 'Identifies when Exchange Online transport rule configured to forward emails. + This could be an adversary mailbox configured to collect mail from multiple user accounts.' +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1h +queryPeriod: 1h +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Collection + - Exfiltration +relevantTechniques: + - T1114 + - T1020 +query: | + EnrichedMicrosoft365AuditLogs + | where Workload == "Exchange" + | where Operation in~ ("New-TransportRule", "Set-TransportRule") + | mv-apply DynamicParameters = todynamic(tostring(AdditionalProperties.Parameters)) on ( + summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value)) + ) + | extend RuleName = case( + Operation =~ "Set-TransportRule", ObjectId, // Assuming ObjectId maps to what was previously OfficeObjectId + Operation =~ "New-TransportRule", ParsedParameters.Name, + "Unknown" + ) + | mv-expand ExpandedParameters = todynamic(tostring(AdditionalProperties.Parameters)) + | where ExpandedParameters.Name in~ ("BlindCopyTo", "RedirectMessageTo") and isnotempty(ExpandedParameters.Value) + | extend RedirectTo = ExpandedParameters.Value + | extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P\d+))?', dynamic(["IPAddress", "Port"]), ClientIp)[0] + | extend From = ParsedParameters.From + | project TimeGenerated, RedirectTo, IPAddress = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1]), UserId, From, Operation, RuleName, Parameters = tostring(AdditionalProperties.Parameters) + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IPAddress +version: 2.0.4 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - Malicious_Inbox_Rule.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - Malicious_Inbox_Rule.yaml new file mode 100644 index 00000000000..6c8238c2e2c --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - Malicious_Inbox_Rule.yaml @@ -0,0 +1,56 @@ +id: a9c76c8d-f60d-49ec-9b1f-bdfee6db3807 +name: Malicious Inbox Rule +description: | + 'Often times after the initial compromise the attackers create inbox rules to delete emails that contain certain keywords. + This is done so as to limit ability to warn compromised users that they've been compromised. Below is a sample query that tries to detect this. + Reference: https://www.reddit.com/r/sysadmin/comments/7kyp0a/recent_phishing_attempts_my_experience_and_what/' +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 1d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Persistence + - DefenseEvasion +relevantTechniques: + - T1098 + - T1078 +query: | + let Keywords = dynamic(["helpdesk", "alert", "suspicious", "fake", "malicious", "phishing", "spam", "do not click", "do not open", "hijacked", "Fatal"]); + EnrichedMicrosoft365AuditLogs + | where Workload =~ "Exchange" + | where Operation =~ "New-InboxRule" and (ResultStatus =~ "True" or ResultStatus =~ "Succeeded") + | where tostring(parse_json(tostring(AdditionalProperties)).Parameters) has "Deleted Items" or tostring(parse_json(tostring(AdditionalProperties)).Parameters) has "Junk Email" or tostring(parse_json(tostring(AdditionalProperties)).Parameters) has "DeleteMessage" + | extend Events = parse_json(tostring(AdditionalProperties)).Parameters + | extend SubjectContainsWords = tostring(Events.SubjectContainsWords), BodyContainsWords = tostring(Events.BodyContainsWords), SubjectOrBodyContainsWords = tostring(Events.SubjectOrBodyContainsWords) + | where SubjectContainsWords has_any (Keywords) or BodyContainsWords has_any (Keywords) or SubjectOrBodyContainsWords has_any (Keywords) + | extend ClientIPAddress = case(ClientIp has ".", tostring(split(ClientIp, ":")[0]), ClientIp has "[", tostring(trim_start(@'[[]',tostring(split(ClientIp, "]")[0]))), ClientIp) + | extend Keyword = iff(isnotempty(SubjectContainsWords), SubjectContainsWords, (iff(isnotempty(BodyContainsWords), BodyContainsWords, SubjectOrBodyContainsWords))) + | extend RuleDetail = case(ObjectId contains '/', tostring(split(ObjectId, '/')[-1]), tostring(split(ObjectId, '\\')[-1])) + | summarize count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by Operation, UserId, ClientIPAddress, ResultStatus, Keyword, ObjectId, RuleDetail + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) + +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: Host + fieldMappings: + - identifier: FullName + columnName: OriginatingServer + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIPAddress +version: 2.0.4 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - MultipleTeamsDeletes.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - MultipleTeamsDeletes.yaml new file mode 100644 index 00000000000..594864e0623 --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - MultipleTeamsDeletes.yaml @@ -0,0 +1,39 @@ +id: db60e4b6-a845-4f28-a18c-94ebbaad6c6c +name: Multiple Teams deleted by a single user +description: | + 'This detection flags the occurrences of deleting multiple teams within an hour. + This data is a part of Office 365 Connector in Microsoft Sentinel.' +severity: Low +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 1d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Impact +relevantTechniques: + - T1485 + - T1489 +query: | + let max_delete_count = 3; + EnrichedMicrosoft365AuditLogs + | where Workload =~ "MicrosoftTeams" + | where Operation =~ "TeamDeleted" + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), DeletedTeams = make_set(tostring(AdditionalProperties.TeamName), 1000) by UserId + | where array_length(DeletedTeams) > max_delete_count + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix +version: 2.0.4 +kind: Scheduled \ No newline at end of file diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - Office_MailForwarding.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - Office_MailForwarding.yaml new file mode 100644 index 00000000000..4b9607a472f --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - Office_MailForwarding.yaml @@ -0,0 +1,61 @@ +id: d75e8289-d1cb-44d4-bd59-2f44a9172478 +name: Multiple Users Email Forwarded to Same Destination +description: | + Identifies when multiple (more than one) users' mailboxes are configured to forward to the same destination. + This could be an attacker-controlled destination mailbox configured to collect mail from multiple compromised user accounts. +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 7d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Collection + - Exfiltration +relevantTechniques: + - T1114 + - T1020 +query: | + let queryfrequency = 1d; + let queryperiod = 7d; + EnrichedMicrosoft365AuditLogs + | where TimeGenerated > ago(queryperiod) + | where Workload =~ "Exchange" + //| where Operation in ("Set-Mailbox", "New-InboxRule", "Set-InboxRule") // Uncomment or adjust based on actual field usage + | where tostring(AdditionalProperties.Parameters) has_any ("ForwardTo", "RedirectTo", "ForwardingSmtpAddress") + | mv-apply DynamicParameters = todynamic(tostring(AdditionalProperties.Parameters)) on ( + summarize ParsedParameters = make_bag(bag_pack(tostring(DynamicParameters.Name), DynamicParameters.Value)) + ) + | evaluate bag_unpack(ParsedParameters, columnsConflict='replace_source') + | extend DestinationMailAddress = tolower(case( + isnotempty(column_ifexists("ForwardTo", "")), column_ifexists("ForwardTo", ""), + isnotempty(column_ifexists("RedirectTo", "")), column_ifexists("RedirectTo", ""), + isnotempty(column_ifexists("ForwardingSmtpAddress", "")), trim_start(@"smtp:", column_ifexists("ForwardingSmtpAddress", "")), + "")) + | where isnotempty(DestinationMailAddress) + | mv-expand split(DestinationMailAddress, ";") + | extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P\d+))?', dynamic(["IPAddress", "Port"]), ClientIp)[0] + | extend ClientIP = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1]) + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), DistinctUserCount = dcount(UserId), UserId = make_set(UserId, 250), Ports = make_set(Port, 250), EventCount = count() by tostring(DestinationMailAddress), ClientIP + | where DistinctUserCount > 1 and EndTime > ago(queryfrequency) + | mv-expand UserId to typeof(string) + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIP +version: 2.0.3 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - Office_Uploaded_Executables.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - Office_Uploaded_Executables.yaml new file mode 100644 index 00000000000..36564efeb3a --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - Office_Uploaded_Executables.yaml @@ -0,0 +1,80 @@ +id: 178c62b4-d5e5-40f5-8eab-7fccd0051e7a +name: New Executable via Office FileUploaded Operation +description: | + Identifies when executable file types are uploaded to Office services such as SharePoint and OneDrive. + List currently includes exe, inf, gzip, cmd, bat file extensions. + Additionally, identifies when a given user is uploading these files to another user's workspace. + This may be an indication of a staging location for malware or other malicious activity. +severity: Low +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 8d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - CommandAndControl + - LateralMovement +relevantTechniques: + - T1105 + - T1570 +query: | + let threshold = 2; + let uploadOp = 'FileUploaded'; + let execExt = dynamic(['exe', 'inf', 'gzip', 'cmd', 'bat']); + let starttime = 8d; + let endtime = 1d; + EnrichedMicrosoft365AuditLogs + | where TimeGenerated >= ago(endtime) + | where Operation == uploadOp + | extend SourceFileExtension = extract(@"\.([^\./]+)$", 1, tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)) // Extract file extension + | where SourceFileExtension in (execExt) + | extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl) + | extend SourceRelativeUrl = tostring(parse_json(tostring(AdditionalProperties)).SourceRelativeUrl) + | extend SourceFileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName) + | project TimeGenerated, Id, Workload, RecordType, Operation, UserType, UserKey, UserId, ClientIp, UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent), Site_Url, SourceRelativeUrl, SourceFileName + | join kind=leftanti ( + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (ago(starttime) .. ago(endtime)) + | where Operation == uploadOp + | extend SourceFileExtension = extract(@"\.([^\./]+)$", 1, tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)) // Extract file extension + | where SourceFileExtension in (execExt) + | extend SourceRelativeUrl = tostring(parse_json(tostring(AdditionalProperties)).SourceRelativeUrl) + | summarize SourceRelativeUrl = make_set(SourceRelativeUrl, 100000), UserId = make_set(UserId, 100000), PrevSeenCount = count() by SourceFileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName) + // Uncomment the line below to enforce the threshold + // | where PrevSeenCount > threshold + | mvexpand SourceRelativeUrl, UserId + | extend SourceRelativeUrl = tostring(SourceRelativeUrl), UserId = tostring(UserId) + ) on SourceFileName, SourceRelativeUrl, UserId + | extend SiteUrlUserFolder = tolower(split(Site_Url, '/')[-2]) + | extend UserIdUserFolderFormat = tolower(replace_regex(UserId, '@|\\.', '_')) + | extend UserIdDiffThanUserFolder = iff(Site_Url has '/personal/' and SiteUrlUserFolder != UserIdUserFolderFormat, true, false) + | summarize TimeGenerated = make_list(TimeGenerated, 100000), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), UserAgents = make_list(UserAgent, 100000), Ids = make_list(Id, 100000), SourceRelativeUrls = make_list(SourceRelativeUrl, 100000), FileNames = make_list(SourceFileName, 100000) + by Workload, RecordType, Operation, UserType, UserKey, UserId, ClientIp, Site_Url, SiteUrlUserFolder, UserIdUserFolderFormat, UserIdDiffThanUserFolder + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIp + - entityType: URL + fieldMappings: + - identifier: Url + columnName: Site_Url + - entityType: File + fieldMappings: + - identifier: Name + columnName: FileNames +version: 2.0.5 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - RareOfficeOperations.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - RareOfficeOperations.yaml new file mode 100644 index 00000000000..7cbbc57bff2 --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - RareOfficeOperations.yaml @@ -0,0 +1,45 @@ +id: 433c254d-4b84-46f7-99ec-9dfefb5f6a7b +name: Rare and Potentially High-Risk Office Operations +description: | + Identifies Office operations that are typically rare and can provide capabilities useful to attackers. +severity: Low +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 1d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Persistence + - Collection +relevantTechniques: + - T1098 + - T1114 +query: | + EnrichedMicrosoft365AuditLogs + | where Operation in~ ( "Add-MailboxPermission", "Add-MailboxFolderPermission", "Set-Mailbox", "New-ManagementRoleAssignment", "New-InboxRule", "Set-InboxRule", "Set-TransportRule") + and not(UserId has_any ('NT AUTHORITY\\SYSTEM (Microsoft.Exchange.ServiceHost)', 'NT AUTHORITY\\SYSTEM (Microsoft.Exchange.AdminApi.NetCore)', 'NT AUTHORITY\\SYSTEM (w3wp)', 'devilfish-applicationaccount') and Operation in~ ( "Add-MailboxPermission", "Set-Mailbox")) + | extend ClientIPOnly = tostring(extract_all(@'\[?(::ffff:)?(?P(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?', dynamic(["IPAddress"]), ClientIp)[0]) + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIPOnly + - entityType: CloudApplication + fieldMappings: + - identifier: AppId + columnName: AppId +version: 2.0.5 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - SharePoint_Downloads_byNewIP.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - SharePoint_Downloads_byNewIP.yaml new file mode 100644 index 00000000000..76dbad90889 --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - SharePoint_Downloads_byNewIP.yaml @@ -0,0 +1,72 @@ +id: 7460e34e-4c99-47b2-b7c0-c42e339fc586 +name: SharePoint File Operation via Previously Unseen IPs +description: | + Identifies anomalies using user behavior by setting a threshold for significant changes in file upload/download activities from new IP addresses. It establishes a baseline of typical behavior, compares it to recent activity, and flags deviations exceeding a default threshold of 25. +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 14d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Exfiltration +relevantTechniques: + - T1030 +query: | + let threshold = 0.25; + let szSharePointFileOperation = "SharePointFileOperation"; + let szOperations = dynamic(["FileDownloaded", "FileUploaded"]); + let starttime = 14d; + let endtime = 1d; + // Define a baseline of normal user behavior + let userBaseline = EnrichedMicrosoft365AuditLogs + | where TimeGenerated between(ago(starttime)..ago(endtime)) + | where RecordType == szSharePointFileOperation + | where Operation in (szOperations) + | extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent) + | extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl) + | where isnotempty(UserAgent) + | summarize Count = count() by UserId, Operation, Site_Url, ClientIp + | summarize AvgCount = avg(Count) by UserId, Operation, Site_Url, ClientIp; + // Get recent user activity + let recentUserActivity = EnrichedMicrosoft365AuditLogs + | where TimeGenerated > ago(endtime) + | where RecordType == szSharePointFileOperation + | where Operation in (szOperations) + | extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent) + | extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl) + | where isnotempty(UserAgent) + | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), RecentCount = count() by UserId, UserType, Operation, Site_Url, ClientIp, ObjectId, Workload, UserAgent; + // Join the baseline and recent activity, and calculate the deviation + let UserBehaviorAnalysis = userBaseline + | join kind=inner (recentUserActivity) on UserId, Operation, Site_Url, ClientIp + | extend Deviation = abs(RecentCount - AvgCount) / AvgCount; + // Filter for significant deviations + UserBehaviorAnalysis + | where Deviation > threshold + | project StartTimeUtc, EndTimeUtc, UserId, UserType, Operation, ClientIp, Site_Url, ObjectId, Workload, UserAgent, Deviation, Count=RecentCount + | order by Count desc, ClientIp asc, Operation asc, UserId asc + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIp + - entityType: URL + fieldMappings: + - identifier: Url + columnName: Site_Url +version: 2.0.4 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - SharePoint_Downloads_byNewUserAgent.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - SharePoint_Downloads_byNewUserAgent.yaml new file mode 100644 index 00000000000..3af5175c877 --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - SharePoint_Downloads_byNewUserAgent.yaml @@ -0,0 +1,85 @@ +id: efd17c5f-5167-40f8-a1e9-0818940785d9 +name: SharePointFileOperation via devices with previously unseen user agents +description: | + Identifies anomalies if the number of documents uploaded or downloaded from device(s) associated with a previously unseen user agent exceeds a threshold (default is 5) and deviation (default is 25%). +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 14d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Exfiltration +relevantTechniques: + - T1030 +query: | + // Set threshold for the number of downloads/uploads from a new user agent + let threshold = 5; + // Define constants for SharePoint file operations + let szSharePointFileOperation = "SharePointFileOperation"; + let szOperations = dynamic(["FileDownloaded", "FileUploaded"]); + // Define the historical activity for analysis + let starttime = 14d; // Define the start time for historical data (14 days ago) + let endtime = 1d; // Define the end time for historical data (1 day ago) + // Extract the base events for analysis + let Baseevents = + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (ago(starttime) .. ago(endtime)) + | where RecordType == szSharePointFileOperation + | where Operation in (szOperations) + | extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent) + | extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl) + | where isnotempty(UserAgent); + // Identify frequently occurring user agents + let FrequentUA = Baseevents + | summarize FUACount = count() by UserAgent, RecordType, Operation + | where FUACount >= threshold + | distinct UserAgent; + // Calculate a user baseline for further analysis + let UserBaseLine = Baseevents + | summarize Count = count() by UserId, Operation, Site_Url + | summarize AvgCount = avg(Count) by UserId, Operation, Site_Url; + // Extract recent activity for analysis + let RecentActivity = EnrichedMicrosoft365AuditLogs + | where TimeGenerated > ago(endtime) + | where RecordType == szSharePointFileOperation + | where Operation in (szOperations) + | extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent) + | extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl) + | where isnotempty(UserAgent) + | where UserAgent in (FrequentUA) + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ObjectIdCount = dcount(ObjectId), ObjectIdList = make_set(ObjectId), UserAgentSeenCount = count() + by RecordType, Operation, UserAgent, UserId, ClientIp, Site_Url; + // Analyze user behavior based on baseline and recent activity + let UserBehaviorAnalysis = UserBaseLine + | join kind=inner (RecentActivity) on UserId, Operation, Site_Url + | extend Deviation = abs(UserAgentSeenCount - AvgCount) / AvgCount; + // Filter and format results for specific user behavior analysis + UserBehaviorAnalysis + | where Deviation > 0.25 + | extend UserIdName = tostring(split(UserId, '@')[0]), UserIdUPNSuffix = tostring(split(UserId, '@')[1]) + | project-reorder StartTime, EndTime, UserAgent, UserAgentSeenCount, UserId, ClientIp, Site_Url + | order by UserAgentSeenCount desc, UserAgent asc, UserId asc, Site_Url asc +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: UserIdName + - identifier: UPNSuffix + columnName: UserIdUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIp + - entityType: URL + fieldMappings: + - identifier: Url + columnName: Site_Url +version: 2.2.4 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - exchange_auditlogdisabled.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - exchange_auditlogdisabled.yaml new file mode 100644 index 00000000000..cfba77230ca --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - exchange_auditlogdisabled.yaml @@ -0,0 +1,49 @@ +id: dc451755-8ab3-4059-b805-e454c45d1d44 +name: Exchange AuditLog Disabled +description: | + 'Identifies when the exchange audit logging has been disabled which may be an adversary attempt to evade detection or avoid other defenses.' +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 1d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - DefenseEvasion +relevantTechniques: + - T1562 +query: | + EnrichedMicrosoft365AuditLogs + | where Workload =~ "Exchange" + | where UserType in~ ("Admin", "DcAdmin") + | where Operation =~ "Set-AdminAuditLogConfig" + | extend AdminAuditLogEnabledValue = tostring(parse_json(tostring(parse_json(tostring(array_slice(parse_json(tostring(AdditionalProperties.Parameters)), 3, 3)))[0])).Value) + | where AdminAuditLogEnabledValue =~ "False" + | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP = SourceIp, ResultStatus, Parameters = tostring(AdditionalProperties.Parameters), AdminAuditLogEnabledValue + | extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId) + | extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '') + | extend AccountName = iff(UserId contains '\\', tostring(split(UserId, '\\')[1]), AccountName) + | extend AccountNTDomain = iff(UserId contains '\\', tostring(split(UserId, '\\')[0]), '') +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountNTDomain + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIP +version: 2.0.6 +kind: Scheduled \ No newline at end of file diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - office_policytampering.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - office_policytampering.yaml new file mode 100644 index 00000000000..c7fc2444c0c --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - office_policytampering.yaml @@ -0,0 +1,59 @@ +id: 0f1f2b17-f9d6-4d2a-a0fb-a7ae1659e3eb +name: Office Policy Tampering +description: | + Identifies if any tampering is done to either audit log, ATP Safelink, SafeAttachment, AntiPhish, or Dlp policy. + An adversary may use this technique to evade detection or avoid other policy-based defenses. + References: https://docs.microsoft.com/powershell/module/exchange/advanced-threat-protection/remove-antiphishrule?view=exchange-ps. +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 1d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Persistence + - DefenseEvasion +relevantTechniques: + - T1098 + - T1562 +query: | + let opList = EnrichedMicrosoft365AuditLogs + | summarize by Operation + | where Operation has_any ("Remove", "Disable") + | where Operation contains "AntiPhish" or Operation contains "SafeAttachment" or Operation contains "SafeLinks" or Operation contains "Dlp" or Operation contains "Audit" + | summarize make_set(Operation, 500); + EnrichedMicrosoft365AuditLogs + | where RecordType == "ExchangeAdmin" + | where UserType in~ ("Admin", "DcAdmin") + | where Operation in~ (opList) + | extend ClientIPOnly = case( + ClientIp has ".", tostring(split(ClientIp, ":")[0]), + ClientIp has "[", tostring(trim_start(@'[[]', tostring(split(ClientIp, "]")[0]))), + ClientIp + ) + | extend Port = case( + ClientIp has ".", tostring(split(ClientIp, ":")[1]), + ClientIp has "[", tostring(split(ClientIp, "]:")[1]), + "" + ) + | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP = ClientIPOnly, Port, ResultStatus, Parameters = tostring(AdditionalProperties.Parameters) + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIP +version: 2.0.3 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - sharepoint_file_transfer_above_threshold.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - sharepoint_file_transfer_above_threshold.yaml new file mode 100644 index 00000000000..d03b2943d5f --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - sharepoint_file_transfer_above_threshold.yaml @@ -0,0 +1,59 @@ +id: 30375d00-68cc-4f95-b89a-68064d566358 +name: Office365 Sharepoint File Transfer Above Threshold +description: | + Identifies Office365 Sharepoint File Transfers above a certain threshold in a 15-minute time period. + Please note that entity mapping for arrays is not supported, so when there is a single value in an array, we will pull that value from the array as a single string to populate the entity to support entity mapping features within Sentinel. Additionally, if the array is multivalued, we will input a string to indicate this with a unique hash so that matching will not occur. +severity: Medium +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 15m +queryPeriod: 15m +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Exfiltration +relevantTechniques: + - T1020 +query: | + let threshold = 5000; + EnrichedMicrosoft365AuditLogs + | where Workload has_any("SharePoint", "OneDrive") and Operation has_any("FileDownloaded", "FileSyncDownloadedFull", "FileSyncUploadedFull", "FileUploaded") + | summarize count_distinct_ObjectId=dcount(ObjectId), fileslist=make_set(ObjectId, 10000) by UserId, ClientIp, bin(TimeGenerated, 15m) + | where count_distinct_ObjectId >= threshold + | extend FileSample = iff(array_length(fileslist) == 1, tostring(fileslist[0]), strcat("SeeFilesListField","_", tostring(hash(tostring(fileslist))))) + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIp + - entityType: File + fieldMappings: + - identifier: Name + columnName: FileSample +customDetails: + TransferCount: count_distinct_ObjectId + FilesList: fileslist +incidentConfiguration: + createIncident: true + groupingConfiguration: + enabled: true + reopenClosedIncident: false + lookbackDuration: 5h + matchingMethod: Selected + groupByEntities: + - Account + groupByAlertDetails: [] + groupByCustomDetails: [] +version: 1.0.4 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/Office 365 - sharepoint_file_transfer_folders_above_threshold.yaml b/Solutions/Global Secure Access/Analytic Rules/Office 365 - sharepoint_file_transfer_folders_above_threshold.yaml new file mode 100644 index 00000000000..7401cc275cf --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/Office 365 - sharepoint_file_transfer_folders_above_threshold.yaml @@ -0,0 +1,61 @@ +id: abd6976d-8f71-4851-98c4-4d086201319c +name: Office365 Sharepoint File Transfer Above Threshold +description: | + Identifies Office365 Sharepoint File Transfers with a distinct folder count above a certain threshold in a 15-minute time period. + Please note that entity mapping for arrays is not supported, so when there is a single value in an array, we will pull that value from the array as a single string to populate the entity to support entity mapping features within Sentinel. Additionally, if the array is multivalued, we will input a string to indicate this with a unique hash so that matching will not occur. +severity: Medium +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 15m +queryPeriod: 15m +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Exfiltration +relevantTechniques: + - T1020 +query: | + let threshold = 500; + EnrichedMicrosoft365AuditLogs + | where Workload has_any("SharePoint", "OneDrive") and Operation has_any("FileDownloaded", "FileSyncDownloadedFull", "FileSyncUploadedFull", "FileUploaded") + | extend EventSource = tostring(parse_json(tostring(AdditionalProperties)).EventSource) + | extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent) + | summarize count_distinct_ObjectId = dcount(ObjectId), dirlist = make_set(ObjectId, 10000) by UserId, ClientIp, UserAgent, bin(TimeGenerated, 15m) + | where count_distinct_ObjectId >= threshold + | extend DirSample = iff(array_length(dirlist) == 1, tostring(dirlist[0]), strcat("SeeDirListField","_", tostring(hash(tostring(dirlist))))) + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserId + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIp + - entityType: File + fieldMappings: + - identifier: Name + columnName: DirSample +customDetails: + TransferCount: count_distinct_ObjectId + FilesList: dirlist +incidentConfiguration: + createIncident: true + groupingConfiguration: + enabled: true + reopenClosedIncident: false + lookbackDuration: 5h + matchingMethod: Selected + groupByEntities: + - Account + groupByAlertDetails: [] + groupByCustomDetails: [] +version: 1.0.4 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/SWG - Abnormal Deny Rate.yaml b/Solutions/Global Secure Access/Analytic Rules/SWG - Abnormal Deny Rate.yaml new file mode 100644 index 00000000000..137210a2b0a --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/SWG - Abnormal Deny Rate.yaml @@ -0,0 +1,58 @@ +id: e3b6a9e7-4c3a-45e6-8baf-1d3bfa8e0c2b +name: Detect Abnormal Deny Rate for Source to Destination IP +description: | + Identifies abnormal deny rate for specific source IP to destination IP based on the normal average and standard deviation learned during a configured period. This can indicate potential exfiltration, initial access, or C2, where an attacker tries to exploit the same vulnerability on machines in the organization but is being blocked by firewall rules. +configurableParameters: + - minimumOfStdsThreshold: The number of stds to use in the threshold calculation. Default is set to 3. + - learningPeriodTime: Learning period for threshold calculation in days. Default is set to 5. + - binTime: Learning buckets time in hours. Default is set to 1 hour. + - minimumThreshold: Minimum threshold for alert. Default is set to 5. + - minimumBucketThreshold: Minimum learning buckets threshold for alert. Default is set to 5. +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - NetworkAccessTrafficLogs +queryFrequency: 1h +queryPeriod: 25h +triggerOperator: gt +triggerThreshold: 1 +tactics: + - InitialAccess + - Exfiltration + - CommandAndControl +relevantTechniques: [] +query: | + let NumOfStdsThreshold = 3; + let LearningPeriod = 5d; + let BinTime = 1h; + let MinThreshold = 5.0; + let MinLearningBuckets = 5; + let TrafficLogs = NetworkAccessTraffic + | where Action == 'Denied' + | where isnotempty(DestinationIp) and isnotempty(SourceIp); + let LearningSrcIpDenyRate = TrafficLogs + | where TimeGenerated between (ago(LearningPeriod + 1d) .. ago(1d)) + | summarize count() by SourceIp, bin(TimeGenerated, BinTime), DestinationIp + | summarize LearningTimeSrcIpDenyRateAvg = avg(count_), LearningTimeSrcIpDenyRateStd = stdev(count_), LearningTimeBuckets = count() by SourceIp, DestinationIp + | where LearningTimeBuckets > MinLearningBuckets; + let AlertTimeSrcIpDenyRate = TrafficLogs + | where TimeGenerated between (ago(1h) .. now()) + | summarize AlertTimeSrcIpDenyRateCount = count() by SourceIp, DestinationIp; + AlertTimeSrcIpDenyRate + | join kind=leftouter (LearningSrcIpDenyRate) on SourceIp, DestinationIp + | extend LearningThreshold = max_of(LearningTimeSrcIpDenyRateAvg + NumOfStdsThreshold * LearningTimeSrcIpDenyRateStd, MinThreshold) + | where AlertTimeSrcIpDenyRateCount > LearningThreshold + | project SourceIp, DestinationIp, AlertTimeSrcIpDenyRateCount, LearningThreshold +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: SourceIp + - entityType: URL + fieldMappings: + - identifier: Url + columnName: DestinationIp +version: 1.0.0 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/SWG - Abnormal Port to Protocol.yaml b/Solutions/Global Secure Access/Analytic Rules/SWG - Abnormal Port to Protocol.yaml new file mode 100644 index 00000000000..cfb6b7509f2 --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/SWG - Abnormal Port to Protocol.yaml @@ -0,0 +1,54 @@ +id: f6a8d6a5-3e9f-47c8-a8d5-1b2b9d3b7d6a +name: Detect Protocol Changes for Destination Ports +description: | + Identifies changes in the protocol used for specific destination ports, comparing the current runtime with a learned baseline. This can indicate potential protocol misuse or configuration changes. + +Configurable Parameters: + - Learning period - the time range to establish the baseline. Default is set to 7 days. + - Run time - the time range for current analysis. Default is set to 1 day. +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1h +queryPeriod: 8d +triggerOperator: gt +triggerThreshold: 1 +tactics: + - DefenseEvasion + - Exfiltration + - CommandAndControl +relevantTechniques: [] +query: | + let LearningPeriod = 7d; + let RunTime = 1d; + let StartLearningPeriod = ago(LearningPeriod + RunTime); + let EndRunTime = ago(RunTime); + let LearningPortToProtocol = + NetworkAccessTraffic + | where TimeGenerated between (StartLearningPeriod .. EndRunTime) + | where isnotempty(DestinationPort) + | summarize LearningTimeCount = count() by LearningTimeDstPort = DestinationPort, LearningTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn; + let AlertTimePortToProtocol = + NetworkAccessTraffic + | where TimeGenerated between (EndRunTime .. now()) + | where isnotempty(DestinationPort) + | summarize AlertTimeCount = count() by AlertTimeDstPort = DestinationPort, AlertTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn; + AlertTimePortToProtocol + | join kind=leftouter (LearningPortToProtocol) on $left.AlertTimeDstPort == $right.LearningTimeDstPort and $left.SourceIp == $right.SourceIp and $left.DestinationFqdn == $right.DestinationFqdn + | where isnull(LearningTimeProtocol) or LearningTimeProtocol != AlertTimeProtocol + | project AlertTimeDstPort, AlertTimeProtocol, LearningTimeProtocol, SourceIp, DestinationFqdn + | extend IPCustomEntity = SourceIp, FqdnCustomEntity = DestinationFqdn +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IPCustomEntity + - entityType: URL + fieldMappings: + - identifier: Url + columnName: FqdnCustomEntity +version: 1.0.0 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Analytic Rules/SWG - Source IP Port Scan.yaml b/Solutions/Global Secure Access/Analytic Rules/SWG - Source IP Port Scan.yaml new file mode 100644 index 00000000000..1c2f4ebeb1e --- /dev/null +++ b/Solutions/Global Secure Access/Analytic Rules/SWG - Source IP Port Scan.yaml @@ -0,0 +1,41 @@ +id: 82cfa6b9-5f7e-4b8b-8b2f-a63f21b7a7d1 +name: Detect Source IP Scanning Multiple Open Ports +description: | + Identifies a source IP scanning multiple open ports on Global Secure Access Firewall. This can indicate malicious scanning of ports by an attacker, trying to reveal open ports in the organization that can be compromised for initial access. +Configurable Parameters: + - Port scan time - the time range to look for multiple ports scanned. Default is set to 30 seconds. + - Minimum different ports threshold - alert only if more than this number of ports scanned. Default is set to 100. +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 1d +triggerOperator: gt +triggerThreshold: 1 +tactics: + - Discovery +relevantTechniques: + - T1046 +query: | + let port_scan_time = 30s; + let min_ports_threshold = 100; + NetworkAccessTraffic + | where TimeGenerated > ago(1d) + | where Action == 'Allowed' + | summarize PortsScanned = dcount(DestinationPort) by SourceIp, bin(TimeGenerated, port_scan_time) + | where PortsScanned > min_ports_threshold + | project SourceIp, PortsScanned, TimeGenerated +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: SourceIp + - entityType: URL + fieldMappings: + - identifier: Url + columnName: Fqdn +version: 1.0.0 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Data/Solution_GlobalSecureAccess.json b/Solutions/Global Secure Access/Data/Solution_GlobalSecureAccess.json new file mode 100644 index 00000000000..4f92d581f03 --- /dev/null +++ b/Solutions/Global Secure Access/Data/Solution_GlobalSecureAccess.json @@ -0,0 +1,65 @@ +{ + "Name": "Global Secure Access", + "Author": "Microsoft - support@microsoft.com", + "Logo": "", + "Description": "[Global Secure Access](https://aka.ms/GlobalSecureAccess) is a [domain solution](https://learn.microsoft.com/en-us/azure/sentinel/sentinel-solutions-catalog#domain-solutions) and does not include any data connectors. The content in this solution requires one of the product solutions below.\n\n**Prerequisite:**\n\nInstall one or more of the listed solutions to unlock the value provided by this solution.\n1. Microsoft Entra ID \n\n**Underlying Microsoft Technologies used:**\n\nThis solution depends on the following technologies, and some of these dependencies may either be in Preview state or might result in additional ingestion or operational costs:\n1. Product solutions as described above\n", + "WorkbookBladeDescription": "This Microsoft Sentinel Solution installs workbooks. Workbooks provide a flexible canvas for data monitoring, analysis, and the creation of rich visual reports within the Azure portal. They allow you to tap into one or many data sources from Microsoft Sentinel and combine them into unified interactive experiences.", + "AnalyticalRuleBladeDescription": "This solution installs the following analytic rule templates. After installing the solution, create and enable analytic rules in the Manage solution view.", + "HuntingQueryBladeDescription": "This solution installs the following hunting queries. After installing the solution, run these hunting queries to hunt for threats in the Manage solution view.", + "Workbooks": [ + "Workbooks/GSAM365EnrichedEvents.json", + "Workbooks/GSANetworkTraffic.json" + ], + "Analytic Rules": [ + "Analytic Rules/Identity - AfterHoursActivity.yaml", + "Analytic Rules/Identity - SharedSessions.yaml", + "Analytic Rules/Office 365 - exchange_auditlogdisabled.yaml", + "Analytic Rules/Office 365 - External User added to Team and immediately uploads file.yaml", + "Analytic Rules/Office 365 - ExternalUserAddedRemovedInTeams.yaml", + "Analytic Rules/Office 365 - Mail_redirect_via_ExO_transport_rule.yaml", + "Analytic Rules/Office 365 - Malicious_Inbox_Rule.yaml", + "Analytic Rules/Office 365 - MultipleTeamsDeletes.yaml", + "Analytic Rules/Office 365 - Office_MailForwarding.yaml", + "Analytic Rules/Office 365 - office_policytampering.yaml", + "Analytic Rules/Office 365 - Office_Uploaded_Executables.yaml", + "Analytic Rules/Office 365 - RareOfficeOperations.yaml", + "Analytic Rules/Office 365 - SharePoint_Downloads_byNewIP.yaml", + "Analytic Rules/Office 365 - SharePoint_Downloads_byNewUserAgent.yaml", + "Analytic Rules/Office 365 - sharepoint_file_transfer_above_threshold.yaml", + "Analytic Rules/Office 365 - sharepoint_file_transfer_folders_above_threshold.yaml", + "Analytic Rules/SWG - Abnormal Deny Rate.yaml", + "Analytic Rules/SWG - Abnormal Port to Protocol.yaml", + "Analytic Rules/SWG - Source IP Port Scan.yaml" + ], + "Playbooks": [], + "Hunting Queries": [ + "Hunting Queries/AnomolousUserAccessingOtherUsersMailbox.yaml", + "Hunting Queries/ExternalUserAddedRemovedInTeams_HuntVersion.yaml", + "Hunting Queries/ExternalUserFromNewOrgAddedToTeams.yaml", + "Hunting Queries/Mail_redirect_via_ExO_transport_rule_hunting.yaml", + "Hunting Queries/MultiTeamBot.yaml", + "Hunting Queries/MultiTeamOwner.yaml", + "Hunting Queries/MultipleTeamsDeletes.yaml", + "Hunting Queries/NewBotAddedToTeams.yaml", + "Hunting Queries/New_WindowsReservedFileNamesOnOfficeFileServices.yaml", + "Hunting Queries/OfficeMailForwarding_hunting.yaml", + "Hunting Queries/TeamsFilesUploaded.yaml", + "Hunting Queries/UserAddToTeamsAndUploadsFile.yaml", + "Hunting Queries/WindowsReservedFileNamesOnOfficeFileServices.yaml", + "Hunting Queries/double_file_ext_exes.yaml", + "Hunting Queries/new_adminaccountactivity.yaml", + "Hunting Queries/new_sharepoint_downloads_by_IP.yaml", + "Hunting Queries/new_sharepoint_downloads_by_UserAgent.yaml", + "Hunting Queries/nonowner_MailboxLogin.yaml", + "Hunting Queries/powershell_or_nonbrowser_MailboxLogin.yaml", + "Hunting Queries/sharepoint_downloads.yaml", + "Hunting Queries/MultipleUsersEmailForwardedToSameDestination.yaml" + ], + "BasePath": "C:\\git\\Azure-Sentinel\\Azure-Sentinel\\Solutions\\Global Secure Access", + "Version": "3.0.0", + "Metadata": "SolutionMetadata.json", + "TemplateSpec": true, + "StaticDataConnectorIds": [ + "AzureActiveDirectory" + ] +} diff --git a/Solutions/Global Secure Access/Hunting Queries/AnomolousUserAccessingOtherUsersMailbox.yaml b/Solutions/Global Secure Access/Hunting Queries/AnomolousUserAccessingOtherUsersMailbox.yaml new file mode 100644 index 00000000000..5a5e54f3638 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/AnomolousUserAccessingOtherUsersMailbox.yaml @@ -0,0 +1,62 @@ +id: 271e8881-3044-4332-a5f4-42264c2e0315 +name: Anomalous access to other users' mailboxes +description: | + 'Looks for users accessing multiple other users' mailboxes or accessing multiple folders in another users mailbox.' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Collection +relevantTechniques: + - T1114.002 +tags: + - Solorigate + - NOBELIUM +query: | + let starttime = todatetime('{{StartTimeISO}}'); + let endtime = todatetime('{{EndTimeISO}}'); + let lookback = totimespan((endtime - starttime) * 2); + // Adjust this value to alter how many mailbox (other than their own) a user needs to access before being included in results + let user_threshold = 1; + // Adjust this value to alter how many mailbox folders in other's email accounts a users needs to access before being included in results. + let folder_threshold = 5; + // Exclude historical as known good (set lookback and timeframe to same value to skip this) + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (ago(lookback)..starttime) + | where Operation =~ "MailItemsAccessed" + | where ResultStatus =~ "Succeeded" + | extend MailboxOwnerUPN = tostring(parse_json(AdditionalProperties).MailboxOwnerUPN) + | where tolower(MailboxOwnerUPN) != tolower(UserId) + | join kind=rightanti ( + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (starttime..endtime) + | where Operation =~ "MailItemsAccessed" + | where ResultStatus =~ "Succeeded" + | extend MailboxOwnerUPN = tostring(parse_json(AdditionalProperties).MailboxOwnerUPN) + | where tolower(MailboxOwnerUPN) != tolower(UserId) + ) on MailboxOwnerUPN, UserId + | where isnotempty(tostring(parse_json(AdditionalProperties).Folders)) + | mv-expand Folders = parse_json(AdditionalProperties).Folders + | extend folders = tostring(Folders.Path) + | extend ClientIP = iif(ClientIp startswith "[", extract("\\[([^\\]]*)", 1, ClientIp), ClientIp) + | extend ClientInfoString = tostring(parse_json(AdditionalProperties).ClientInfoString) + | extend MailboxGuid = tostring(parse_json(AdditionalProperties).MailboxGuid) + | summarize StartTime = max(TimeGenerated), EndTime = min(TimeGenerated), set_folders = make_set(folders, 100000), set_ClientInfoString = make_set(ClientInfoString, 100000), set_ClientIP = make_set(ClientIP, 100000), set_MailboxGuid = make_set(MailboxGuid, 100000), set_MailboxOwnerUPN = make_set(MailboxOwnerUPN, 100000) by UserId + | extend folder_count = array_length(set_folders) + | extend user_count = array_length(set_MailboxGuid) + | where user_count > user_threshold or folder_count > folder_threshold + | extend Reason = case(user_count > user_threshold and folder_count > folder_threshold, "Both User and Folder Threshold Exceeded", folder_count > folder_threshold and user_count < user_threshold, "Folder Count Threshold Exceeded", "User Threshold Exceeded") + | sort by user_count desc + | project-reorder UserId, user_count, folder_count, set_MailboxOwnerUPN, set_ClientIP, set_ClientInfoString, set_folders + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) + | extend Account_0_Name = AccountName + | extend Account_0_UPNSuffix = AccountUPNSuffix +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/ExternalUserAddedRemovedInTeams_HuntVersion.yaml b/Solutions/Global Secure Access/Hunting Queries/ExternalUserAddedRemovedInTeams_HuntVersion.yaml new file mode 100644 index 00000000000..02f36b96d27 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/ExternalUserAddedRemovedInTeams_HuntVersion.yaml @@ -0,0 +1,42 @@ +id: 119d9e1c-afcc-4d23-b239-cdb4e7bf851c +name: External User Added and Removed in a Short Timeframe +description: | + This hunting query identifies external user accounts that are added to a Team and then removed within one hour. +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Persistence +relevantTechniques: + - T1136 +query: | + // If you want to look at user added further than 7 days ago adjust this value + // If you want to change the timeframe of how quickly accounts need to be added and removed change this value + let time_delta = 1h; + EnrichedMicrosoft365AuditLogs + | where Workload == "MicrosoftTeams" + | where Operation == "MemberAdded" + | extend UPN = tostring(parse_json(tostring(AdditionalProperties)).UPN) // Assuming UPN is stored in AdditionalProperties + | where UPN contains "#EXT#" + | project TimeAdded = TimeGenerated, Operation, UPN, UserWhoAdded = UserId, TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName), TeamGuid = tostring(parse_json(tostring(AdditionalProperties)).TeamGuid) + | join kind=innerunique ( + EnrichedMicrosoft365AuditLogs + | where Workload == "MicrosoftTeams" + | where Operation == "MemberRemoved" + | extend UPN = tostring(parse_json(tostring(AdditionalProperties)).UPN) // Assuming UPN is stored in AdditionalProperties + | where UPN contains "#EXT#" + | project TimeDeleted = TimeGenerated, Operation, UPN, UserWhoDeleted = UserId, TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName), TeamGuid = tostring(parse_json(tostring(AdditionalProperties)).TeamGuid) + ) on UPN, TeamGuid + | where TimeDeleted < (TimeAdded + time_delta) + | project TimeAdded, TimeDeleted, UPN, UserWhoAdded, UserWhoDeleted, TeamName, TeamGuid + | extend AccountName = tostring(split(UPN, "@")[0]), AccountUPNSuffix = tostring(split(UPN, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix +version: 2.0.1 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Hunting Queries/ExternalUserFromNewOrgAddedToTeams.yaml b/Solutions/Global Secure Access/Hunting Queries/ExternalUserFromNewOrgAddedToTeams.yaml new file mode 100644 index 00000000000..9cee2439f7c --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/ExternalUserFromNewOrgAddedToTeams.yaml @@ -0,0 +1,48 @@ +id: 6fce5baf-bfc2-4c56-a6b7-9c4733fc5a45 +name: External user from a new organisation added to Teams +description: | + 'This query identifies external users added to Teams where the user's domain is not one previously seen in Teams data.' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Persistence +relevantTechniques: + - T1136 +query: | + let starttime = todatetime('{{StartTimeISO}}'); + let endtime = todatetime('{{EndTimeISO}}'); + let lookback = totimespan((endtime - starttime) * 7); + let known_orgs = ( + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (ago(lookback) .. starttime) + | where Workload == "MicrosoftTeams" + | where Operation in ("MemberAdded", "TeamsSessionStarted") + // Extract the correct UPN and parse our external organization domain + | extend Members = parse_json(tostring(AdditionalProperties.Members)) + | extend UPN = iif(Operation == "MemberAdded", tostring(Members[0].UPN), UserId) + | extend Organization = tostring(split(split(UPN, "_")[1], "#")[0]) + | where isnotempty(Organization) + | summarize by Organization + ); + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (starttime .. endtime) + | where Workload == "MicrosoftTeams" + | where Operation == "MemberAdded" + | extend Members = parse_json(tostring(AdditionalProperties.Members)) + | extend UPN = tostring(Members[0].UPN) + | extend Organization = tostring(split(split(UPN, "_")[1], "#")[0]) + | where isnotempty(Organization) + | where Organization !in (known_orgs) + | extend AccountName = tostring(split(UPN, "@")[0]), AccountUPNSuffix = tostring(split(UPN, "@")[1]) + | extend Account_0_Name = AccountName + | extend Account_0_UPNSuffix = AccountUPNSuffix +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/Mail_redirect_via_ExO_transport_rule_hunting.yaml b/Solutions/Global Secure Access/Hunting Queries/Mail_redirect_via_ExO_transport_rule_hunting.yaml new file mode 100644 index 00000000000..94de9dade23 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/Mail_redirect_via_ExO_transport_rule_hunting.yaml @@ -0,0 +1,42 @@ +id: 9891684a-1e3a-4546-9403-3439513cbc70 +name: Mail Redirect via ExO Transport Rule +description: | + Identifies when Exchange Online transport rule is configured to forward emails. + This could be an adversary mailbox configured to collect mail from multiple user accounts. +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Collection + - Exfiltration +relevantTechniques: + - T1114 + - T1020 +query: | + EnrichedMicrosoft365AuditLogs + | where Workload == "Exchange" + | where Operation in ("New-TransportRule", "Set-TransportRule") + | mv-apply DynamicParameters = todynamic(AdditionalProperties.Parameters) on (summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))) + | extend RuleName = case( + Operation == "Set-TransportRule", ObjectId, + Operation == "New-TransportRule", ParsedParameters.Name, + "Unknown") + | mv-expand ExpandedParameters = todynamic(AdditionalProperties.Parameters) + | where ExpandedParameters.Name in ("BlindCopyTo", "RedirectMessageTo") and isnotempty(ExpandedParameters.Value) + | extend RedirectTo = ExpandedParameters.Value + | extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P\d+))?', dynamic(["IPAddress", "Port"]), ClientIp)[0] + | project TimeGenerated, RedirectTo, IPAddress = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1]), UserId, Operation, RuleName, AdditionalProperties + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IPAddress +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/MultiTeamBot.yaml b/Solutions/Global Secure Access/Hunting Queries/MultiTeamBot.yaml new file mode 100644 index 00000000000..b150a46ba9c --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/MultiTeamBot.yaml @@ -0,0 +1,39 @@ +id: 9eb64924-ec8d-44d0-b1f2-10665150fb74 +name: Bots added to multiple teams +description: | + 'This hunting query helps identify bots added to multiple Teams in a short space of time.' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Persistence + - Collection +relevantTechniques: + - T1176 + - T1119 +query: | + // Adjust these thresholds to suit your environment. + let threshold = 2; + let time_threshold = timespan(5m); + EnrichedMicrosoft365AuditLogs + | where Workload == "MicrosoftTeams" + | where Operation == "BotAddedToTeam" + | extend TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName) + | summarize Start = max(TimeGenerated), End = min(TimeGenerated), Teams = make_set(TeamName, 10000) by UserId + | extend CountOfTeams = array_length(Teams) + | extend TimeDelta = End - Start + | where CountOfTeams > threshold + | where TimeDelta <= time_threshold + | project Start, End, Teams, CountOfTeams, UserId + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) + | extend Account_0_Name = AccountName + | extend Account_0_UPNSuffix = AccountUPNSuffix +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/MultiTeamOwner.yaml b/Solutions/Global Secure Access/Hunting Queries/MultiTeamOwner.yaml new file mode 100644 index 00000000000..637e316c514 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/MultiTeamOwner.yaml @@ -0,0 +1,44 @@ +id: 558f15dd-3171-4b11-bf24-31c0610a20e0 +name: User made Owner of multiple teams +description: | + This hunting query identifies users who have been made Owner of multiple Teams. +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - PrivilegeEscalation +relevantTechniques: + - T1078 +query: | + // Adjust this value to change how many teams a user is made owner of before detecting + let max_owner_count = 3; + // Identify users who have been made owner of multiple Teams + let high_owner_count = ( + EnrichedMicrosoft365AuditLogs + | where Workload == "MicrosoftTeams" + | where Operation == "MemberRoleChanged" + | extend Member = tostring(UserId) + | extend NewRole = toint(parse_json(tostring(AdditionalProperties)).Role) + | where NewRole == 2 + | summarize TeamCount = dcount(ObjectId) by Member + | where TeamCount > max_owner_count + | project Member + ); + EnrichedMicrosoft365AuditLogs + | where Workload == "MicrosoftTeams" + | where Operation == "MemberRoleChanged" + | extend Member = tostring(UserId) + | extend NewRole = toint(parse_json(tostring(AdditionalProperties)).Role) + | where NewRole == 2 + | where Member in (high_owner_count) + | extend AccountName = tostring(split(Member, "@")[0]), AccountUPNSuffix = tostring(split(Member, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix +version: 2.0.1 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Hunting Queries/MultipleTeamsDeletes.yaml b/Solutions/Global Secure Access/Hunting Queries/MultipleTeamsDeletes.yaml new file mode 100644 index 00000000000..edc5adc2882 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/MultipleTeamsDeletes.yaml @@ -0,0 +1,39 @@ +id: 64990414-b015-4edf-bef0-343b741e68c5 +name: Multiple Teams deleted by a single user +description: | + 'This hunting query identifies where multiple Teams have been deleted by a single user in a short timeframe.' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Impact +relevantTechniques: + - T1485 + - T1489 +query: | + // Adjust this value to change how many Teams should be deleted before including + let max_delete = 3; + let deleting_users = ( + EnrichedMicrosoft365AuditLogs + | where Workload == "MicrosoftTeams" + | where Operation == "TeamDeleted" + | summarize count_ = count() by UserId + | where count_ > max_delete + | project UserId + ); + EnrichedMicrosoft365AuditLogs + | where Workload == "MicrosoftTeams" + | where Operation == "TeamDeleted" + | where UserId in (deleting_users) + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) + | extend Account_0_Name = AccountName + | extend Account_0_UPNSuffix = AccountUPNSuffix +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/MultipleUsersEmailForwardedToSameDestination.yaml b/Solutions/Global Secure Access/Hunting Queries/MultipleUsersEmailForwardedToSameDestination.yaml new file mode 100644 index 00000000000..12d9310574d --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/MultipleUsersEmailForwardedToSameDestination.yaml @@ -0,0 +1,56 @@ +id: a1551ae4-f61c-4bca-9c57-4d0d681db2e9 +name: Multiple Users Email Forwarded to Same Destination +description: | + Identifies when multiple (more than one) users' mailboxes are configured to forward to the same destination. + This could be an attacker-controlled destination mailbox configured to collect mail from multiple compromised user accounts. +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +queryFrequency: 1d +queryPeriod: 7d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - Collection + - Exfiltration +relevantTechniques: + - T1114 + - T1020 +query: | + let queryfrequency = 1d; + let queryperiod = 7d; + EnrichedMicrosoft365AuditLogs + | where TimeGenerated > ago(queryperiod) + | where Workload == "Exchange" + | where AdditionalProperties has_any ("ForwardTo", "RedirectTo", "ForwardingSmtpAddress") + | mv-apply DynamicParameters = todynamic(AdditionalProperties) on (summarize ParsedParameters = make_bag(bag_pack(tostring(DynamicParameters.Name), DynamicParameters.Value))) + | evaluate bag_unpack(ParsedParameters, columnsConflict='replace_source') + | extend DestinationMailAddress = tolower(case( + isnotempty(column_ifexists("ForwardTo", "")), column_ifexists("ForwardTo", ""), + isnotempty(column_ifexists("RedirectTo", "")), column_ifexists("RedirectTo", ""), + isnotempty(column_ifexists("ForwardingSmtpAddress", "")), trim_start(@"smtp:", column_ifexists("ForwardingSmtpAddress", "")), + "")) + | where isnotempty(DestinationMailAddress) + | mv-expand split(DestinationMailAddress, ";") + | extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P\d+))?', dynamic(["IPAddress", "Port"]), ClientIp)[0] + | extend ClientIp = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1]) + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), DistinctUserCount = dcount(UserId), UserId = make_set(UserId, 250), Ports = make_set(Port, 250), EventCount = count() by tostring(DestinationMailAddress), ClientIp + | where DistinctUserCount > 1 and EndTime > ago(queryfrequency) + | mv-expand UserId to typeof(string) + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIp +version: 2.0.1 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Hunting Queries/NewBotAddedToTeams.yaml b/Solutions/Global Secure Access/Hunting Queries/NewBotAddedToTeams.yaml new file mode 100644 index 00000000000..7e25db9f105 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/NewBotAddedToTeams.yaml @@ -0,0 +1,39 @@ +id: bf76e508-9282-4cf1-9cc1-5c20c3dea2ee +name: Previously Unseen Bot or Application Added to Teams +description: | + This hunting query helps identify new, and potentially unapproved applications or bots being added to Teams. +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Persistence + - Collection +relevantTechniques: + - T1176 + - T1119 +query: | + let starttime = todatetime('{{StartTimeISO}}'); + let endtime = todatetime('{{EndTimeISO}}'); + let lookback = starttime - 14d; + let historical_bots = + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (lookback .. starttime) + | where Workload == "MicrosoftTeams" + | extend AddonName = tostring(parse_json(tostring(AdditionalProperties)).AddonName) + | where isnotempty(AddonName) + | distinct AddonName; + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (starttime .. endtime) + | where Workload == "MicrosoftTeams" + | extend AddonName = tostring(parse_json(tostring(AdditionalProperties)).AddonName) + | where AddonName !in (historical_bots) + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/New_WindowsReservedFileNamesOnOfficeFileServices.yaml b/Solutions/Global Secure Access/Hunting Queries/New_WindowsReservedFileNamesOnOfficeFileServices.yaml new file mode 100644 index 00000000000..19250a69449 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/New_WindowsReservedFileNamesOnOfficeFileServices.yaml @@ -0,0 +1,74 @@ +id: 641ecd2d-27c9-4f05-8433-8205096b09fc +name: New Windows Reserved Filenames staged on Office file services +description: | + 'This identifies new Windows Reserved Filenames on Office services like SharePoint and OneDrive in the past 7 days. It also detects when a user uploads these files to another user's workspace, which may indicate malicious activity.' +description-detailed: | + 'Identifies when new Windows Reserved Filenames show up on Office services such as SharePoint and OneDrive in relation to the previous 7 days. + List currently includes ''CON'', ''PRN'', ''AUX'', ''NUL'', ''COM1'', ''COM2'', ''COM3'', ''COM4'', ''COM5'', ''COM6'', + ''COM7'', ''COM8'', ''COM9'', ''LPT1'', ''LPT2'', ''LPT3'', ''LPT4'', ''LPT5'', ''LPT6'', ''LPT7'', ''LPT8'', ''LPT9'' file extensions. + Additionally, identifies when a given user is uploading these files to another users workspace. + This may be indication of a staging location for malware or other malicious activity. + References: https://docs.microsoft.com/windows/win32/fileio/naming-a-file' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - CommandAndControl +relevantTechniques: + - T1105 +query: | + let starttime = todatetime('{{StartTimeISO}}'); + let endtime = todatetime('{{EndTimeISO}}'); + let lookback = totimespan((endtime - starttime) * 7); + let Reserved = dynamic(['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9']); + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (starttime .. endtime) + | extend FileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName) + | extend ClientUserAgent = tostring(parse_json(tostring(AdditionalProperties)).ClientUserAgent) + | extend SiteUrl = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl) + | where isnotempty(ObjectId) + | where ObjectId !~ FileName + | where ObjectId in (Reserved) or FileName in (Reserved) + | where ClientUserAgent !has "Mac OS" + | project TimeGenerated, Id, Workload, RecordType, Operation, UserType, UserKey, UserId, ClientIp, ClientUserAgent, SiteUrl, ObjectId, FileName + | join kind=leftanti ( + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (ago(lookback) .. starttime) + | extend FileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName) + | extend ClientUserAgent = tostring(parse_json(tostring(AdditionalProperties)).ClientUserAgent) + | extend SiteUrl = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl) + | where isnotempty(ObjectId) + | where ObjectId !~ FileName + | where ObjectId in (Reserved) or FileName in (Reserved) + | where ClientUserAgent !has "Mac OS" + | summarize PrevSeenCount = count() by ObjectId, UserId, FileName + ) on ObjectId + | extend SiteUrlUserFolder = tolower(split(SiteUrl, '/')[-2]) + | extend UserIdUserFolderFormat = tolower(replace_regex(UserId, '@|\\.', '_')) + | extend UserIdDiffThanUserFolder = iff(SiteUrl has '/personal/' and SiteUrlUserFolder != UserIdUserFolderFormat, true, false) + | summarize TimeGenerated = make_list(TimeGenerated, 100000), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Operations = make_list(Operation, 100000), UserAgents = make_list(ClientUserAgent, 100000), + Ids = make_list(Id, 100000), SourceRelativeUrls = make_list(ObjectId, 100000), FileNames = make_list(FileName, 100000) + by Workload, RecordType, UserType, UserKey, UserId, ClientIp, SiteUrl, ObjectId, SiteUrlUserFolder, UserIdUserFolderFormat, UserIdDiffThanUserFolder + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) + | extend IP_0_Address = ClientIp + | extend Account_0_Name = AccountName + | extend Account_0_UPNSuffix = AccountUPNSuffix + | extend URL_0_Url = SiteUrl +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IP_0_Address + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: URL + fieldMappings: + - identifier: Url + columnName: URL_0_Url +version: 2.0.1 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Hunting Queries/OfficeMailForwarding_hunting.yaml b/Solutions/Global Secure Access/Hunting Queries/OfficeMailForwarding_hunting.yaml new file mode 100644 index 00000000000..77344589187 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/OfficeMailForwarding_hunting.yaml @@ -0,0 +1,83 @@ +id: d49fc965-aef3-49f6-89ad-10cc4697eb5b +name: Office Mail Forwarding - Hunting Version +description: | + Adversaries often abuse email-forwarding rules to monitor victim activities, steal information, and gain intelligence on the victim or their organization. This query highlights cases where user mail is being forwarded, including to external domains. +description-detailed: | + Adversaries often abuse email-forwarding rules to monitor activities of a victim, steal information and further gain intelligence on + victim or victim's organization. This query over Office Activity data highlights cases where user mail is being forwarded and shows if + it is being forwarded to external domains as well. +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Collection + - Exfiltration +relevantTechniques: + - T1114 + - T1020 +query: | + EnrichedMicrosoft365AuditLogs + | where Workload == "Exchange" + | where (Operation == "Set-Mailbox" and tostring(parse_json(tostring(AdditionalProperties))) contains 'ForwardingSmtpAddress') + or (Operation in ('New-InboxRule', 'Set-InboxRule') and (tostring(parse_json(tostring(AdditionalProperties))) contains 'ForwardTo' or tostring(parse_json(tostring(AdditionalProperties))) contains 'RedirectTo')) + | extend parsed = parse_json(tostring(AdditionalProperties)) + | extend fwdingDestination_initial = iif(Operation == "Set-Mailbox", tostring(parsed.ForwardingSmtpAddress), coalesce(tostring(parsed.ForwardTo), tostring(parsed.RedirectTo))) + | where isnotempty(fwdingDestination_initial) + | extend fwdingDestination = iff(fwdingDestination_initial has "smtp", (split(fwdingDestination_initial, ":")[1]), fwdingDestination_initial) + | parse fwdingDestination with * '@' ForwardedtoDomain + | parse UserId with *'@' UserDomain + | extend subDomain = ((split(strcat(tostring(split(UserDomain, '.')[-2]), '.', tostring(split(UserDomain, '.')[-1])), '.'))[0]) + | where ForwardedtoDomain !contains subDomain + | extend Result = iff(ForwardedtoDomain != UserDomain, "Mailbox rule created to forward to External Domain", "Forward rule for Internal domain") + | extend ClientIPAddress = case(ClientIp has ".", tostring(split(ClientIp, ":")[0]), ClientIp has "[", tostring(trim_start(@'[[]', tostring(split(ClientIp, "]")[0]))), ClientIp) + | extend Port = case( + ClientIp has ".", + (split(ClientIp, ":")[1]), + ClientIp has "[", + tostring(split(ClientIp, "]:")[1]), + ClientIp + ) + | project + TimeGenerated, + UserId, + UserDomain, + subDomain, + Operation, + ForwardedtoDomain, + ClientIPAddress, + Result, + Port, + ObjectId, + fwdingDestination, + AdditionalProperties + | extend + AccountName = tostring(split(UserId, "@")[0]), + AccountUPNSuffix = tostring(split(UserId, "@")[1]) + | extend Host = tostring(parse_json(tostring(AdditionalProperties)).OriginatingServer) + | extend HostName = tostring(split(Host, ".")[0]) + | extend DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.')) + | extend Account_0_Name = AccountName + | extend Account_0_UPNSuffix = AccountUPNSuffix + | extend IP_0_Address = ClientIPAddress + | extend Host_0_HostName = HostName + | extend Host_0_DnsDomain = DnsDomain +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIPAddress + - entityType: Host + fieldMappings: + - identifier: HostName + columnName: Host_0_HostName + - identifier: DnsDomain + columnName: Host_0_DnsDomain +version: 2.0.1 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Hunting Queries/TeamsFilesUploaded.yaml b/Solutions/Global Secure Access/Hunting Queries/TeamsFilesUploaded.yaml new file mode 100644 index 00000000000..a61d6f2fa9a --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/TeamsFilesUploaded.yaml @@ -0,0 +1,47 @@ +id: 90e198a9-efb6-4719-ad89-81b8e93633a7 +name: Files uploaded to teams and access summary +description: | + 'This hunting query identifies files uploaded to SharePoint via a Teams chat and + summarizes users and IP addresses that have accessed these files. This allows for + identification of anomalous file sharing patterns.' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - InitialAccess + - Exfiltration +relevantTechniques: + - T1199 + - T1102 + - T1078 +query: | + EnrichedMicrosoft365AuditLogs + | where RecordType == "SharePointFileOperation" + | where Operation == "FileUploaded" + | where UserId != "app@sharepoint" + | where ObjectId has "Microsoft Teams Chat Files" + | extend SourceFileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName) + | join kind=leftouter ( + EnrichedMicrosoft365AuditLogs + | where RecordType == "SharePointFileOperation" + | where Operation == "FileDownloaded" or Operation == "FileAccessed" + | where UserId != "app@sharepoint" + | where ObjectId has "Microsoft Teams Chat Files" + | extend UserId1 = UserId, ClientIp1 = ClientIp + ) on ObjectId + | extend userBag = bag_pack("UserId1", UserId1, "ClientIp1", ClientIp1) + | summarize AccessedBy = make_bag(userBag), make_set(UserId1, 10000) by bin(TimeGenerated, 1h), UserId, ObjectId, SourceFileName + | extend NumberOfUsersAccessed = array_length(bag_keys(AccessedBy)) + | project timestamp = TimeGenerated, UserId, FileLocation = ObjectId, FileName = SourceFileName, AccessedBy, NumberOfUsersAccessed + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) + | extend Account_0_Name = AccountName + | extend Account_0_UPNSuffix = AccountUPNSuffix +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/UserAddToTeamsAndUploadsFile.yaml b/Solutions/Global Secure Access/Hunting Queries/UserAddToTeamsAndUploadsFile.yaml new file mode 100644 index 00000000000..f1ba1da23c2 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/UserAddToTeamsAndUploadsFile.yaml @@ -0,0 +1,37 @@ +id: 3d6d0c04-7337-40cf-ace6-c471d442356d +name: User added to Teams and immediately uploads file +description: | + 'This hunting query identifies users who are added to a Teams Channel or Teams chat + and within 1 minute of being added upload a file via the chat. This might be + an indicator of suspicious activity.' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - InitialAccess +relevantTechniques: + - T1566 +query: | + let threshold = 1m; + let MemberAddedEvents = EnrichedMicrosoft365AuditLogs + | where Workload == "MicrosoftTeams" + | where Operation == "MemberAdded" + | extend TeamName = tostring(parse_json(AdditionalProperties).TeamName) + | project TimeGenerated, UploaderID = UserId, TeamName; + let FileUploadEvents = EnrichedMicrosoft365AuditLogs + | where RecordType == "SharePointFileOperation" + | where ObjectId has "Microsoft Teams Chat Files" + | where Operation == "FileUploaded" + | extend SourceFileName = tostring(parse_json(AdditionalProperties).SourceFileName) + | project UploadTime = TimeGenerated, UploaderID = UserId, FileLocation = ObjectId, SourceFileName; + MemberAddedEvents + | join kind=inner (FileUploadEvents) on UploaderID + | where UploadTime > TimeGenerated and UploadTime < TimeGenerated + threshold + | extend timestamp = TimeGenerated, AccountCustomEntity = UploaderID +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountCustomEntity +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/WindowsReservedFileNamesOnOfficeFileServices.yaml b/Solutions/Global Secure Access/Hunting Queries/WindowsReservedFileNamesOnOfficeFileServices.yaml new file mode 100644 index 00000000000..226886457a7 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/WindowsReservedFileNamesOnOfficeFileServices.yaml @@ -0,0 +1,57 @@ +id: 61c28cd7-3139-4731-8ea7-2cbbeabb4684 +name: Windows Reserved Filenames Staged on Office File Services +description: | + 'This identifies Windows Reserved Filenames on Office services like SharePoint and OneDrive. It also detects when a user uploads these files to another user's workspace, which may indicate malicious activity.' +description-detailed: | + 'Identifies when Windows Reserved Filenames show up on Office services such as SharePoint and OneDrive. + List currently includes CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, + COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9 file extensions. + Additionally, identifies when a given user is uploading these files to another user's workspace. + This may be an indication of a staging location for malware or other malicious activity. + References: https://docs.microsoft.com/windows/win32/fileio/naming-a-file' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - CommandAndControl +relevantTechniques: + - T1105 +query: | + // Reserved FileNames/Extension for Windows + let Reserved = dynamic(['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9']); + EnrichedMicrosoft365AuditLogs + | extend SourceFileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName) + | extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent) + | extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl) + | where isnotempty(ObjectId) + | where ObjectId in (Reserved) or SourceFileName in (Reserved) + | where UserAgent !has "Mac OS" + | extend SiteUrlUserFolder = tolower(split(Site_Url, '/')[-2]) + | extend UserIdUserFolderFormat = tolower(replace_regex(UserId, '@|\\.', '_')) + // identify when UserId is not a match to the specific site url personal folder reference + | extend UserIdDiffThanUserFolder = iff(Site_Url has '/personal/' and SiteUrlUserFolder != UserIdUserFolderFormat, true, false) + | summarize TimeGenerated = make_list(TimeGenerated, 100000), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Operations = make_list(Operation, 100000), UserAgents = make_list(UserAgent, 100000), ObjectIds = make_list(Id, 100000), SourceRelativeUrls = make_list(ObjectId, 100000), FileNames = make_list(SourceFileName, 100000) + by Workload, RecordType, UserType, UserKey, UserId, ClientIp, Site_Url, ObjectId, SiteUrlUserFolder, UserIdUserFolderFormat, UserIdDiffThanUserFolder + // Use mvexpand on any list items and you can expand out the exact time and other metadata about the hit + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) + | extend IP_0_Address = ClientIp + | extend Account_0_Name = AccountName + | extend Account_0_UPNSuffix = AccountUPNSuffix + | extend URL_0_Url = Site_Url +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: ClientIp + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: URL + fieldMappings: + - identifier: Url + columnName: Site_Url +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/double_file_ext_exes.yaml b/Solutions/Global Secure Access/Hunting Queries/double_file_ext_exes.yaml new file mode 100644 index 00000000000..df44ad7cdb8 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/double_file_ext_exes.yaml @@ -0,0 +1,52 @@ +id: d12580c2-1474-4125-a8a3-553f50d91215 +name: Exes with double file extension and access summary +description: | + 'Provides a summary of executable files with double file extensions in SharePoint + and the users and IP addresses that have accessed them.' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - DefenseEvasion +relevantTechniques: + - T1036 +query: | + let known_ext = dynamic(["lnk", "log", "option", "config", "manifest", "partial"]); + let excluded_users = dynamic(["app@sharepoint"]); + EnrichedMicrosoft365AuditLogs + | where RecordType == "SharePointFileOperation" and isnotempty(ObjectId) + | where ObjectId has ".exe." + and not(ObjectId endswith ".lnk") + and not(ObjectId endswith ".log") + and not(ObjectId endswith ".option") + and not(ObjectId endswith ".config") + and not(ObjectId endswith ".manifest") + and not(ObjectId endswith ".partial") + | extend Extension = extract("[^.]*\\.[^.]*$", 0, ObjectId) + | extend SourceFileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName) + | join kind=leftouter ( + EnrichedMicrosoft365AuditLogs + | where RecordType == "SharePointFileOperation" and (Operation == "FileDownloaded" or Operation == "FileAccessed") + | where not(ObjectId endswith ".lnk") + and not(ObjectId endswith ".log") + and not(ObjectId endswith ".option") + and not(ObjectId endswith ".config") + and not(ObjectId endswith ".manifest") + and not(ObjectId endswith ".partial") + ) on ObjectId + | where UserId1 !in (excluded_users) + | extend userBag = bag_pack("UserId", UserId1, "ClientIp", ClientIp1) + | summarize make_set(UserId1, 10000), userBag = make_bag(userBag), UploadTime = max(TimeGenerated) by UserId, ObjectId, SourceFileName, Extension + | extend NumberOfUsers = array_length(bag_keys(userBag)) + | project UploadTime, Uploader = UserId, FileLocation = ObjectId, FileName = SourceFileName, AccessedBy = userBag, Extension, NumberOfUsers + | extend UploaderName = tostring(split(Uploader, "@")[0]), UploaderUPNSuffix = tostring(split(Uploader, "@")[1]) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: UploaderName + - identifier: UPNSuffix + columnName: UploaderUPNSuffix +version: 2.0.1 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Hunting Queries/new_adminaccountactivity.yaml b/Solutions/Global Secure Access/Hunting Queries/new_adminaccountactivity.yaml new file mode 100644 index 00000000000..99a142e7dd6 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/new_adminaccountactivity.yaml @@ -0,0 +1,53 @@ +id: 723c5f46-133f-4f1e-ada6-5c138f811d75 +name: New Admin Account Activity Seen Which Was Not Seen Historically +description: | + This will help you discover any new admin account activity which was seen and were not seen historically. + Any new accounts seen in the results can be validated and investigated for any suspicious activities. +severity: Medium +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - PrivilegeEscalation + - Collection +relevantTechniques: + - T1078 + - T1114 +query: | + let starttime = todatetime('{{StartTimeISO}}'); + let endtime = todatetime('{{EndTimeISO}}'); + let lookback = starttime - 14d; + let historicalActivity = + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (lookback .. starttime) + | where RecordType == "ExchangeAdmin" and UserType in ("Admin", "DcAdmin") + | summarize historicalCount = count() by UserId; + let recentActivity = EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (starttime .. endtime) + | where UserType in ("Admin", "DcAdmin") + | summarize recentCount = count() by UserId; + recentActivity + | join kind=leftanti (historicalActivity) on UserId + | project UserId, recentCount + | join kind=rightsemi ( + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (starttime .. endtime) + | where RecordType == "ExchangeAdmin" + | where UserType in ("Admin", "DcAdmin") + ) on UserId + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by RecordType, Operation, UserType, UserId, ResultStatus + | extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId) + | extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '') + | extend AccountName = iff(UserId contains '\\', tostring(split(UserId, '\\')[1]), AccountName) + | extend AccountNTDomain = iff(UserId contains '\\', tostring(split(UserId, '\\')[0]), '') +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - identifier: NTDomain + columnName: AccountNTDomain +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/new_sharepoint_downloads_by_IP.yaml b/Solutions/Global Secure Access/Hunting Queries/new_sharepoint_downloads_by_IP.yaml new file mode 100644 index 00000000000..1ef34600f99 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/new_sharepoint_downloads_by_IP.yaml @@ -0,0 +1,50 @@ +id: e3d24cfd-b2a1-4ba7-8f80-0360892f9d57 +name: SharePointFileOperation via previously unseen IPs +description: | + 'Shows SharePoint upload/download volume by IPs with high-risk ASNs. New IPs with volume spikes may be unauthorized and exfiltrating documents.' +description-detailed: | + 'Shows volume of documents uploaded to or downloaded from SharePoint by IPs with ASNs associated with high user lockout or malicious activity. + In stable environments such connections by new IPs may be unauthorized, especially if associated with + spikes in volume which could be associated with large-scale document exfiltration.' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - SigninLogs + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Exfiltration +relevantTechniques: + - T1030 +query: | + let starttime = todatetime('{{StartTimeISO}}'); + let endtime = todatetime('{{EndTimeISO}}'); + let lookback = starttime - 14d; + let BLOCK_THRESHOLD = 1.0; + let HighBlockRateASNs = + SigninLogs + | where TimeGenerated > lookback + | where isnotempty(AutonomousSystemNumber) + | summarize make_set(IPAddress), TotalIps = dcount(IPAddress), BlockedSignins = countif(ResultType == "50053"), TotalSignins = count() by AutonomousSystemNumber + | extend BlockRatio = 1.00 * BlockedSignins / TotalSignins + | where BlockRatio >= BLOCK_THRESHOLD + | distinct AutonomousSystemNumber; + let ASNIPs = + SigninLogs + | where TimeGenerated > lookback + | where AutonomousSystemNumber in (HighBlockRateASNs) + | distinct IPAddress, AutonomousSystemNumber; + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (starttime .. endtime) + | where RecordType == "SharePointFileOperation" + | where Operation in ("FileDownloaded", "FileUploaded") + | where ClientIp in (ASNIPs) + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), RecentFileActivities = count() by ClientIp + | extend IP_0_Address = ClientIp +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IP_0_Address +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/new_sharepoint_downloads_by_UserAgent.yaml b/Solutions/Global Secure Access/Hunting Queries/new_sharepoint_downloads_by_UserAgent.yaml new file mode 100644 index 00000000000..be10886060c --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/new_sharepoint_downloads_by_UserAgent.yaml @@ -0,0 +1,61 @@ +id: f2367171-1514-4c67-88ef-27434b6a1093 +name: SharePointFileOperation via devices with previously unseen user agents +description: | + 'Tracking via user agent is one way to differentiate between types of connecting device. + In homogeneous enterprise environments the user agent associated with an attacker device may stand out as unusual.' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - SigninLogs + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Exfiltration +relevantTechniques: + - T1030 +query: | + let starttime = todatetime('{{StartTimeISO}}'); + let endtime = todatetime('{{EndTimeISO}}'); + let lookback = starttime - 14d; + let MINIMUM_BLOCKS = 10; + let SUCCESS_THRESHOLD = 0.2; + let HistoricalActivity = + SigninLogs + | where TimeGenerated > lookback + | where isnotempty(ClientAppUsed) + | summarize SuccessfulSignins = countif(ResultType == "0"), BlockedSignins = countif(ResultType == "50053") by ClientAppUsed + | extend SuccessBlockRatio = 1.00 * SuccessfulSignins / BlockedSignins + | where SuccessBlockRatio < SUCCESS_THRESHOLD + | where BlockedSignins > MINIMUM_BLOCKS; + EnrichedMicrosoft365AuditLogs + | where TimeGenerated between (starttime .. endtime) + | where RecordType == "SharePointFileOperation" + | where Operation in ("FileDownloaded", "FileUploaded") + | extend ClientAppUsed = tostring(parse_json(AdditionalProperties).UserAgent) + | extend SiteUrl = tostring(parse_json(AdditionalProperties).SiteUrl) + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), RecentFileActivities = count() by ClientAppUsed, UserId, ClientIp, SiteUrl + | join kind=innerunique (HistoricalActivity) on ClientAppUsed + | project-away ClientAppUsed1 + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) + | extend IP_0_Address = ClientIp + | extend Account_0_Name = AccountName + | extend Account_0_UPNSuffix = AccountUPNSuffix + | extend URL_0_Url = SiteUrl +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IP_0_Address + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: URL + fieldMappings: + - identifier: Url + columnName: URL_0_Url +version: 2.0.1 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Hunting Queries/nonowner_MailboxLogin.yaml b/Solutions/Global Secure Access/Hunting Queries/nonowner_MailboxLogin.yaml new file mode 100644 index 00000000000..43497e35311 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/nonowner_MailboxLogin.yaml @@ -0,0 +1,47 @@ +id: 0a8f410d-38b5-4d75-90da-32b472b97230 +name: Non-owner mailbox login activity +description: | + 'Finds non-owner mailbox access by admin/delegate permissions. Whitelist valid users and check others for unauthorized access.' +description-detailed: | + 'This will help you determine if mailbox access observed with Admin/Delegate Logontype. + The logon type indicates mailbox accessed from non-owner user. Exchange allows Admin + and delegate permissions to access other user's inbox. + If your organization has valid admin, delegate access given to users, you can whitelist those and investigate other results. + References: https://docs.microsoft.com/office/office-365-management-api/office-365-management-activity-api-schema#logontype' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Collection + - Exfiltration +relevantTechniques: + - T1114 + - T1020 +tags: + - Solorigate + - NOBELIUM +query: | + EnrichedMicrosoft365AuditLogs + | where Workload == "Exchange" + | where Operation == "MailboxLogin" + | extend Logon_Type = tostring(parse_json(tostring(AdditionalProperties)).LogonType) + | extend MailboxOwnerUPN = tostring(parse_json(tostring(AdditionalProperties)).MailboxOwnerUPN) + | where Logon_Type != "Owner" + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by Operation, UserType, UserId, MailboxOwnerUPN, Logon_Type, ClientIp + | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1]) + | extend IP_0_Address = ClientIp + | extend Account_0_Name = AccountName + | extend Account_0_UPNSuffix = AccountUPNSuffix +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IP_0_Address +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Hunting Queries/powershell_or_nonbrowser_MailboxLogin.yaml b/Solutions/Global Secure Access/Hunting Queries/powershell_or_nonbrowser_MailboxLogin.yaml new file mode 100644 index 00000000000..a04c13e8d02 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/powershell_or_nonbrowser_MailboxLogin.yaml @@ -0,0 +1,46 @@ +id: 49a4f65a-fe18-408e-afec-042fde93d3ce +name: PowerShell or non-browser mailbox login activity +description: | + 'Detects mailbox login from Exchange PowerShell. All accounts can use it by default, but admins can change it. Whitelist benign activities.' +description-detailed: | + 'This will help you determine if mailbox login was done from Exchange PowerShell session. + By default, all accounts you create in Office 365 are allowed to use Exchange Online PowerShell. + Administrators can use Exchange Online PowerShell to enable or disable a user's ability to connect to Exchange Online PowerShell. + Whitelist any benign scheduled activities using exchange PowerShell if applicable in your environment. + References: https://docs.microsoft.com/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/connect-to-exchange-online-powershell?view=exchange-ps' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Execution + - Persistence + - Collection +relevantTechniques: + - T1059 + - T1098 + - T1114 +query: | + EnrichedMicrosoft365AuditLogs + | where Workload == "Exchange" and Operation == "MailboxLogin" + | extend ClientApplication = tostring(parse_json(AdditionalProperties).ClientInfoString) + | where ClientApplication == "Client=Microsoft.Exchange.Powershell; Microsoft WinRM Client" + | extend TenantName = tostring(parse_json(AdditionalProperties).TenantName) + | extend MailboxOwner = tostring(parse_json(AdditionalProperties).MailboxOwnerUPN) + | extend LogonType = tostring(parse_json(AdditionalProperties).LogonType) + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by Operation, TenantName, UserType, UserId, MailboxOwner, LogonType, ClientApplication + | extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId) + | extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '') + | extend AccountName = iff(UserId contains '\\', tostring(split(UserId, '\\')[1]), AccountName) + | extend AccountNTDomain = iff(UserId contains '\\', tostring(split(UserId, '\\')[0]), '') +entityMappings: + - entityType: Account + fieldMappings: + - identifier: Name + columnName: AccountName + - identifier: UPNSuffix + columnName: AccountUPNSuffix + - identifier: NTDomain + columnName: AccountNTDomain +version: 2.0.1 +kind: Scheduled diff --git a/Solutions/Global Secure Access/Hunting Queries/sharepoint_downloads.yaml b/Solutions/Global Secure Access/Hunting Queries/sharepoint_downloads.yaml new file mode 100644 index 00000000000..0e9fc5b8561 --- /dev/null +++ b/Solutions/Global Secure Access/Hunting Queries/sharepoint_downloads.yaml @@ -0,0 +1,40 @@ +id: e8ae1375-4640-430c-ae8e-2514d09c71eb +name: SharePoint File Operation via Client IP with Previously Unseen User Agents +description: | + New user agents associated with a client IP for SharePoint file uploads/downloads. +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - EnrichedMicrosoft365AuditLogs +tactics: + - Exfiltration +relevantTechniques: + - T1030 +query: | + let starttime = todatetime('{{StartTimeISO}}'); + let endtime = todatetime('{{EndTimeISO}}'); + let lookback = starttime - 14d; + let historicalUA = EnrichedMicrosoft365AuditLogs + | where RecordType == "SharePointFileOperation" + | where Operation in ("FileDownloaded", "FileUploaded") + | where TimeGenerated between(lookback..starttime) + | extend ClientApplication = tostring(parse_json(AdditionalProperties).UserAgent) + | summarize by ClientIp, ClientApplication; + let recentUA = EnrichedMicrosoft365AuditLogs + | where RecordType == "SharePointFileOperation" + | where Operation in ("FileDownloaded", "FileUploaded") + | where TimeGenerated between(starttime..endtime) + | extend ClientApplication = tostring(parse_json(AdditionalProperties).UserAgent) + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by ClientIp, ClientApplication; + recentUA | join kind=leftanti ( + historicalUA + ) on ClientIp, ClientApplication + // Some EnrichedMicrosoft365AuditLogs records do not contain ClientIp information - exclude these for fewer results + | where not(isempty(ClientIp)) + | extend IP_0_Address = ClientIp +entityMappings: + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IP_0_Address +version: 2.0.1 diff --git a/Solutions/Global Secure Access/Package/3.0.0.zip b/Solutions/Global Secure Access/Package/3.0.0.zip new file mode 100644 index 00000000000..22d9b1485dd Binary files /dev/null and b/Solutions/Global Secure Access/Package/3.0.0.zip differ diff --git a/Solutions/Global Secure Access/Package/createUiDefinition.json b/Solutions/Global Secure Access/Package/createUiDefinition.json new file mode 100644 index 00000000000..7edea43d12a --- /dev/null +++ b/Solutions/Global Secure Access/Package/createUiDefinition.json @@ -0,0 +1,729 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "config": { + "isWizard": false, + "basics": { + "description": "\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/Global%20Secure%20Access/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\n[Global Secure Access](https://aka.ms/GlobalSecureAccess) is a [domain solution](https://learn.microsoft.com/en-us/azure/sentinel/sentinel-solutions-catalog#domain-solutions) and does not include any data connectors. The content in this solution requires one of the product solutions below.\n\n**Prerequisite:**\n\nInstall one or more of the listed solutions to unlock the value provided by this solution.\n1. Microsoft Entra ID \n\n**Underlying Microsoft Technologies used:**\n\nThis solution depends on the following technologies, and some of these dependencies may either be in Preview state or might result in additional ingestion or operational costs:\n1. Product solutions as described above\n\n\n**Workbooks:** 2, **Analytic Rules:** 19, **Hunting Queries:** 21\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "subscription": { + "resourceProviders": [ + "Microsoft.OperationsManagement/solutions", + "Microsoft.OperationalInsights/workspaces/providers/alertRules", + "Microsoft.Insights/workbooks", + "Microsoft.Logic/workflows" + ] + }, + "location": { + "metadata": { + "hidden": "Hiding location, we get it from the log analytics workspace" + }, + "visible": false + }, + "resourceGroup": { + "allowExisting": true + } + } + }, + "basics": [ + { + "name": "getLAWorkspace", + "type": "Microsoft.Solutions.ArmApiControl", + "toolTip": "This filters by workspaces that exist in the Resource Group selected", + "condition": "[greater(length(resourceGroup().name),0)]", + "request": { + "method": "GET", + "path": "[concat(subscription().id,'/providers/Microsoft.OperationalInsights/workspaces?api-version=2020-08-01')]" + } + }, + { + "name": "workspace", + "type": "Microsoft.Common.DropDown", + "label": "Workspace", + "placeholder": "Select a workspace", + "toolTip": "This dropdown will list only workspace that exists in the Resource Group selected", + "constraints": { + "allowedValues": "[map(filter(basics('getLAWorkspace').value, (filter) => contains(toLower(filter.id), toLower(resourceGroup().name))), (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.name, '\"}')))]", + "required": true + }, + "visible": true + } + ], + "steps": [ + { + "name": "workbooks", + "label": "Workbooks", + "subLabel": { + "preValidation": "Configure the workbooks", + "postValidation": "Done" + }, + "bladeTitle": "Workbooks", + "elements": [ + { + "name": "workbooks-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This Microsoft Sentinel Solution installs workbooks. Workbooks provide a flexible canvas for data monitoring, analysis, and the creation of rich visual reports within the Azure portal. They allow you to tap into one or many data sources from Microsoft Sentinel and combine them into unified interactive experiences." + } + }, + { + "name": "workbooks-link", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more", + "uri": "https://docs.microsoft.com/azure/sentinel/tutorial-monitor-your-data" + } + } + }, + { + "name": "workbook1", + "type": "Microsoft.Common.Section", + "label": "Microsoft Global Secure Access Enriched M365 Logs", + "elements": [ + { + "name": "workbook1-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This Workbook provides a detailed view of Microsoft 365 log data, enriched with contextual information to enhance visibility into user activities and potential security threats." + } + } + ] + }, + { + "name": "workbook2", + "type": "Microsoft.Common.Section", + "label": "Microsoft Global Secure Access Traffic Logs", + "elements": [ + { + "name": "workbook2-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This workbook provides an overview of all traffic logs within your network, offering insights into data transfer, anomalies, and potential threats." + } + } + ] + } + ] + }, + { + "name": "analytics", + "label": "Analytics", + "subLabel": { + "preValidation": "Configure the analytics", + "postValidation": "Done" + }, + "bladeTitle": "Analytics", + "elements": [ + { + "name": "analytics-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This solution installs the following analytic rule templates. After installing the solution, create and enable analytic rules in the Manage solution view." + } + }, + { + "name": "analytics-link", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more", + "uri": "https://docs.microsoft.com/azure/sentinel/tutorial-detect-threats-custom?WT.mc_id=Portal-Microsoft_Azure_CreateUIDef" + } + } + }, + { + "name": "analytic1", + "type": "Microsoft.Common.Section", + "label": "Detect Connections Outside Operational Hours", + "elements": [ + { + "name": "analytic1-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This query identifies connections that occur outside of the defined operational hours. It helps in monitoring and flagging any unusual activity that may occur during non-business hours, indicating potential security concerns or policy violations." + } + } + ] + }, + { + "name": "analytic2", + "type": "Microsoft.Common.Section", + "label": "Detect IP Address Changes and Overlapping Sessions", + "elements": [ + { + "name": "analytic2-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This query identifies network sessions based on DeviceId and UserPrincipalName, then checks for changed IP addresses and overlapping session times." + } + } + ] + }, + { + "name": "analytic3", + "type": "Microsoft.Common.Section", + "label": "Exchange AuditLog Disabled", + "elements": [ + { + "name": "analytic3-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies when the exchange audit logging has been disabled which may be an adversary attempt to evade detection or avoid other defenses." + } + } + ] + }, + { + "name": "analytic4", + "type": "Microsoft.Common.Section", + "label": "Accessed files shared by temporary external user", + "elements": [ + { + "name": "analytic4-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This detection identifies when an external user is added to a Team or Teams chat and shares a file which is accessed by many users (>10) and the users is removed within short period of time. This might be an indicator of suspicious activity." + } + } + ] + }, + { + "name": "analytic5", + "type": "Microsoft.Common.Section", + "label": "External User Added and Removed in Short Timeframe", + "elements": [ + { + "name": "analytic5-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This detection flags the occurrences of external user accounts that are added to a Team and then removed within one hour." + } + } + ] + }, + { + "name": "analytic6", + "type": "Microsoft.Common.Section", + "label": "Mail redirect via ExO transport rule", + "elements": [ + { + "name": "analytic6-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies when Exchange Online transport rule configured to forward emails.\nThis could be an adversary mailbox configured to collect mail from multiple user accounts." + } + } + ] + }, + { + "name": "analytic7", + "type": "Microsoft.Common.Section", + "label": "Malicious Inbox Rule", + "elements": [ + { + "name": "analytic7-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Often times after the initial compromise the attackers create inbox rules to delete emails that contain certain keywords.\n This is done so as to limit ability to warn compromised users that they've been compromised. Below is a sample query that tries to detect this.\nReference: https://www.reddit.com/r/sysadmin/comments/7kyp0a/recent_phishing_attempts_my_experience_and_what/" + } + } + ] + }, + { + "name": "analytic8", + "type": "Microsoft.Common.Section", + "label": "Multiple Teams deleted by a single user", + "elements": [ + { + "name": "analytic8-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This detection flags the occurrences of deleting multiple teams within an hour.\nThis data is a part of Office 365 Connector in Microsoft Sentinel." + } + } + ] + }, + { + "name": "analytic9", + "type": "Microsoft.Common.Section", + "label": "Multiple Users Email Forwarded to Same Destination", + "elements": [ + { + "name": "analytic9-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies when multiple (more than one) users' mailboxes are configured to forward to the same destination. \nThis could be an attacker-controlled destination mailbox configured to collect mail from multiple compromised user accounts." + } + } + ] + }, + { + "name": "analytic10", + "type": "Microsoft.Common.Section", + "label": "Office Policy Tampering", + "elements": [ + { + "name": "analytic10-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies if any tampering is done to either audit log, ATP Safelink, SafeAttachment, AntiPhish, or Dlp policy. \nAn adversary may use this technique to evade detection or avoid other policy-based defenses.\nReferences: https://docs.microsoft.com/powershell/module/exchange/advanced-threat-protection/remove-antiphishrule?view=exchange-ps." + } + } + ] + }, + { + "name": "analytic11", + "type": "Microsoft.Common.Section", + "label": "New Executable via Office FileUploaded Operation", + "elements": [ + { + "name": "analytic11-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies when executable file types are uploaded to Office services such as SharePoint and OneDrive.\nList currently includes exe, inf, gzip, cmd, bat file extensions.\nAdditionally, identifies when a given user is uploading these files to another user's workspace.\nThis may be an indication of a staging location for malware or other malicious activity." + } + } + ] + }, + { + "name": "analytic12", + "type": "Microsoft.Common.Section", + "label": "Rare and Potentially High-Risk Office Operations", + "elements": [ + { + "name": "analytic12-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies Office operations that are typically rare and can provide capabilities useful to attackers." + } + } + ] + }, + { + "name": "analytic13", + "type": "Microsoft.Common.Section", + "label": "SharePoint File Operation via Previously Unseen IPs", + "elements": [ + { + "name": "analytic13-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies anomalies using user behavior by setting a threshold for significant changes in file upload/download activities from new IP addresses. It establishes a baseline of typical behavior, compares it to recent activity, and flags deviations exceeding a default threshold of 25." + } + } + ] + }, + { + "name": "analytic14", + "type": "Microsoft.Common.Section", + "label": "SharePointFileOperation via devices with previously unseen user agents", + "elements": [ + { + "name": "analytic14-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies anomalies if the number of documents uploaded or downloaded from device(s) associated with a previously unseen user agent exceeds a threshold (default is 5) and deviation (default is 25%)." + } + } + ] + }, + { + "name": "analytic15", + "type": "Microsoft.Common.Section", + "label": "Office365 Sharepoint File Transfer Above Threshold", + "elements": [ + { + "name": "analytic15-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies Office365 Sharepoint File Transfers above a certain threshold in a 15-minute time period.\nPlease note that entity mapping for arrays is not supported, so when there is a single value in an array, we will pull that value from the array as a single string to populate the entity to support entity mapping features within Sentinel. Additionally, if the array is multivalued, we will input a string to indicate this with a unique hash so that matching will not occur." + } + } + ] + }, + { + "name": "analytic16", + "type": "Microsoft.Common.Section", + "label": "Office365 Sharepoint File Transfer Above Threshold", + "elements": [ + { + "name": "analytic16-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies Office365 Sharepoint File Transfers with a distinct folder count above a certain threshold in a 15-minute time period.\nPlease note that entity mapping for arrays is not supported, so when there is a single value in an array, we will pull that value from the array as a single string to populate the entity to support entity mapping features within Sentinel. Additionally, if the array is multivalued, we will input a string to indicate this with a unique hash so that matching will not occur." + } + } + ] + }, + { + "name": "analytic17", + "type": "Microsoft.Common.Section", + "label": "Detect Abnormal Deny Rate for Source to Destination IP", + "elements": [ + { + "name": "analytic17-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies abnormal deny rate for specific source IP to destination IP based on the normal average and standard deviation learned during a configured period. This can indicate potential exfiltration, initial access, or C2, where an attacker tries to exploit the same vulnerability on machines in the organization but is being blocked by firewall rules." + } + } + ] + }, + { + "name": "analytic18", + "type": "Microsoft.Common.Section", + "label": "Detect Protocol Changes for Destination Ports", + "elements": [ + { + "name": "analytic18-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies changes in the protocol used for specific destination ports, comparing the current runtime with a learned baseline. This can indicate potential protocol misuse or configuration changes." + } + } + ] + }, + { + "name": "analytic19", + "type": "Microsoft.Common.Section", + "label": "Detect Source IP Scanning Multiple Open Ports", + "elements": [ + { + "name": "analytic19-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies a source IP scanning multiple open ports on Global Secure Access Firewall. This can indicate malicious scanning of ports by an attacker, trying to reveal open ports in the organization that can be compromised for initial access." + } + } + ] + } + ] + }, + { + "name": "huntingqueries", + "label": "Hunting Queries", + "bladeTitle": "Hunting Queries", + "elements": [ + { + "name": "huntingqueries-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This solution installs the following hunting queries. After installing the solution, run these hunting queries to hunt for threats in the Manage solution view." + } + }, + { + "name": "huntingqueries-link", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more", + "uri": "https://docs.microsoft.com/azure/sentinel/hunting" + } + } + }, + { + "name": "huntingquery1", + "type": "Microsoft.Common.Section", + "label": "Anomalous access to other users' mailboxes", + "elements": [ + { + "name": "huntingquery1-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Looks for users accessing multiple other users' mailboxes or accessing multiple folders in another users mailbox. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery2", + "type": "Microsoft.Common.Section", + "label": "External User Added and Removed in a Short Timeframe", + "elements": [ + { + "name": "huntingquery2-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This hunting query identifies external user accounts that are added to a Team and then removed within one hour. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery3", + "type": "Microsoft.Common.Section", + "label": "External user from a new organisation added to Teams", + "elements": [ + { + "name": "huntingquery3-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This query identifies external users added to Teams where the user's domain is not one previously seen in Teams data. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery4", + "type": "Microsoft.Common.Section", + "label": "Mail Redirect via ExO Transport Rule", + "elements": [ + { + "name": "huntingquery4-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies when Exchange Online transport rule is configured to forward emails.\nThis could be an adversary mailbox configured to collect mail from multiple user accounts. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery5", + "type": "Microsoft.Common.Section", + "label": "Bots added to multiple teams", + "elements": [ + { + "name": "huntingquery5-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This hunting query helps identify bots added to multiple Teams in a short space of time. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery6", + "type": "Microsoft.Common.Section", + "label": "User made Owner of multiple teams", + "elements": [ + { + "name": "huntingquery6-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This hunting query identifies users who have been made Owner of multiple Teams. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery7", + "type": "Microsoft.Common.Section", + "label": "Multiple Teams deleted by a single user", + "elements": [ + { + "name": "huntingquery7-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This hunting query identifies where multiple Teams have been deleted by a single user in a short timeframe. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery8", + "type": "Microsoft.Common.Section", + "label": "Previously Unseen Bot or Application Added to Teams", + "elements": [ + { + "name": "huntingquery8-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This hunting query helps identify new, and potentially unapproved applications or bots being added to Teams. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery9", + "type": "Microsoft.Common.Section", + "label": "New Windows Reserved Filenames staged on Office file services", + "elements": [ + { + "name": "huntingquery9-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This identifies new Windows Reserved Filenames on Office services like SharePoint and OneDrive in the past 7 days. It also detects when a user uploads these files to another user's workspace, which may indicate malicious activity. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery10", + "type": "Microsoft.Common.Section", + "label": "Office Mail Forwarding - Hunting Version", + "elements": [ + { + "name": "huntingquery10-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Adversaries often abuse email-forwarding rules to monitor victim activities, steal information, and gain intelligence on the victim or their organization. This query highlights cases where user mail is being forwarded, including to external domains. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery11", + "type": "Microsoft.Common.Section", + "label": "Files uploaded to teams and access summary", + "elements": [ + { + "name": "huntingquery11-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This hunting query identifies files uploaded to SharePoint via a Teams chat and\nsummarizes users and IP addresses that have accessed these files. This allows for \nidentification of anomalous file sharing patterns. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery12", + "type": "Microsoft.Common.Section", + "label": "User added to Teams and immediately uploads file", + "elements": [ + { + "name": "huntingquery12-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This hunting query identifies users who are added to a Teams Channel or Teams chat\nand within 1 minute of being added upload a file via the chat. This might be\nan indicator of suspicious activity. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery13", + "type": "Microsoft.Common.Section", + "label": "Windows Reserved Filenames Staged on Office File Services", + "elements": [ + { + "name": "huntingquery13-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This identifies Windows Reserved Filenames on Office services like SharePoint and OneDrive. It also detects when a user uploads these files to another user's workspace, which may indicate malicious activity. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery14", + "type": "Microsoft.Common.Section", + "label": "Exes with double file extension and access summary", + "elements": [ + { + "name": "huntingquery14-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Provides a summary of executable files with double file extensions in SharePoint \n and the users and IP addresses that have accessed them. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery15", + "type": "Microsoft.Common.Section", + "label": "New Admin Account Activity Seen Which Was Not Seen Historically", + "elements": [ + { + "name": "huntingquery15-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This will help you discover any new admin account activity which was seen and were not seen historically.\nAny new accounts seen in the results can be validated and investigated for any suspicious activities. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery16", + "type": "Microsoft.Common.Section", + "label": "SharePointFileOperation via previously unseen IPs", + "elements": [ + { + "name": "huntingquery16-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Shows SharePoint upload/download volume by IPs with high-risk ASNs. New IPs with volume spikes may be unauthorized and exfiltrating documents. This hunting query depends on AzureActiveDirectory AzureActiveDirectory data connector (SigninLogs EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery17", + "type": "Microsoft.Common.Section", + "label": "SharePointFileOperation via devices with previously unseen user agents", + "elements": [ + { + "name": "huntingquery17-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Tracking via user agent is one way to differentiate between types of connecting device.\nIn homogeneous enterprise environments the user agent associated with an attacker device may stand out as unusual. This hunting query depends on AzureActiveDirectory AzureActiveDirectory data connector (SigninLogs EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery18", + "type": "Microsoft.Common.Section", + "label": "Non-owner mailbox login activity", + "elements": [ + { + "name": "huntingquery18-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Finds non-owner mailbox access by admin/delegate permissions. Whitelist valid users and check others for unauthorized access. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery19", + "type": "Microsoft.Common.Section", + "label": "PowerShell or non-browser mailbox login activity", + "elements": [ + { + "name": "huntingquery19-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Detects mailbox login from Exchange PowerShell. All accounts can use it by default, but admins can change it. Whitelist benign activities. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery20", + "type": "Microsoft.Common.Section", + "label": "SharePoint File Operation via Client IP with Previously Unseen User Agents", + "elements": [ + { + "name": "huntingquery20-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "New user agents associated with a client IP for SharePoint file uploads/downloads. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + }, + { + "name": "huntingquery21", + "type": "Microsoft.Common.Section", + "label": "Multiple Users Email Forwarded to Same Destination", + "elements": [ + { + "name": "huntingquery21-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Identifies when multiple (more than one) users' mailboxes are configured to forward to the same destination. \nThis could be an attacker-controlled destination mailbox configured to collect mail from multiple compromised user accounts. This hunting query depends on AzureActiveDirectory data connector (EnrichedMicrosoft365AuditLogs Parser or Table)" + } + } + ] + } + ] + } + ], + "outputs": { + "workspace-location": "[first(map(filter(basics('getLAWorkspace').value, (filter) => and(contains(toLower(filter.id), toLower(resourceGroup().name)),equals(filter.name,basics('workspace')))), (item) => item.location))]", + "location": "[location()]", + "workspace": "[basics('workspace')]" + } + } +} diff --git a/Solutions/Global Secure Access/Package/mainTemplate.json b/Solutions/Global Secure Access/Package/mainTemplate.json new file mode 100644 index 00000000000..c41c912851a --- /dev/null +++ b/Solutions/Global Secure Access/Package/mainTemplate.json @@ -0,0 +1,4967 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "author": "Microsoft - support@microsoft.com", + "comments": "Solution template for Global Secure Access" + }, + "parameters": { + "location": { + "type": "string", + "minLength": 1, + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Not used, but needed to pass arm-ttk test `Location-Should-Not-Be-Hardcoded`. We instead use the `workspace-location` which is derived from the LA workspace" + } + }, + "workspace-location": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "[concat('Region to deploy solution resources -- separate from location selection',parameters('location'))]" + } + }, + "workspace": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Workspace name for Log Analytics where Microsoft Sentinel is setup" + } + }, + "workbook1-name": { + "type": "string", + "defaultValue": "Microsoft Global Secure Access Enriched M365 Logs", + "minLength": 1, + "metadata": { + "description": "Name for the workbook" + } + }, + "workbook2-name": { + "type": "string", + "defaultValue": "Microsoft Global Secure Access Traffic Logs", + "minLength": 1, + "metadata": { + "description": "Name for the workbook" + } + } + }, + "variables": { + "email": "support@microsoft.com", + "_email": "[variables('email')]", + "_solutionName": "Global Secure Access", + "_solutionVersion": "3.0.0", + "solutionId": "azuresentinel.azure-sentinel-solution-globalsecureaccess", + "_solutionId": "[variables('solutionId')]", + "workbookVersion1": "1.0.0", + "workbookContentId1": "GSAM365EnrichedEvents", + "workbookId1": "[resourceId('Microsoft.Insights/workbooks', variables('workbookContentId1'))]", + "workbookTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-wb-',uniquestring(variables('_workbookContentId1'))))]", + "_workbookContentId1": "[variables('workbookContentId1')]", + "workspaceResourceId": "[resourceId('microsoft.OperationalInsights/Workspaces', parameters('workspace'))]", + "_workbookcontentProductId1": "[concat(take(variables('_solutionId'),50),'-','wb','-', uniqueString(concat(variables('_solutionId'),'-','Workbook','-',variables('_workbookContentId1'),'-', variables('workbookVersion1'))))]", + "workbookVersion2": "1.0.0", + "workbookContentId2": "GSANetworkTraffic", + "workbookId2": "[resourceId('Microsoft.Insights/workbooks', variables('workbookContentId2'))]", + "workbookTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-wb-',uniquestring(variables('_workbookContentId2'))))]", + "_workbookContentId2": "[variables('workbookContentId2')]", + "_workbookcontentProductId2": "[concat(take(variables('_solutionId'),50),'-','wb','-', uniqueString(concat(variables('_solutionId'),'-','Workbook','-',variables('_workbookContentId2'),'-', variables('workbookVersion2'))))]", + "analyticRuleObject1": { + "analyticRuleVersion1": "1.0.0", + "_analyticRulecontentId1": "4c9f0a9e-44d7-4c9b-b7f0-f6a6e0d8f8fa", + "analyticRuleId1": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '4c9f0a9e-44d7-4c9b-b7f0-f6a6e0d8f8fa')]", + "analyticRuleTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('4c9f0a9e-44d7-4c9b-b7f0-f6a6e0d8f8fa')))]", + "_analyticRulecontentProductId1": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','4c9f0a9e-44d7-4c9b-b7f0-f6a6e0d8f8fa','-', '1.0.0')))]" + }, + "analyticRuleObject2": { + "analyticRuleVersion2": "1.0.0", + "_analyticRulecontentId2": "57abf863-1c1e-46c6-85b2-35370b712c1e", + "analyticRuleId2": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '57abf863-1c1e-46c6-85b2-35370b712c1e')]", + "analyticRuleTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('57abf863-1c1e-46c6-85b2-35370b712c1e')))]", + "_analyticRulecontentProductId2": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','57abf863-1c1e-46c6-85b2-35370b712c1e','-', '1.0.0')))]" + }, + "analyticRuleObject3": { + "analyticRuleVersion3": "2.0.6", + "_analyticRulecontentId3": "dc451755-8ab3-4059-b805-e454c45d1d44", + "analyticRuleId3": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'dc451755-8ab3-4059-b805-e454c45d1d44')]", + "analyticRuleTemplateSpecName3": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('dc451755-8ab3-4059-b805-e454c45d1d44')))]", + "_analyticRulecontentProductId3": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','dc451755-8ab3-4059-b805-e454c45d1d44','-', '2.0.6')))]" + }, + "analyticRuleObject4": { + "analyticRuleVersion4": "2.1.1", + "_analyticRulecontentId4": "4d38f80f-6b7d-4a1f-aeaf-e38df637e6ac", + "analyticRuleId4": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '4d38f80f-6b7d-4a1f-aeaf-e38df637e6ac')]", + "analyticRuleTemplateSpecName4": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('4d38f80f-6b7d-4a1f-aeaf-e38df637e6ac')))]", + "_analyticRulecontentProductId4": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','4d38f80f-6b7d-4a1f-aeaf-e38df637e6ac','-', '2.1.1')))]" + }, + "analyticRuleObject5": { + "analyticRuleVersion5": "2.1.2", + "_analyticRulecontentId5": "1a8f1297-23a4-4f09-a20b-90af8fc3641a", + "analyticRuleId5": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '1a8f1297-23a4-4f09-a20b-90af8fc3641a')]", + "analyticRuleTemplateSpecName5": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('1a8f1297-23a4-4f09-a20b-90af8fc3641a')))]", + "_analyticRulecontentProductId5": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','1a8f1297-23a4-4f09-a20b-90af8fc3641a','-', '2.1.2')))]" + }, + "analyticRuleObject6": { + "analyticRuleVersion6": "2.0.4", + "_analyticRulecontentId6": "edcfc2e0-3134-434c-8074-9101c530d419", + "analyticRuleId6": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'edcfc2e0-3134-434c-8074-9101c530d419')]", + "analyticRuleTemplateSpecName6": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('edcfc2e0-3134-434c-8074-9101c530d419')))]", + "_analyticRulecontentProductId6": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','edcfc2e0-3134-434c-8074-9101c530d419','-', '2.0.4')))]" + }, + "analyticRuleObject7": { + "analyticRuleVersion7": "2.0.4", + "_analyticRulecontentId7": "a9c76c8d-f60d-49ec-9b1f-bdfee6db3807", + "analyticRuleId7": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'a9c76c8d-f60d-49ec-9b1f-bdfee6db3807')]", + "analyticRuleTemplateSpecName7": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('a9c76c8d-f60d-49ec-9b1f-bdfee6db3807')))]", + "_analyticRulecontentProductId7": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','a9c76c8d-f60d-49ec-9b1f-bdfee6db3807','-', '2.0.4')))]" + }, + "analyticRuleObject8": { + "analyticRuleVersion8": "2.0.4", + "_analyticRulecontentId8": "db60e4b6-a845-4f28-a18c-94ebbaad6c6c", + "analyticRuleId8": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'db60e4b6-a845-4f28-a18c-94ebbaad6c6c')]", + "analyticRuleTemplateSpecName8": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('db60e4b6-a845-4f28-a18c-94ebbaad6c6c')))]", + "_analyticRulecontentProductId8": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','db60e4b6-a845-4f28-a18c-94ebbaad6c6c','-', '2.0.4')))]" + }, + "analyticRuleObject9": { + "analyticRuleVersion9": "2.0.3", + "_analyticRulecontentId9": "d75e8289-d1cb-44d4-bd59-2f44a9172478", + "analyticRuleId9": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'd75e8289-d1cb-44d4-bd59-2f44a9172478')]", + "analyticRuleTemplateSpecName9": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('d75e8289-d1cb-44d4-bd59-2f44a9172478')))]", + "_analyticRulecontentProductId9": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','d75e8289-d1cb-44d4-bd59-2f44a9172478','-', '2.0.3')))]" + }, + "analyticRuleObject10": { + "analyticRuleVersion10": "2.0.3", + "_analyticRulecontentId10": "0f1f2b17-f9d6-4d2a-a0fb-a7ae1659e3eb", + "analyticRuleId10": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '0f1f2b17-f9d6-4d2a-a0fb-a7ae1659e3eb')]", + "analyticRuleTemplateSpecName10": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('0f1f2b17-f9d6-4d2a-a0fb-a7ae1659e3eb')))]", + "_analyticRulecontentProductId10": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','0f1f2b17-f9d6-4d2a-a0fb-a7ae1659e3eb','-', '2.0.3')))]" + }, + "analyticRuleObject11": { + "analyticRuleVersion11": "2.0.5", + "_analyticRulecontentId11": "178c62b4-d5e5-40f5-8eab-7fccd0051e7a", + "analyticRuleId11": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '178c62b4-d5e5-40f5-8eab-7fccd0051e7a')]", + "analyticRuleTemplateSpecName11": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('178c62b4-d5e5-40f5-8eab-7fccd0051e7a')))]", + "_analyticRulecontentProductId11": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','178c62b4-d5e5-40f5-8eab-7fccd0051e7a','-', '2.0.5')))]" + }, + "analyticRuleObject12": { + "analyticRuleVersion12": "2.0.5", + "_analyticRulecontentId12": "433c254d-4b84-46f7-99ec-9dfefb5f6a7b", + "analyticRuleId12": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '433c254d-4b84-46f7-99ec-9dfefb5f6a7b')]", + "analyticRuleTemplateSpecName12": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('433c254d-4b84-46f7-99ec-9dfefb5f6a7b')))]", + "_analyticRulecontentProductId12": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','433c254d-4b84-46f7-99ec-9dfefb5f6a7b','-', '2.0.5')))]" + }, + "analyticRuleObject13": { + "analyticRuleVersion13": "2.0.4", + "_analyticRulecontentId13": "7460e34e-4c99-47b2-b7c0-c42e339fc586", + "analyticRuleId13": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '7460e34e-4c99-47b2-b7c0-c42e339fc586')]", + "analyticRuleTemplateSpecName13": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('7460e34e-4c99-47b2-b7c0-c42e339fc586')))]", + "_analyticRulecontentProductId13": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','7460e34e-4c99-47b2-b7c0-c42e339fc586','-', '2.0.4')))]" + }, + "analyticRuleObject14": { + "analyticRuleVersion14": "2.2.4", + "_analyticRulecontentId14": "efd17c5f-5167-40f8-a1e9-0818940785d9", + "analyticRuleId14": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'efd17c5f-5167-40f8-a1e9-0818940785d9')]", + "analyticRuleTemplateSpecName14": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('efd17c5f-5167-40f8-a1e9-0818940785d9')))]", + "_analyticRulecontentProductId14": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','efd17c5f-5167-40f8-a1e9-0818940785d9','-', '2.2.4')))]" + }, + "analyticRuleObject15": { + "analyticRuleVersion15": "1.0.4", + "_analyticRulecontentId15": "30375d00-68cc-4f95-b89a-68064d566358", + "analyticRuleId15": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '30375d00-68cc-4f95-b89a-68064d566358')]", + "analyticRuleTemplateSpecName15": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('30375d00-68cc-4f95-b89a-68064d566358')))]", + "_analyticRulecontentProductId15": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','30375d00-68cc-4f95-b89a-68064d566358','-', '1.0.4')))]" + }, + "analyticRuleObject16": { + "analyticRuleVersion16": "1.0.4", + "_analyticRulecontentId16": "abd6976d-8f71-4851-98c4-4d086201319c", + "analyticRuleId16": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'abd6976d-8f71-4851-98c4-4d086201319c')]", + "analyticRuleTemplateSpecName16": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('abd6976d-8f71-4851-98c4-4d086201319c')))]", + "_analyticRulecontentProductId16": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','abd6976d-8f71-4851-98c4-4d086201319c','-', '1.0.4')))]" + }, + "analyticRuleObject17": { + "analyticRuleVersion17": "1.0.0", + "_analyticRulecontentId17": "e3b6a9e7-4c3a-45e6-8baf-1d3bfa8e0c2b", + "analyticRuleId17": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'e3b6a9e7-4c3a-45e6-8baf-1d3bfa8e0c2b')]", + "analyticRuleTemplateSpecName17": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('e3b6a9e7-4c3a-45e6-8baf-1d3bfa8e0c2b')))]", + "_analyticRulecontentProductId17": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','e3b6a9e7-4c3a-45e6-8baf-1d3bfa8e0c2b','-', '1.0.0')))]" + }, + "analyticRuleObject18": { + "analyticRuleVersion18": "1.0.0", + "_analyticRulecontentId18": "f6a8d6a5-3e9f-47c8-a8d5-1b2b9d3b7d6a", + "analyticRuleId18": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', 'f6a8d6a5-3e9f-47c8-a8d5-1b2b9d3b7d6a')]", + "analyticRuleTemplateSpecName18": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('f6a8d6a5-3e9f-47c8-a8d5-1b2b9d3b7d6a')))]", + "_analyticRulecontentProductId18": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','f6a8d6a5-3e9f-47c8-a8d5-1b2b9d3b7d6a','-', '1.0.0')))]" + }, + "analyticRuleObject19": { + "analyticRuleVersion19": "1.0.0", + "_analyticRulecontentId19": "82cfa6b9-5f7e-4b8b-8b2f-a63f21b7a7d1", + "analyticRuleId19": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '82cfa6b9-5f7e-4b8b-8b2f-a63f21b7a7d1')]", + "analyticRuleTemplateSpecName19": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('82cfa6b9-5f7e-4b8b-8b2f-a63f21b7a7d1')))]", + "_analyticRulecontentProductId19": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','82cfa6b9-5f7e-4b8b-8b2f-a63f21b7a7d1','-', '1.0.0')))]" + }, + "huntingQueryObject1": { + "huntingQueryVersion1": "2.0.1", + "_huntingQuerycontentId1": "271e8881-3044-4332-a5f4-42264c2e0315", + "huntingQueryTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('271e8881-3044-4332-a5f4-42264c2e0315')))]" + }, + "huntingQueryObject2": { + "huntingQueryVersion2": "2.0.1", + "_huntingQuerycontentId2": "119d9e1c-afcc-4d23-b239-cdb4e7bf851c", + "huntingQueryTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('119d9e1c-afcc-4d23-b239-cdb4e7bf851c')))]" + }, + "huntingQueryObject3": { + "huntingQueryVersion3": "2.0.1", + "_huntingQuerycontentId3": "6fce5baf-bfc2-4c56-a6b7-9c4733fc5a45", + "huntingQueryTemplateSpecName3": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('6fce5baf-bfc2-4c56-a6b7-9c4733fc5a45')))]" + }, + "huntingQueryObject4": { + "huntingQueryVersion4": "2.0.1", + "_huntingQuerycontentId4": "9891684a-1e3a-4546-9403-3439513cbc70", + "huntingQueryTemplateSpecName4": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('9891684a-1e3a-4546-9403-3439513cbc70')))]" + }, + "huntingQueryObject5": { + "huntingQueryVersion5": "2.0.1", + "_huntingQuerycontentId5": "9eb64924-ec8d-44d0-b1f2-10665150fb74", + "huntingQueryTemplateSpecName5": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('9eb64924-ec8d-44d0-b1f2-10665150fb74')))]" + }, + "huntingQueryObject6": { + "huntingQueryVersion6": "2.0.1", + "_huntingQuerycontentId6": "558f15dd-3171-4b11-bf24-31c0610a20e0", + "huntingQueryTemplateSpecName6": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('558f15dd-3171-4b11-bf24-31c0610a20e0')))]" + }, + "huntingQueryObject7": { + "huntingQueryVersion7": "2.0.1", + "_huntingQuerycontentId7": "64990414-b015-4edf-bef0-343b741e68c5", + "huntingQueryTemplateSpecName7": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('64990414-b015-4edf-bef0-343b741e68c5')))]" + }, + "huntingQueryObject8": { + "huntingQueryVersion8": "2.0.1", + "_huntingQuerycontentId8": "bf76e508-9282-4cf1-9cc1-5c20c3dea2ee", + "huntingQueryTemplateSpecName8": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('bf76e508-9282-4cf1-9cc1-5c20c3dea2ee')))]" + }, + "huntingQueryObject9": { + "huntingQueryVersion9": "2.0.1", + "_huntingQuerycontentId9": "641ecd2d-27c9-4f05-8433-8205096b09fc", + "huntingQueryTemplateSpecName9": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('641ecd2d-27c9-4f05-8433-8205096b09fc')))]" + }, + "huntingQueryObject10": { + "huntingQueryVersion10": "2.0.1", + "_huntingQuerycontentId10": "d49fc965-aef3-49f6-89ad-10cc4697eb5b", + "huntingQueryTemplateSpecName10": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('d49fc965-aef3-49f6-89ad-10cc4697eb5b')))]" + }, + "huntingQueryObject11": { + "huntingQueryVersion11": "2.0.1", + "_huntingQuerycontentId11": "90e198a9-efb6-4719-ad89-81b8e93633a7", + "huntingQueryTemplateSpecName11": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('90e198a9-efb6-4719-ad89-81b8e93633a7')))]" + }, + "huntingQueryObject12": { + "huntingQueryVersion12": "2.0.1", + "_huntingQuerycontentId12": "3d6d0c04-7337-40cf-ace6-c471d442356d", + "huntingQueryTemplateSpecName12": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('3d6d0c04-7337-40cf-ace6-c471d442356d')))]" + }, + "huntingQueryObject13": { + "huntingQueryVersion13": "2.0.1", + "_huntingQuerycontentId13": "61c28cd7-3139-4731-8ea7-2cbbeabb4684", + "huntingQueryTemplateSpecName13": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('61c28cd7-3139-4731-8ea7-2cbbeabb4684')))]" + }, + "huntingQueryObject14": { + "huntingQueryVersion14": "2.0.1", + "_huntingQuerycontentId14": "d12580c2-1474-4125-a8a3-553f50d91215", + "huntingQueryTemplateSpecName14": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('d12580c2-1474-4125-a8a3-553f50d91215')))]" + }, + "huntingQueryObject15": { + "huntingQueryVersion15": "2.0.1", + "_huntingQuerycontentId15": "723c5f46-133f-4f1e-ada6-5c138f811d75", + "huntingQueryTemplateSpecName15": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('723c5f46-133f-4f1e-ada6-5c138f811d75')))]" + }, + "huntingQueryObject16": { + "huntingQueryVersion16": "2.0.1", + "_huntingQuerycontentId16": "e3d24cfd-b2a1-4ba7-8f80-0360892f9d57", + "huntingQueryTemplateSpecName16": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('e3d24cfd-b2a1-4ba7-8f80-0360892f9d57')))]" + }, + "huntingQueryObject17": { + "huntingQueryVersion17": "2.0.1", + "_huntingQuerycontentId17": "f2367171-1514-4c67-88ef-27434b6a1093", + "huntingQueryTemplateSpecName17": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('f2367171-1514-4c67-88ef-27434b6a1093')))]" + }, + "huntingQueryObject18": { + "huntingQueryVersion18": "2.0.1", + "_huntingQuerycontentId18": "0a8f410d-38b5-4d75-90da-32b472b97230", + "huntingQueryTemplateSpecName18": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('0a8f410d-38b5-4d75-90da-32b472b97230')))]" + }, + "huntingQueryObject19": { + "huntingQueryVersion19": "2.0.1", + "_huntingQuerycontentId19": "49a4f65a-fe18-408e-afec-042fde93d3ce", + "huntingQueryTemplateSpecName19": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('49a4f65a-fe18-408e-afec-042fde93d3ce')))]" + }, + "huntingQueryObject20": { + "huntingQueryVersion20": "2.0.1", + "_huntingQuerycontentId20": "e8ae1375-4640-430c-ae8e-2514d09c71eb", + "huntingQueryTemplateSpecName20": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('e8ae1375-4640-430c-ae8e-2514d09c71eb')))]" + }, + "huntingQueryObject21": { + "huntingQueryVersion21": "2.0.1", + "_huntingQuerycontentId21": "a1551ae4-f61c-4bca-9c57-4d0d681db2e9", + "huntingQueryTemplateSpecName21": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-hq-',uniquestring('a1551ae4-f61c-4bca-9c57-4d0d681db2e9')))]" + }, + "_solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]" + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('workbookTemplateSpecName1')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "GSAM365EnrichedEvents Workbook with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('workbookVersion1')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Insights/workbooks", + "name": "[variables('workbookContentId1')]", + "location": "[parameters('workspace-location')]", + "kind": "shared", + "apiVersion": "2021-08-01", + "metadata": { + "description": "This Workbook provides a detailed view of Microsoft 365 log data, enriched with contextual information to enhance visibility into user activities and potential security threats." + }, + "properties": { + "displayName": "[parameters('workbook1-name')]", + "serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":1,\"content\":{\"json\":\"## Traffic Logs workbook\\n---\\n\\nLog information in the dashboard is limited to 30 days.\"},\"name\":\"text - 1\"},{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"parameters\":[{\"id\":\"ff8b2a55-1849-4848-acf8-eab5452e9f10\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"LogAnalyticWorkspace\",\"label\":\"Log Analytic Workspace\",\"type\":5,\"description\":\"The log analytic workspace in which to execute the queries\",\"isRequired\":true,\"query\":\"resources\\r\\n| where type == \\\"microsoft.operationalinsights/workspaces\\\"\\r\\n| project id\",\"typeSettings\":{\"resourceTypeFilter\":{\"microsoft.operationalinsights/workspaces\":true},\"additionalResourceOptions\":[\"value::1\"],\"showDefault\":false},\"timeContext\":{\"durationMs\":86400000},\"defaultValue\":\"value::1\",\"queryType\":1,\"resourceType\":\"microsoft.resourcegraph/resources\"},{\"id\":\"f15f34d8-8e2d-4c39-8dee-be2f979c86a8\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"TimeRange\",\"label\":\"Time Range\",\"type\":4,\"isRequired\":true,\"typeSettings\":{\"selectableValues\":[{\"durationMs\":300000},{\"durationMs\":900000},{\"durationMs\":1800000},{\"durationMs\":3600000},{\"durationMs\":14400000},{\"durationMs\":43200000},{\"durationMs\":86400000},{\"durationMs\":172800000},{\"durationMs\":259200000},{\"durationMs\":604800000},{\"durationMs\":1209600000},{\"durationMs\":2419200000},{\"durationMs\":2592000000}],\"allowCustom\":true},\"timeContext\":{\"durationMs\":86400000},\"value\":{\"durationMs\":2592000000}},{\"id\":\"8bab511b-53b3-4220-9d1c-372345b06728\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"Users\",\"type\":2,\"isRequired\":true,\"multiSelect\":true,\"quote\":\"'\",\"delimiter\":\",\",\"query\":\"EnrichedMicrosoft365AuditLogsDemos_CL\\r\\n| summarize Count = count() by UserId_s\\r\\n| order by Count desc, UserId_s asc\\r\\n| project Value = UserId_s, Label = strcat(UserId_s, ' - ', Count, ' Logs'), Selected = false\",\"typeSettings\":{\"limitSelectTo\":20,\"additionalResourceOptions\":[\"value::all\"],\"selectAllValue\":\"*\",\"showDefault\":false},\"timeContext\":{\"durationMs\":0},\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"value\":[\"value::all\"]}],\"style\":\"pills\",\"queryType\":1,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"parameters - 15\"},{\"type\":11,\"content\":{\"version\":\"LinkItem/1.0\",\"style\":\"tabs\",\"links\":[{\"id\":\"2b2cd1be-9d25-412c-8444-f005c4789b55\",\"cellValue\":\"tabSel\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Overview\",\"subTarget\":\"Overview\",\"style\":\"link\"},{\"id\":\"cc3e67f2-f20f-4430-8dee-d0773b90d9ce\",\"cellValue\":\"tabSel\",\"linkTarget\":\"parameter\",\"linkLabel\":\"All Traffic\",\"subTarget\":\"AllTraffic\",\"style\":\"link\"}]},\"name\":\"links - 7\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| project \\r\\n Timestamp = createdDateTime_t,\\r\\n User = userPrincipalName_s,\\r\\n SourceIP = SourceIP,\\r\\n DestinationIP = destinationIp_s,\\r\\n DestinationPort = destinationPort_d,\\r\\n Action = action_s,\\r\\n PolicyName = policyName_s,\\r\\n TransportProtocol = transportProtocol_s,\\r\\n TrafficType = trafficType_s,\\r\\n DestinationURL = destinationUrl_s,\\r\\n ReceivedBytes = receivedBytes_d,\\r\\n SentBytes = sentBytes_d,\\r\\n DeviceOS = deviceOperatingSystem_s,\\r\\n PolicyRuleID = policyRuleId_s\\r\\n| order by Timestamp desc\",\"size\":3,\"showAnalytics\":true,\"title\":\"Log\",\"timeContextFromParameter\":\"TimeRange\",\"showExportToExcel\":true,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"gridSettings\":{\"rowLimit\":1000,\"filter\":true}},\"conditionalVisibility\":{\"parameterName\":\"tabSel\",\"comparison\":\"isEqualTo\",\"value\":\"AllTraffic\"},\"name\":\"query - 6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"// Unique Users\\nNetworkAccessDemo_CL\\n| extend GeoInfo = geo_info_from_ip_address(SourceIP) // Extend each row with geolocation info\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\n| project SourceIP, Country = tostring(GeoInfo.country), State = tostring(GeoInfo.state), City = tostring(GeoInfo.city), Latitude = tostring(GeoInfo.latitude), Longitude = tostring(GeoInfo.longitude)\\n| summarize UniqueUsers=dcount(Country)\\n| extend snapshot = \\\"Total Locations\\\"\\n| project col1 = UniqueUsers, snapshot\\n\\n// Union with Unique Devices\\n| union (\\n NetworkAccessDemo_CL\\n | where userPrincipalName_s in ({Users}) or '*' in ({Users})\\n | extend BytesInGB = todouble(sentBytes_d + receivedBytes_d) / (1024 * 1024 * 1024) // Convert bytes to gigabytes\\n | summarize TotalBytesGB = sum(BytesInGB)\\n | extend snapshot = \\\"Total Bytes (GB)\\\"\\n | project col1 = tolong(TotalBytesGB), snapshot\\n)\\n\\n// Union with Total Internet Access\\n| union (\\n NetworkAccessDemo_CL\\n | where userPrincipalName_s in ({Users}) or '*' in ({Users})\\n | summarize TotalTransactions = count()\\n | extend snapshot = \\\"Total Transactions\\\"\\n | project col1 = TotalTransactions, snapshot\\n)\\n\\n// Union with Total Private Access\\n// Order by Snapshot for consistent tile ordering on dashboard\\n| order by snapshot\",\"size\":4,\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"snapshot\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"col1\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":true,\"size\":\"auto\"},\"mapSettings\":{\"locInfo\":\"LatLong\",\"sizeSettings\":\"ExistingClients\",\"sizeAggregation\":\"Sum\",\"legendMetric\":\"ExistingClients\",\"legendAggregation\":\"Sum\",\"itemColorSettings\":{\"type\":\"heatmap\",\"colorAggregation\":\"Sum\",\"nodeColorField\":\"ExistingClients\",\"heatmapPalette\":\"greenRed\"}},\"textSettings\":{\"style\":\"bignumber\"}},\"conditionalVisibility\":{\"parameterName\":\"tabSel\",\"comparison\":\"isEqualTo\",\"value\":\"Overview\"},\"name\":\"query - 2\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| extend BytesInGB = todouble(sentBytes_d + receivedBytes_d) / (1024 * 1024 * 1024) // Convert bytes to gigabytes\\r\\n| summarize TotalBytesGB = sum(BytesInGB) by bin(createdDateTime_t, 1h), trafficType_s\\r\\n| order by bin(createdDateTime_t, 1h) asc, trafficType_s asc\\r\\n| project createdDateTime_t, trafficType_s, TotalBytesGB\\r\\n\",\"size\":2,\"title\":\"Usage over Time (GB)\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\"},\"conditionalVisibility\":{\"parameterName\":\"tabSel\",\"comparison\":\"isEqualTo\",\"value\":\"Overview\"},\"name\":\"query - 0\"}]},\"name\":\"group - 5\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| extend GeoInfo = geo_info_from_ip_address(SourceIP) // Extend each row with geolocation info\\r\\n| project createdDateTime_t, SourceIP, Country = tostring(GeoInfo.country), State = tostring(GeoInfo.state), City = tostring(GeoInfo.city), Latitude = tostring(GeoInfo.latitude), Longitude = tostring(GeoInfo.longitude)\\r\\n| where Country != \\\"\\\"\\r\\n| summarize Count = count() by City, State, Country\\r\\n\",\"size\":0,\"title\":\"Locations\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"map\",\"mapSettings\":{\"locInfo\":\"CountryRegion\",\"locInfoColumn\":\"Country\",\"latitude\":\"Latitude\",\"longitude\":\"Longitude\",\"sizeSettings\":\"Count\",\"sizeAggregation\":\"Sum\",\"labelSettings\":\"Country\",\"legendMetric\":\"Country\",\"legendAggregation\":\"Count\",\"itemColorSettings\":{\"nodeColorField\":\"Count\",\"colorAggregation\":\"Sum\",\"type\":\"heatmap\",\"heatmapPalette\":\"turquoise\"}}},\"customWidth\":\"50\",\"name\":\"query - 0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| where tolower(action_s) == \\\"allow\\\" and destinationWebCategory_displayName_s != '' // Filter for allowed traffic\\r\\n| extend firstCategory = tostring(split(destinationWebCategory_displayName_s, ',')[0]) // Split and get the first category\\r\\n| summarize Count = count() by firstCategory\\r\\n| top 10 by Count\\r\\n\",\"size\":2,\"title\":\"Top Allowed Web Categories\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\"},\"customWidth\":\"50\",\"name\":\"query - 7\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| where tolower(action_s) == \\\"block\\\" and destinationFQDN_s != '' // Filter for allowed traffic\\r\\n| summarize Count = count() by destinationFQDN_s\\r\\n| top 100 by Count\",\"size\":0,\"title\":\"Top Blocked Destinations\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"query - 5\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| where tolower(action_s) == \\\"block\\\" and destinationWebCategory_displayName_s != '' // Filter for blocked traffic\\r\\n| extend firstCategory = tostring(split(destinationWebCategory_displayName_s, ',')[0]) // Split and get the first category\\r\\n| summarize Count = count() by firstCategory\\r\\n| top 10 by Count\\r\\n\",\"size\":3,\"title\":\"Top Blocked Web Categories\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\"},\"customWidth\":\"50\",\"name\":\"query - 6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| where sentBytes_d > 0\\r\\n| where tolower(action_s) != \\\"block\\\" \\r\\n| summarize Count = count() , Sent = sum(sentBytes_d), Recived = sum(receivedBytes_d), Total = sum(receivedBytes_d+ sentBytes_d) by destinationFQDN_s\\r\\n| order by Count desc\\r\\n\",\"size\":0,\"title\":\"Top Allowed Destinations\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Count\",\"formatter\":4,\"formatOptions\":{\"palette\":\"magenta\"}},{\"columnMatch\":\"Recived\",\"formatter\":4,\"formatOptions\":{\"palette\":\"turquoise\"}},{\"columnMatch\":\"Total\",\"formatter\":4,\"formatOptions\":{\"palette\":\"pink\"}},{\"columnMatch\":\"Sent\",\"formatter\":4,\"formatOptions\":{\"palette\":\"blue\"}}]}},\"customWidth\":\"50\",\"name\":\"query - 1\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| where transportProtocol_s != ''\\r\\n| summarize Count = count() by toupper(transportProtocol_s)\\r\\n| top 10 by Count\\r\\n\",\"size\":2,\"title\":\"Protocol Distburion\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\"},\"customWidth\":\"50\",\"name\":\"query - 3\"}]},\"conditionalVisibility\":{\"parameterName\":\"tabSel\",\"comparison\":\"isEqualTo\",\"value\":\"Overview\"},\"name\":\"group - 4\"}],\"$schema\":\"https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json\"}\r\n", + "version": "1.0", + "sourceId": "[variables('workspaceResourceId')]", + "category": "sentinel" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Workbook-', last(split(variables('workbookId1'),'/'))))]", + "properties": { + "description": "@{workbookKey=GSAM365EnrichedEvents; logoFileName=gsa.svg; description=This Workbook provides a detailed view of Microsoft 365 log data, enriched with contextual information to enhance visibility into user activities and potential security threats.; dataTypesDependencies=System.Object[]; dataConnectorsDependencies=System.Object[]; previewImagesFileNames=System.Object[]; version=1.0.0; title=Microsoft Global Secure Access Enriched M365 Logs; templateRelativePath=GSAM365EnrichedEvents.json; provider=Microsoft}.description", + "parentId": "[variables('workbookId1')]", + "contentId": "[variables('_workbookContentId1')]", + "kind": "Workbook", + "version": "[variables('workbookVersion1')]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "contentId": "AzureActiveDirectory", + "kind": "DataConnector" + } + ] + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_workbookContentId1')]", + "contentKind": "Workbook", + "displayName": "[parameters('workbook1-name')]", + "contentProductId": "[variables('_workbookcontentProductId1')]", + "id": "[variables('_workbookcontentProductId1')]", + "version": "[variables('workbookVersion1')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('workbookTemplateSpecName2')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "GSANetworkTraffic Workbook with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('workbookVersion2')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Insights/workbooks", + "name": "[variables('workbookContentId2')]", + "location": "[parameters('workspace-location')]", + "kind": "shared", + "apiVersion": "2021-08-01", + "metadata": { + "description": "This workbook provides an overview of all traffic logs within your network, offering insights into data transfer, anomalies, and potential threats." + }, + "properties": { + "displayName": "[parameters('workbook2-name')]", + "serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":1,\"content\":{\"json\":\"## Traffic Logs workbook\\n---\\n\\nLog information in the dashboard is limited to 30 days.\"},\"name\":\"text - 0\"},{\"type\":9,\"content\":{\"version\":\"KqlParameterItem/1.0\",\"crossComponentResources\":[\"\"],\"parameters\":[{\"id\":\"ff8b2a55-1849-4848-acf8-eab5452e9f10\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"LogAnalyticWorkspace\",\"label\":\"Log Analytic Workspace\",\"type\":5,\"description\":\"The log analytic workspace in which to execute the queries\",\"isRequired\":true,\"query\":\"resources\\r\\n| where type == \\\"microsoft.operationalinsights/workspaces\\\"\\r\\n| project id\",\"typeSettings\":{\"resourceTypeFilter\":{\"microsoft.operationalinsights/workspaces\":true},\"additionalResourceOptions\":[\"value::1\"],\"showDefault\":false},\"timeContext\":{\"durationMs\":86400000},\"defaultValue\":\"value::1\",\"queryType\":1,\"resourceType\":\"microsoft.resourcegraph/resources\"},{\"id\":\"f15f34d8-8e2d-4c39-8dee-be2f979c86a8\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"TimeRange\",\"label\":\"Time Range\",\"type\":4,\"isRequired\":true,\"typeSettings\":{\"selectableValues\":[{\"durationMs\":300000},{\"durationMs\":900000},{\"durationMs\":1800000},{\"durationMs\":3600000},{\"durationMs\":14400000},{\"durationMs\":43200000},{\"durationMs\":86400000},{\"durationMs\":172800000},{\"durationMs\":259200000},{\"durationMs\":604800000},{\"durationMs\":1209600000},{\"durationMs\":2419200000},{\"durationMs\":2592000000}],\"allowCustom\":true},\"timeContext\":{\"durationMs\":86400000},\"value\":{\"durationMs\":2592000000}},{\"id\":\"8bab511b-53b3-4220-9d1c-372345b06728\",\"version\":\"KqlParameterItem/1.0\",\"name\":\"Users\",\"type\":2,\"isRequired\":true,\"multiSelect\":true,\"quote\":\"'\",\"delimiter\":\",\",\"query\":\"EnrichedMicrosoft365AuditLogsDemos_CL\\r\\n| summarize Count = count() by UserId_s\\r\\n| order by Count desc, UserId_s asc\\r\\n| project Value = UserId_s, Label = strcat(UserId_s, ' - ', Count, ' Logs'), Selected = false\",\"typeSettings\":{\"limitSelectTo\":20,\"additionalResourceOptions\":[\"value::all\"],\"selectAllValue\":\"*\",\"showDefault\":false},\"timeContext\":{\"durationMs\":0},\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"value\":[\"value::all\"]}],\"style\":\"pills\",\"queryType\":1,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"name\":\"parameters - 15\"},{\"type\":11,\"content\":{\"version\":\"LinkItem/1.0\",\"style\":\"tabs\",\"links\":[{\"id\":\"2b2cd1be-9d25-412c-8444-f005c4789b55\",\"cellValue\":\"tabSel\",\"linkTarget\":\"parameter\",\"linkLabel\":\"Overview\",\"subTarget\":\"Overview\",\"style\":\"link\"},{\"id\":\"cc3e67f2-f20f-4430-8dee-d0773b90d9ce\",\"cellValue\":\"tabSel\",\"linkTarget\":\"parameter\",\"linkLabel\":\"All Traffic\",\"subTarget\":\"AllTraffic\",\"style\":\"link\"}]},\"name\":\"links - 7\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| project \\r\\n Timestamp = createdDateTime_t,\\r\\n User = userPrincipalName_s,\\r\\n SourceIP = SourceIP,\\r\\n DestinationIP = destinationIp_s,\\r\\n DestinationPort = destinationPort_d,\\r\\n Action = action_s,\\r\\n PolicyName = policyName_s,\\r\\n TransportProtocol = transportProtocol_s,\\r\\n TrafficType = trafficType_s,\\r\\n DestinationURL = destinationUrl_s,\\r\\n ReceivedBytes = receivedBytes_d,\\r\\n SentBytes = sentBytes_d,\\r\\n DeviceOS = deviceOperatingSystem_s,\\r\\n PolicyRuleID = policyRuleId_s\\r\\n| order by Timestamp desc\",\"size\":3,\"showAnalytics\":true,\"title\":\"Log\",\"timeContextFromParameter\":\"TimeRange\",\"showExportToExcel\":true,\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"gridSettings\":{\"rowLimit\":1000,\"filter\":true}},\"conditionalVisibility\":{\"parameterName\":\"tabSel\",\"comparison\":\"isEqualTo\",\"value\":\"AllTraffic\"},\"name\":\"query - 6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"// Unique Users\\nNetworkAccessDemo_CL\\n| extend GeoInfo = geo_info_from_ip_address(SourceIP) // Extend each row with geolocation info\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\n| project SourceIP, Country = tostring(GeoInfo.country), State = tostring(GeoInfo.state), City = tostring(GeoInfo.city), Latitude = tostring(GeoInfo.latitude), Longitude = tostring(GeoInfo.longitude)\\n| summarize UniqueUsers=dcount(Country)\\n| extend snapshot = \\\"Total Locations\\\"\\n| project col1 = UniqueUsers, snapshot\\n\\n// Union with Unique Devices\\n| union (\\n NetworkAccessDemo_CL\\n | where userPrincipalName_s in ({Users}) or '*' in ({Users})\\n | extend BytesInGB = todouble(sentBytes_d + receivedBytes_d) / (1024 * 1024 * 1024) // Convert bytes to gigabytes\\n | summarize TotalBytesGB = sum(BytesInGB)\\n | extend snapshot = \\\"Total Bytes (GB)\\\"\\n | project col1 = tolong(TotalBytesGB), snapshot\\n)\\n\\n// Union with Total Internet Access\\n| union (\\n NetworkAccessDemo_CL\\n | where userPrincipalName_s in ({Users}) or '*' in ({Users})\\n | summarize TotalTransactions = count()\\n | extend snapshot = \\\"Total Trasnacations\\\"\\n | project col1 = TotalTransactions, snapshot\\n)\\n\\n// Union with Total Private Access\\n// Order by Snapshot for consistent tile ordering on dashboard\\n| order by snapshot\",\"size\":4,\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"tiles\",\"tileSettings\":{\"titleContent\":{\"columnMatch\":\"snapshot\",\"formatter\":1},\"leftContent\":{\"columnMatch\":\"col1\",\"formatter\":12,\"formatOptions\":{\"palette\":\"auto\"}},\"showBorder\":true,\"size\":\"auto\"},\"mapSettings\":{\"locInfo\":\"LatLong\",\"sizeSettings\":\"ExistingClients\",\"sizeAggregation\":\"Sum\",\"legendMetric\":\"ExistingClients\",\"legendAggregation\":\"Sum\",\"itemColorSettings\":{\"type\":\"heatmap\",\"colorAggregation\":\"Sum\",\"nodeColorField\":\"ExistingClients\",\"heatmapPalette\":\"greenRed\"}},\"textSettings\":{\"style\":\"bignumber\"}},\"conditionalVisibility\":{\"parameterName\":\"tabSel\",\"comparison\":\"isEqualTo\",\"value\":\"Overview\"},\"name\":\"query - 2\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| extend BytesInGB = todouble(sentBytes_d + receivedBytes_d) / (1024 * 1024 * 1024) // Convert bytes to gigabytes\\r\\n| summarize TotalBytesGB = sum(BytesInGB) by bin(createdDateTime_t, 1h), trafficType_s\\r\\n| order by bin(createdDateTime_t, 1h) asc, trafficType_s asc\\r\\n| project createdDateTime_t, trafficType_s, TotalBytesGB\\r\\n\",\"size\":2,\"title\":\"Usage over Time (GB)\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"barchart\"},\"conditionalVisibility\":{\"parameterName\":\"tabSel\",\"comparison\":\"isEqualTo\",\"value\":\"Overview\"},\"name\":\"query - 0\"}]},\"name\":\"group - 5\"},{\"type\":12,\"content\":{\"version\":\"NotebookGroup/1.0\",\"groupType\":\"editable\",\"items\":[{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| extend GeoInfo = geo_info_from_ip_address(SourceIP) // Extend each row with geolocation info\\r\\n| project createdDateTime_t, SourceIP, Country = tostring(GeoInfo.country), State = tostring(GeoInfo.state), City = tostring(GeoInfo.city), Latitude = tostring(GeoInfo.latitude), Longitude = tostring(GeoInfo.longitude)\\r\\n| where Country != \\\"\\\"\\r\\n| summarize Count = count() by City, State, Country\\r\\n\\r\\n\",\"size\":0,\"title\":\"Locations\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"map\",\"mapSettings\":{\"locInfo\":\"CountryRegion\",\"locInfoColumn\":\"Country\",\"latitude\":\"Latitude\",\"longitude\":\"Longitude\",\"sizeSettings\":\"Count\",\"sizeAggregation\":\"Sum\",\"labelSettings\":\"Country\",\"legendMetric\":\"Country\",\"legendAggregation\":\"Count\",\"itemColorSettings\":{\"nodeColorField\":\"Count\",\"colorAggregation\":\"Sum\",\"type\":\"heatmap\",\"heatmapPalette\":\"turquoise\"}}},\"customWidth\":\"50\",\"name\":\"query - 0\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| where tolower(action_s) == \\\"allow\\\" and destinationWebCategory_displayName_s != '' // Filter for allowed traffic\\r\\n| extend firstCategory = tostring(split(destinationWebCategory_displayName_s, ',')[0]) // Split and get the first category\\r\\n| summarize Count = count() by firstCategory\\r\\n| top 10 by Count\\r\\n\",\"size\":2,\"title\":\"Top Allowed Web Categories\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\"},\"customWidth\":\"50\",\"name\":\"query - 7\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| where tolower(action_s) == \\\"block\\\" and destinationFQDN_s != '' // Filter for allowed traffic\\r\\n| summarize Count = count() by destinationFQDN_s\\r\\n| top 100 by Count\",\"size\":0,\"title\":\"Top Blocked Destinations\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\"},\"customWidth\":\"50\",\"name\":\"query - 5\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| where tolower(action_s) == \\\"block\\\" and destinationWebCategory_displayName_s != '' // Filter for blocked traffic\\r\\n| extend firstCategory = tostring(split(destinationWebCategory_displayName_s, ',')[0]) // Split and get the first category\\r\\n| summarize Count = count() by firstCategory\\r\\n| top 10 by Count\\r\\n\",\"size\":3,\"title\":\"Top Blocked Web Categories\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\"},\"customWidth\":\"50\",\"name\":\"query - 6\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| where sentBytes_d > 0\\r\\n| where tolower(action_s) != \\\"block\\\" \\r\\n| summarize Count = count() , Sent = sum(sentBytes_d), Recived = sum(receivedBytes_d), Total = sum(receivedBytes_d+ sentBytes_d) by destinationFQDN_s\\r\\n| order by Count desc\\r\\n\",\"size\":0,\"title\":\"Top Allowed Destinations\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"gridSettings\":{\"formatters\":[{\"columnMatch\":\"Count\",\"formatter\":4,\"formatOptions\":{\"palette\":\"magenta\"}},{\"columnMatch\":\"Recived\",\"formatter\":4,\"formatOptions\":{\"palette\":\"turquoise\"}},{\"columnMatch\":\"Total\",\"formatter\":4,\"formatOptions\":{\"palette\":\"pink\"}},{\"columnMatch\":\"Sent\",\"formatter\":4,\"formatOptions\":{\"palette\":\"blue\"}}]}},\"customWidth\":\"50\",\"name\":\"query - 1\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"NetworkAccessDemo_CL\\r\\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\\r\\n| where transportProtocol_s != ''\\r\\n| summarize Count = count() by toupper(transportProtocol_s)\\r\\n| top 10 by Count\\r\\n\",\"size\":2,\"title\":\"Protocol Distburion\",\"timeContextFromParameter\":\"TimeRange\",\"queryType\":0,\"resourceType\":\"microsoft.operationalinsights/workspaces\",\"visualization\":\"piechart\"},\"customWidth\":\"50\",\"name\":\"query - 3\"}]},\"conditionalVisibility\":{\"parameterName\":\"tabSel\",\"comparison\":\"isEqualTo\",\"value\":\"Overview\"},\"name\":\"group - 4\"}],\"$schema\":\"https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json\"}\r\n", + "version": "1.0", + "sourceId": "[variables('workspaceResourceId')]", + "category": "sentinel" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Workbook-', last(split(variables('workbookId2'),'/'))))]", + "properties": { + "description": "@{workbookKey=GSANetworkTraffic; logoFileName=gsa.svg; description=This workbook provides an overview of all traffic logs within your network, offering insights into data transfer, anomalies, and potential threats.; dataTypesDependencies=System.Object[]; dataConnectorsDependencies=System.Object[]; previewImagesFileNames=System.Object[]; version=1.0.0; title=Microsoft Global Secure Access Traffic Logs; templateRelativePath=GSANetworkTraffic.json; subtitle=; provider=Microsoft}.description", + "parentId": "[variables('workbookId2')]", + "contentId": "[variables('_workbookContentId2')]", + "kind": "Workbook", + "version": "[variables('workbookVersion2')]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "contentId": "AzureActiveDirectory", + "kind": "DataConnector" + } + ] + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_workbookContentId2')]", + "contentKind": "Workbook", + "displayName": "[parameters('workbook2-name')]", + "contentProductId": "[variables('_workbookcontentProductId2')]", + "id": "[variables('_workbookcontentProductId2')]", + "version": "[variables('workbookVersion2')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject1').analyticRuleTemplateSpecName1]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Identity - AfterHoursActivity_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject1').analyticRuleVersion1]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject1')._analyticRulecontentId1]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "This query identifies connections that occur outside of the defined operational hours. It helps in monitoring and flagging any unusual activity that may occur during non-business hours, indicating potential security concerns or policy violations.", + "displayName": "Detect Connections Outside Operational Hours", + "enabled": false, + "query": "let starttime = todatetime('{{StartTimeISO}}');\nlet endtime = todatetime('{{EndTimeISO}}');\nlet operational_start_hour = 8; // Start of operational hours (8 AM)\nlet operational_end_hour = 18; // End of operational hours (6 PM)\nNetworkAccessTraffic\n| where TimeGenerated between(starttime .. endtime)\n| extend HourOfDay = datetime_part('hour', TimeGenerated)\n| where HourOfDay < operational_start_hour or HourOfDay >= operational_end_hour\n| project TimeGenerated, UserPrincipalName, SourceIp, DestinationIp, DestinationPort, Action, DeviceId, DeviceOperatingSystem, ConnectionId\n| extend IPCustomEntity = SourceIp, AccountCustomEntity = UserPrincipalName\n", + "queryFrequency": "PT1H", + "queryPeriod": "PT24H", + "severity": "High", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "InitialAccess" + ], + "techniques": [ + "T1078", + "T1133" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "Name", + "columnName": "AccountCustomEntity" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "IPCustomEntity" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject1').analyticRuleId1,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 1", + "parentId": "[variables('analyticRuleObject1').analyticRuleId1]", + "contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject1').analyticRuleVersion1]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]", + "contentKind": "AnalyticsRule", + "displayName": "Detect Connections Outside Operational Hours", + "contentProductId": "[variables('analyticRuleObject1')._analyticRulecontentProductId1]", + "id": "[variables('analyticRuleObject1')._analyticRulecontentProductId1]", + "version": "[variables('analyticRuleObject1').analyticRuleVersion1]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject2').analyticRuleTemplateSpecName2]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Identity - SharedSessions_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject2').analyticRuleVersion2]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject2')._analyticRulecontentId2]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "This query identifies network sessions based on DeviceId and UserPrincipalName, then checks for changed IP addresses and overlapping session times.", + "displayName": "Detect IP Address Changes and Overlapping Sessions", + "enabled": false, + "query": "// Identify sessions\nlet sessions = \n NetworkAccessTraffic\n | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), SourceIps = make_set(SourceIp) by DeviceId, UserPrincipalName, SessionId\n | sort by StartTime asc;\n// Check for changed IP addresses and overlapping session times\nsessions\n | extend PreviousSourceIps = prev(SourceIps, 1)\n | extend PreviousEndTime = prev(EndTime, 1)\n | extend PreviousDeviceId = prev(DeviceId, 1)\n | extend PreviousUserPrincipalName = prev(UserPrincipalName, 1)\n | where DeviceId == PreviousDeviceId and UserPrincipalName == PreviousUserPrincipalName\n | where set_difference(SourceIps, PreviousSourceIps) != dynamic([]) // Check if the current and previous IP sets differ\n | where PreviousEndTime > StartTime // Check for overlapping session times\n | project DeviceId, UserPrincipalName, SourceIps, PreviousSourceIps, StartTime, EndTime, PreviousEndTime\n | extend IPCustomEntity = tostring(array_slice(SourceIps, 0, 1)[0]), PreviousIPCustomEntity = tostring(array_slice(PreviousSourceIps, 0, 1)[0]), AccountCustomEntity = UserPrincipalName\n", + "queryFrequency": "PT1H", + "queryPeriod": "PT24H", + "severity": "High", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "InitialAccess" + ], + "techniques": [ + "T1078", + "T1133" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "Name", + "columnName": "AccountCustomEntity" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "IPCustomEntity" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject2').analyticRuleId2,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 2", + "parentId": "[variables('analyticRuleObject2').analyticRuleId2]", + "contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject2').analyticRuleVersion2]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]", + "contentKind": "AnalyticsRule", + "displayName": "Detect IP Address Changes and Overlapping Sessions", + "contentProductId": "[variables('analyticRuleObject2')._analyticRulecontentProductId2]", + "id": "[variables('analyticRuleObject2')._analyticRulecontentProductId2]", + "version": "[variables('analyticRuleObject2').analyticRuleVersion2]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject3').analyticRuleTemplateSpecName3]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - exchange_auditlogdisabled_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject3').analyticRuleVersion3]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject3')._analyticRulecontentId3]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies when the exchange audit logging has been disabled which may be an adversary attempt to evade detection or avoid other defenses.", + "displayName": "Exchange AuditLog Disabled", + "enabled": false, + "query": "EnrichedMicrosoft365AuditLogs\n| where Workload =~ \"Exchange\"\n| where UserType in~ (\"Admin\", \"DcAdmin\")\n| where Operation =~ \"Set-AdminAuditLogConfig\"\n| extend AdminAuditLogEnabledValue = tostring(parse_json(tostring(parse_json(tostring(array_slice(parse_json(tostring(AdditionalProperties.Parameters)), 3, 3)))[0])).Value)\n| where AdminAuditLogEnabledValue =~ \"False\"\n| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP = SourceIp, ResultStatus, Parameters = tostring(AdditionalProperties.Parameters), AdminAuditLogEnabledValue\n| extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId)\n| extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '')\n| extend AccountName = iff(UserId contains '\\\\', tostring(split(UserId, '\\\\')[1]), AccountName)\n| extend AccountNTDomain = iff(UserId contains '\\\\', tostring(split(UserId, '\\\\')[0]), '')\n", + "queryFrequency": "P1D", + "queryPeriod": "P1D", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "DefenseEvasion" + ], + "techniques": [ + "T1562" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + }, + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "Name", + "columnName": "AccountNTDomain" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIP" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject3').analyticRuleId3,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 3", + "parentId": "[variables('analyticRuleObject3').analyticRuleId3]", + "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject3').analyticRuleVersion3]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]", + "contentKind": "AnalyticsRule", + "displayName": "Exchange AuditLog Disabled", + "contentProductId": "[variables('analyticRuleObject3')._analyticRulecontentProductId3]", + "id": "[variables('analyticRuleObject3')._analyticRulecontentProductId3]", + "version": "[variables('analyticRuleObject3').analyticRuleVersion3]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject4').analyticRuleTemplateSpecName4]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - External User added to Team and immediately uploads file_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject4').analyticRuleVersion4]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject4')._analyticRulecontentId4]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "This detection identifies when an external user is added to a Team or Teams chat and shares a file which is accessed by many users (>10) and the users is removed within short period of time. This might be an indicator of suspicious activity.", + "displayName": "Accessed files shared by temporary external user", + "enabled": false, + "query": "let fileAccessThreshold = 10;\nEnrichedMicrosoft365AuditLogs\n| where Workload =~ \"MicrosoftTeams\"\n| where Operation =~ \"MemberAdded\"\n| extend MemberAdded = tostring(parse_json(tostring(AdditionalProperties)).Members[0].UPN)\n| where MemberAdded contains \"#EXT#\"\n| project TimeAdded = TimeGenerated, Operation, MemberAdded, UserWhoAdded = UserId, TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName)\n| join kind=inner (\n EnrichedMicrosoft365AuditLogs\n | where Workload =~ \"MicrosoftTeams\"\n | where Operation =~ \"MemberRemoved\"\n | extend MemberAdded = tostring(parse_json(tostring(AdditionalProperties)).Members[0].UPN)\n | where MemberAdded contains \"#EXT#\"\n | project TimeDeleted = TimeGenerated, Operation, MemberAdded, UserWhoDeleted = UserId, TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName)\n) on MemberAdded, TeamName\n| where TimeDeleted > TimeAdded\n| join kind=inner (\n EnrichedMicrosoft365AuditLogs\n | where RecordType == \"SharePointFileOperation\"\n | where Operation == \"FileUploaded\"\n | extend MemberAdded = UserId, SourceRelativeUrl = tostring(parse_json(tostring(AdditionalProperties)).SourceRelativeUrl), TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName)\n | where SourceRelativeUrl has \"Microsoft Teams Chat Files\"\n | join kind=inner (\n EnrichedMicrosoft365AuditLogs\n | where RecordType == \"SharePointFileOperation\"\n | where Operation == \"FileAccessed\"\n | extend SourceRelativeUrl = tostring(parse_json(tostring(AdditionalProperties)).SourceRelativeUrl), TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName)\n | where SourceRelativeUrl has \"Microsoft Teams Chat Files\"\n | summarize FileAccessCount = count() by ObjectId, TeamName\n | where FileAccessCount > fileAccessThreshold\n ) on ObjectId, TeamName\n) on MemberAdded, TeamName\n| project-away MemberAdded1, MemberAdded2, ObjectId1, Operation1, Operation2\n| extend MemberAddedAccountName = tostring(split(MemberAdded, \"@\")[0]), MemberAddedAccountUPNSuffix = tostring(split(MemberAdded, \"@\")[1])\n| extend UserWhoAddedAccountName = tostring(split(UserWhoAdded, \"@\")[0]), UserWhoAddedAccountUPNSuffix = tostring(split(UserWhoAdded, \"@\")[1])\n| extend UserWhoDeletedAccountName = tostring(split(UserWhoDeleted, \"@\")[0]), UserWhoDeletedAccountUPNSuffix = tostring(split(UserWhoDeleted, \"@\")[1])\n", + "queryFrequency": "PT1H", + "queryPeriod": "PT1H", + "severity": "Low", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "InitialAccess" + ], + "techniques": [ + "T1566" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "MemberAdded" + }, + { + "identifier": "Name", + "columnName": "MemberAddedAccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "MemberAddedAccountUPNSuffix" + } + ] + }, + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserWhoAdded" + }, + { + "identifier": "Name", + "columnName": "UserWhoAddedAccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "UserWhoAddedAccountUPNSuffix" + } + ] + }, + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserWhoDeleted" + }, + { + "identifier": "Name", + "columnName": "UserWhoDeletedAccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "UserWhoDeletedAccountUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIP" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject4').analyticRuleId4,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 4", + "parentId": "[variables('analyticRuleObject4').analyticRuleId4]", + "contentId": "[variables('analyticRuleObject4')._analyticRulecontentId4]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject4').analyticRuleVersion4]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject4')._analyticRulecontentId4]", + "contentKind": "AnalyticsRule", + "displayName": "Accessed files shared by temporary external user", + "contentProductId": "[variables('analyticRuleObject4')._analyticRulecontentProductId4]", + "id": "[variables('analyticRuleObject4')._analyticRulecontentProductId4]", + "version": "[variables('analyticRuleObject4').analyticRuleVersion4]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject5').analyticRuleTemplateSpecName5]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - ExternalUserAddedRemovedInTeams_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject5').analyticRuleVersion5]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject5')._analyticRulecontentId5]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "This detection flags the occurrences of external user accounts that are added to a Team and then removed within one hour.", + "displayName": "External User Added and Removed in Short Timeframe", + "enabled": false, + "query": "let TeamsAddDel = (Op:string){\nEnrichedMicrosoft365AuditLogs\n | where Workload =~ \"MicrosoftTeams\"\n | where Operation == Op\n | where tostring(AdditionalProperties.Members) has (\"#EXT#\")\n | mv-expand Members = parse_json(tostring(AdditionalProperties.Members))\n | extend UPN = tostring(Members.UPN)\n | where UPN has (\"#EXT#\")\n | project TimeGenerated, Operation, UPN, UserId, TeamName = tostring(AdditionalProperties.TeamName), ClientIP = SourceIp\n};\nlet TeamsAdd = TeamsAddDel(\"MemberAdded\")\n| project TimeAdded = TimeGenerated, Operation, MemberAdded = UPN, UserWhoAdded = UserId, TeamName, ClientIP;\nlet TeamsDel = TeamsAddDel(\"MemberRemoved\")\n| project TimeDeleted = TimeGenerated, Operation, MemberRemoved = UPN, UserWhoDeleted = UserId, TeamName, ClientIP;\nTeamsAdd\n| join kind = inner (TeamsDel) on $left.MemberAdded == $right.MemberRemoved\n| where TimeDeleted > TimeAdded\n| project TimeAdded, TimeDeleted, MemberAdded_Removed = MemberAdded, UserWhoAdded, UserWhoDeleted, TeamName\n| extend MemberAdded_RemovedAccountName = tostring(split(MemberAdded_Removed, \"@\")[0]), MemberAddedAccountUPNSuffix = tostring(split(MemberAdded_Removed, \"@\")[1])\n| extend UserWhoAddedAccountName = tostring(split(UserWhoAdded, \"@\")[0]), UserWhoAddedAccountUPNSuffix = tostring(split(UserWhoAdded, \"@\")[1])\n| extend UserWhoDeletedAccountName = tostring(split(UserWhoDeleted, \"@\")[0]), UserWhoDeletedAccountUPNSuffix = tostring(split(UserWhoDeleted, \"@\")[1])\n", + "queryFrequency": "PT1H", + "queryPeriod": "PT1H", + "severity": "Low", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Persistence" + ], + "techniques": [ + "T1136" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "MemberAdded_Removed" + }, + { + "identifier": "Name", + "columnName": "MemberAdded_RemovedAccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "MemberAddedAccountUPNSuffix" + } + ] + }, + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserWhoAdded" + }, + { + "identifier": "Name", + "columnName": "UserWhoAddedAccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "UserWhoAddedAccountUPNSuffix" + } + ] + }, + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserWhoDeleted" + }, + { + "identifier": "Name", + "columnName": "UserWhoDeletedAccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "UserWhoDeletedAccountUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIp" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject5').analyticRuleId5,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 5", + "parentId": "[variables('analyticRuleObject5').analyticRuleId5]", + "contentId": "[variables('analyticRuleObject5')._analyticRulecontentId5]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject5').analyticRuleVersion5]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject5')._analyticRulecontentId5]", + "contentKind": "AnalyticsRule", + "displayName": "External User Added and Removed in Short Timeframe", + "contentProductId": "[variables('analyticRuleObject5')._analyticRulecontentProductId5]", + "id": "[variables('analyticRuleObject5')._analyticRulecontentProductId5]", + "version": "[variables('analyticRuleObject5').analyticRuleVersion5]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject6').analyticRuleTemplateSpecName6]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - Mail_redirect_via_ExO_transport_rule_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject6').analyticRuleVersion6]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject6')._analyticRulecontentId6]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies when Exchange Online transport rule configured to forward emails.\nThis could be an adversary mailbox configured to collect mail from multiple user accounts.", + "displayName": "Mail redirect via ExO transport rule", + "enabled": false, + "query": "EnrichedMicrosoft365AuditLogs\n| where Workload == \"Exchange\"\n| where Operation in~ (\"New-TransportRule\", \"Set-TransportRule\")\n| mv-apply DynamicParameters = todynamic(tostring(AdditionalProperties.Parameters)) on (\n summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))\n )\n| extend RuleName = case(\n Operation =~ \"Set-TransportRule\", ObjectId, // Assuming ObjectId maps to what was previously OfficeObjectId\n Operation =~ \"New-TransportRule\", ParsedParameters.Name,\n \"Unknown\"\n )\n| mv-expand ExpandedParameters = todynamic(tostring(AdditionalProperties.Parameters))\n| where ExpandedParameters.Name in~ (\"BlindCopyTo\", \"RedirectMessageTo\") and isnotempty(ExpandedParameters.Value)\n| extend RedirectTo = ExpandedParameters.Value\n| extend ClientIPValues = extract_all(@'\\[?(::ffff:)?(?P(\\d+\\.\\d+\\.\\d+\\.\\d+)|[^\\]]+)\\]?([-:](?P\\d+))?', dynamic([\"IPAddress\", \"Port\"]), ClientIp)[0]\n| extend From = ParsedParameters.From\n| project TimeGenerated, RedirectTo, IPAddress = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1]), UserId, From, Operation, RuleName, Parameters = tostring(AdditionalProperties.Parameters)\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "queryFrequency": "PT1H", + "queryPeriod": "PT1H", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Collection", + "Exfiltration" + ], + "techniques": [ + "T1114", + "T1020" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "IPAddress" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject6').analyticRuleId6,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 6", + "parentId": "[variables('analyticRuleObject6').analyticRuleId6]", + "contentId": "[variables('analyticRuleObject6')._analyticRulecontentId6]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject6').analyticRuleVersion6]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject6')._analyticRulecontentId6]", + "contentKind": "AnalyticsRule", + "displayName": "Mail redirect via ExO transport rule", + "contentProductId": "[variables('analyticRuleObject6')._analyticRulecontentProductId6]", + "id": "[variables('analyticRuleObject6')._analyticRulecontentProductId6]", + "version": "[variables('analyticRuleObject6').analyticRuleVersion6]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject7').analyticRuleTemplateSpecName7]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - Malicious_Inbox_Rule_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject7').analyticRuleVersion7]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject7')._analyticRulecontentId7]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Often times after the initial compromise the attackers create inbox rules to delete emails that contain certain keywords.\n This is done so as to limit ability to warn compromised users that they've been compromised. Below is a sample query that tries to detect this.\nReference: https://www.reddit.com/r/sysadmin/comments/7kyp0a/recent_phishing_attempts_my_experience_and_what/", + "displayName": "Malicious Inbox Rule", + "enabled": false, + "query": "let Keywords = dynamic([\"helpdesk\", \"alert\", \"suspicious\", \"fake\", \"malicious\", \"phishing\", \"spam\", \"do not click\", \"do not open\", \"hijacked\", \"Fatal\"]);\nEnrichedMicrosoft365AuditLogs\n| where Workload =~ \"Exchange\"\n| where Operation =~ \"New-InboxRule\" and (ResultStatus =~ \"True\" or ResultStatus =~ \"Succeeded\")\n| where tostring(parse_json(tostring(AdditionalProperties)).Parameters) has \"Deleted Items\" or tostring(parse_json(tostring(AdditionalProperties)).Parameters) has \"Junk Email\" or tostring(parse_json(tostring(AdditionalProperties)).Parameters) has \"DeleteMessage\"\n| extend Events = parse_json(tostring(AdditionalProperties)).Parameters\n| extend SubjectContainsWords = tostring(Events.SubjectContainsWords), BodyContainsWords = tostring(Events.BodyContainsWords), SubjectOrBodyContainsWords = tostring(Events.SubjectOrBodyContainsWords)\n| where SubjectContainsWords has_any (Keywords) or BodyContainsWords has_any (Keywords) or SubjectOrBodyContainsWords has_any (Keywords)\n| extend ClientIPAddress = case(ClientIp has \".\", tostring(split(ClientIp, \":\")[0]), ClientIp has \"[\", tostring(trim_start(@'[[]',tostring(split(ClientIp, \"]\")[0]))), ClientIp)\n| extend Keyword = iff(isnotempty(SubjectContainsWords), SubjectContainsWords, (iff(isnotempty(BodyContainsWords), BodyContainsWords, SubjectOrBodyContainsWords)))\n| extend RuleDetail = case(ObjectId contains '/', tostring(split(ObjectId, '/')[-1]), tostring(split(ObjectId, '\\\\')[-1]))\n| summarize count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by Operation, UserId, ClientIPAddress, ResultStatus, Keyword, ObjectId, RuleDetail\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "queryFrequency": "P1D", + "queryPeriod": "P1D", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Persistence", + "DefenseEvasion" + ], + "techniques": [ + "T1098", + "T1078" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + }, + { + "entityType": "Host", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "OriginatingServer" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIPAddress" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject7').analyticRuleId7,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 7", + "parentId": "[variables('analyticRuleObject7').analyticRuleId7]", + "contentId": "[variables('analyticRuleObject7')._analyticRulecontentId7]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject7').analyticRuleVersion7]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject7')._analyticRulecontentId7]", + "contentKind": "AnalyticsRule", + "displayName": "Malicious Inbox Rule", + "contentProductId": "[variables('analyticRuleObject7')._analyticRulecontentProductId7]", + "id": "[variables('analyticRuleObject7')._analyticRulecontentProductId7]", + "version": "[variables('analyticRuleObject7').analyticRuleVersion7]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject8').analyticRuleTemplateSpecName8]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - MultipleTeamsDeletes_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject8').analyticRuleVersion8]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject8')._analyticRulecontentId8]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "This detection flags the occurrences of deleting multiple teams within an hour.\nThis data is a part of Office 365 Connector in Microsoft Sentinel.", + "displayName": "Multiple Teams deleted by a single user", + "enabled": false, + "query": "let max_delete_count = 3;\nEnrichedMicrosoft365AuditLogs\n| where Workload =~ \"MicrosoftTeams\"\n| where Operation =~ \"TeamDeleted\"\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), DeletedTeams = make_set(tostring(AdditionalProperties.TeamName), 1000) by UserId\n| where array_length(DeletedTeams) > max_delete_count\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "queryFrequency": "P1D", + "queryPeriod": "P1D", + "severity": "Low", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Impact" + ], + "techniques": [ + "T1485", + "T1489" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject8').analyticRuleId8,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 8", + "parentId": "[variables('analyticRuleObject8').analyticRuleId8]", + "contentId": "[variables('analyticRuleObject8')._analyticRulecontentId8]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject8').analyticRuleVersion8]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject8')._analyticRulecontentId8]", + "contentKind": "AnalyticsRule", + "displayName": "Multiple Teams deleted by a single user", + "contentProductId": "[variables('analyticRuleObject8')._analyticRulecontentProductId8]", + "id": "[variables('analyticRuleObject8')._analyticRulecontentProductId8]", + "version": "[variables('analyticRuleObject8').analyticRuleVersion8]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject9').analyticRuleTemplateSpecName9]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - Office_MailForwarding_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject9').analyticRuleVersion9]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject9')._analyticRulecontentId9]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies when multiple (more than one) users' mailboxes are configured to forward to the same destination. \nThis could be an attacker-controlled destination mailbox configured to collect mail from multiple compromised user accounts.", + "displayName": "Multiple Users Email Forwarded to Same Destination", + "enabled": false, + "query": "let queryfrequency = 1d;\n let queryperiod = 7d;\n EnrichedMicrosoft365AuditLogs\n | where TimeGenerated > ago(queryperiod)\n | where Workload =~ \"Exchange\"\n //| where Operation in (\"Set-Mailbox\", \"New-InboxRule\", \"Set-InboxRule\") // Uncomment or adjust based on actual field usage\n | where tostring(AdditionalProperties.Parameters) has_any (\"ForwardTo\", \"RedirectTo\", \"ForwardingSmtpAddress\")\n | mv-apply DynamicParameters = todynamic(tostring(AdditionalProperties.Parameters)) on (\n summarize ParsedParameters = make_bag(bag_pack(tostring(DynamicParameters.Name), DynamicParameters.Value))\n )\n | evaluate bag_unpack(ParsedParameters, columnsConflict='replace_source')\n | extend DestinationMailAddress = tolower(case(\n isnotempty(column_ifexists(\"ForwardTo\", \"\")), column_ifexists(\"ForwardTo\", \"\"),\n isnotempty(column_ifexists(\"RedirectTo\", \"\")), column_ifexists(\"RedirectTo\", \"\"),\n isnotempty(column_ifexists(\"ForwardingSmtpAddress\", \"\")), trim_start(@\"smtp:\", column_ifexists(\"ForwardingSmtpAddress\", \"\")),\n \"\"))\n | where isnotempty(DestinationMailAddress)\n | mv-expand split(DestinationMailAddress, \";\")\n | extend ClientIPValues = extract_all(@'\\[?(::ffff:)?(?P(\\d+\\.\\d+\\.\\d+\\.\\d+)|[^\\]]+)\\]?([-:](?P\\d+))?', dynamic([\"IPAddress\", \"Port\"]), ClientIp)[0]\n | extend ClientIP = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1])\n | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), DistinctUserCount = dcount(UserId), UserId = make_set(UserId, 250), Ports = make_set(Port, 250), EventCount = count() by tostring(DestinationMailAddress), ClientIP\n | where DistinctUserCount > 1 and EndTime > ago(queryfrequency)\n | mv-expand UserId to typeof(string)\n | extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "queryFrequency": "P1D", + "queryPeriod": "P7D", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Collection", + "Exfiltration" + ], + "techniques": [ + "T1114", + "T1020" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIP" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject9').analyticRuleId9,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 9", + "parentId": "[variables('analyticRuleObject9').analyticRuleId9]", + "contentId": "[variables('analyticRuleObject9')._analyticRulecontentId9]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject9').analyticRuleVersion9]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject9')._analyticRulecontentId9]", + "contentKind": "AnalyticsRule", + "displayName": "Multiple Users Email Forwarded to Same Destination", + "contentProductId": "[variables('analyticRuleObject9')._analyticRulecontentProductId9]", + "id": "[variables('analyticRuleObject9')._analyticRulecontentProductId9]", + "version": "[variables('analyticRuleObject9').analyticRuleVersion9]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject10').analyticRuleTemplateSpecName10]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - office_policytampering_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject10').analyticRuleVersion10]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject10')._analyticRulecontentId10]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies if any tampering is done to either audit log, ATP Safelink, SafeAttachment, AntiPhish, or Dlp policy. \nAn adversary may use this technique to evade detection or avoid other policy-based defenses.\nReferences: https://docs.microsoft.com/powershell/module/exchange/advanced-threat-protection/remove-antiphishrule?view=exchange-ps.", + "displayName": "Office Policy Tampering", + "enabled": false, + "query": "let opList = EnrichedMicrosoft365AuditLogs \n | summarize by Operation\n | where Operation has_any (\"Remove\", \"Disable\")\n | where Operation contains \"AntiPhish\" or Operation contains \"SafeAttachment\" or Operation contains \"SafeLinks\" or Operation contains \"Dlp\" or Operation contains \"Audit\"\n | summarize make_set(Operation, 500);\nEnrichedMicrosoft365AuditLogs\n | where RecordType == \"ExchangeAdmin\"\n | where UserType in~ (\"Admin\", \"DcAdmin\")\n | where Operation in~ (opList)\n | extend ClientIPOnly = case( \n ClientIp has \".\", tostring(split(ClientIp, \":\")[0]), \n ClientIp has \"[\", tostring(trim_start(@'[[]', tostring(split(ClientIp, \"]\")[0]))),\n ClientIp\n ) \n | extend Port = case(\n ClientIp has \".\", tostring(split(ClientIp, \":\")[1]),\n ClientIp has \"[\", tostring(split(ClientIp, \"]:\")[1]),\n \"\"\n )\n | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP = ClientIPOnly, Port, ResultStatus, Parameters = tostring(AdditionalProperties.Parameters)\n | extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "queryFrequency": "P1D", + "queryPeriod": "P1D", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Persistence", + "DefenseEvasion" + ], + "techniques": [ + "T1098", + "T1562" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIP" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject10').analyticRuleId10,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 10", + "parentId": "[variables('analyticRuleObject10').analyticRuleId10]", + "contentId": "[variables('analyticRuleObject10')._analyticRulecontentId10]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject10').analyticRuleVersion10]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject10')._analyticRulecontentId10]", + "contentKind": "AnalyticsRule", + "displayName": "Office Policy Tampering", + "contentProductId": "[variables('analyticRuleObject10')._analyticRulecontentProductId10]", + "id": "[variables('analyticRuleObject10')._analyticRulecontentProductId10]", + "version": "[variables('analyticRuleObject10').analyticRuleVersion10]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject11').analyticRuleTemplateSpecName11]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - Office_Uploaded_Executables_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject11').analyticRuleVersion11]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject11')._analyticRulecontentId11]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies when executable file types are uploaded to Office services such as SharePoint and OneDrive.\nList currently includes exe, inf, gzip, cmd, bat file extensions.\nAdditionally, identifies when a given user is uploading these files to another user's workspace.\nThis may be an indication of a staging location for malware or other malicious activity.", + "displayName": "New Executable via Office FileUploaded Operation", + "enabled": false, + "query": "let threshold = 2;\nlet uploadOp = 'FileUploaded';\nlet execExt = dynamic(['exe', 'inf', 'gzip', 'cmd', 'bat']);\nlet starttime = 8d;\nlet endtime = 1d;\nEnrichedMicrosoft365AuditLogs\n| where TimeGenerated >= ago(endtime)\n| where Operation == uploadOp\n| extend SourceFileExtension = extract(@\"\\.([^\\./]+)$\", 1, tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)) // Extract file extension\n| where SourceFileExtension in (execExt)\n| extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl)\n| extend SourceRelativeUrl = tostring(parse_json(tostring(AdditionalProperties)).SourceRelativeUrl)\n| extend SourceFileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)\n| project TimeGenerated, Id, Workload, RecordType, Operation, UserType, UserKey, UserId, ClientIp, UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent), Site_Url, SourceRelativeUrl, SourceFileName\n| join kind=leftanti (\n EnrichedMicrosoft365AuditLogs\n | where TimeGenerated between (ago(starttime) .. ago(endtime))\n | where Operation == uploadOp\n | extend SourceFileExtension = extract(@\"\\.([^\\./]+)$\", 1, tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)) // Extract file extension\n | where SourceFileExtension in (execExt)\n | extend SourceRelativeUrl = tostring(parse_json(tostring(AdditionalProperties)).SourceRelativeUrl)\n | summarize SourceRelativeUrl = make_set(SourceRelativeUrl, 100000), UserId = make_set(UserId, 100000), PrevSeenCount = count() by SourceFileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)\n // Uncomment the line below to enforce the threshold\n // | where PrevSeenCount > threshold\n | mvexpand SourceRelativeUrl, UserId\n | extend SourceRelativeUrl = tostring(SourceRelativeUrl), UserId = tostring(UserId)\n) on SourceFileName, SourceRelativeUrl, UserId\n| extend SiteUrlUserFolder = tolower(split(Site_Url, '/')[-2])\n| extend UserIdUserFolderFormat = tolower(replace_regex(UserId, '@|\\\\.', '_'))\n| extend UserIdDiffThanUserFolder = iff(Site_Url has '/personal/' and SiteUrlUserFolder != UserIdUserFolderFormat, true, false)\n| summarize TimeGenerated = make_list(TimeGenerated, 100000), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), UserAgents = make_list(UserAgent, 100000), Ids = make_list(Id, 100000), SourceRelativeUrls = make_list(SourceRelativeUrl, 100000), FileNames = make_list(SourceFileName, 100000)\nby Workload, RecordType, Operation, UserType, UserKey, UserId, ClientIp, Site_Url, SiteUrlUserFolder, UserIdUserFolderFormat, UserIdDiffThanUserFolder\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "queryFrequency": "P1D", + "queryPeriod": "P8D", + "severity": "Low", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "CommandAndControl", + "LateralMovement" + ], + "techniques": [ + "T1105", + "T1570" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIp" + } + ] + }, + { + "entityType": "URL", + "fieldMappings": [ + { + "identifier": "Url", + "columnName": "Site_Url" + } + ] + }, + { + "entityType": "File", + "fieldMappings": [ + { + "identifier": "Name", + "columnName": "FileNames" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject11').analyticRuleId11,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 11", + "parentId": "[variables('analyticRuleObject11').analyticRuleId11]", + "contentId": "[variables('analyticRuleObject11')._analyticRulecontentId11]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject11').analyticRuleVersion11]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject11')._analyticRulecontentId11]", + "contentKind": "AnalyticsRule", + "displayName": "New Executable via Office FileUploaded Operation", + "contentProductId": "[variables('analyticRuleObject11')._analyticRulecontentProductId11]", + "id": "[variables('analyticRuleObject11')._analyticRulecontentProductId11]", + "version": "[variables('analyticRuleObject11').analyticRuleVersion11]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject12').analyticRuleTemplateSpecName12]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - RareOfficeOperations_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject12').analyticRuleVersion12]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject12')._analyticRulecontentId12]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies Office operations that are typically rare and can provide capabilities useful to attackers.", + "displayName": "Rare and Potentially High-Risk Office Operations", + "enabled": false, + "query": "EnrichedMicrosoft365AuditLogs\n| where Operation in~ ( \"Add-MailboxPermission\", \"Add-MailboxFolderPermission\", \"Set-Mailbox\", \"New-ManagementRoleAssignment\", \"New-InboxRule\", \"Set-InboxRule\", \"Set-TransportRule\")\nand not(UserId has_any ('NT AUTHORITY\\\\SYSTEM (Microsoft.Exchange.ServiceHost)', 'NT AUTHORITY\\\\SYSTEM (Microsoft.Exchange.AdminApi.NetCore)', 'NT AUTHORITY\\\\SYSTEM (w3wp)', 'devilfish-applicationaccount') and Operation in~ ( \"Add-MailboxPermission\", \"Set-Mailbox\"))\n| extend ClientIPOnly = tostring(extract_all(@'\\[?(::ffff:)?(?P(\\d+\\.\\d+\\.\\d+\\.\\d+)|[^\\]]+)\\]?', dynamic([\"IPAddress\"]), ClientIp)[0])\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "queryFrequency": "P1D", + "queryPeriod": "P1D", + "severity": "Low", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Persistence", + "Collection" + ], + "techniques": [ + "T1098", + "T1114" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIPOnly" + } + ] + }, + { + "entityType": "CloudApplication", + "fieldMappings": [ + { + "identifier": "AppId", + "columnName": "AppId" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject12').analyticRuleId12,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 12", + "parentId": "[variables('analyticRuleObject12').analyticRuleId12]", + "contentId": "[variables('analyticRuleObject12')._analyticRulecontentId12]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject12').analyticRuleVersion12]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject12')._analyticRulecontentId12]", + "contentKind": "AnalyticsRule", + "displayName": "Rare and Potentially High-Risk Office Operations", + "contentProductId": "[variables('analyticRuleObject12')._analyticRulecontentProductId12]", + "id": "[variables('analyticRuleObject12')._analyticRulecontentProductId12]", + "version": "[variables('analyticRuleObject12').analyticRuleVersion12]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject13').analyticRuleTemplateSpecName13]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - SharePoint_Downloads_byNewIP_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject13').analyticRuleVersion13]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject13')._analyticRulecontentId13]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies anomalies using user behavior by setting a threshold for significant changes in file upload/download activities from new IP addresses. It establishes a baseline of typical behavior, compares it to recent activity, and flags deviations exceeding a default threshold of 25.", + "displayName": "SharePoint File Operation via Previously Unseen IPs", + "enabled": false, + "query": "let threshold = 0.25;\nlet szSharePointFileOperation = \"SharePointFileOperation\";\nlet szOperations = dynamic([\"FileDownloaded\", \"FileUploaded\"]);\nlet starttime = 14d;\nlet endtime = 1d;\n// Define a baseline of normal user behavior\nlet userBaseline = EnrichedMicrosoft365AuditLogs\n| where TimeGenerated between(ago(starttime)..ago(endtime))\n| where RecordType == szSharePointFileOperation\n| where Operation in (szOperations)\n| extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent)\n| extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl)\n| where isnotempty(UserAgent)\n| summarize Count = count() by UserId, Operation, Site_Url, ClientIp\n| summarize AvgCount = avg(Count) by UserId, Operation, Site_Url, ClientIp;\n// Get recent user activity\nlet recentUserActivity = EnrichedMicrosoft365AuditLogs\n| where TimeGenerated > ago(endtime)\n| where RecordType == szSharePointFileOperation\n| where Operation in (szOperations)\n| extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent)\n| extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl)\n| where isnotempty(UserAgent)\n| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), RecentCount = count() by UserId, UserType, Operation, Site_Url, ClientIp, ObjectId, Workload, UserAgent;\n// Join the baseline and recent activity, and calculate the deviation\nlet UserBehaviorAnalysis = userBaseline \n| join kind=inner (recentUserActivity) on UserId, Operation, Site_Url, ClientIp\n| extend Deviation = abs(RecentCount - AvgCount) / AvgCount;\n// Filter for significant deviations\nUserBehaviorAnalysis\n| where Deviation > threshold\n| project StartTimeUtc, EndTimeUtc, UserId, UserType, Operation, ClientIp, Site_Url, ObjectId, Workload, UserAgent, Deviation, Count=RecentCount\n| order by Count desc, ClientIp asc, Operation asc, UserId asc\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "queryFrequency": "P1D", + "queryPeriod": "P14D", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Exfiltration" + ], + "techniques": [ + "T1030" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIp" + } + ] + }, + { + "entityType": "URL", + "fieldMappings": [ + { + "identifier": "Url", + "columnName": "Site_Url" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject13').analyticRuleId13,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 13", + "parentId": "[variables('analyticRuleObject13').analyticRuleId13]", + "contentId": "[variables('analyticRuleObject13')._analyticRulecontentId13]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject13').analyticRuleVersion13]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject13')._analyticRulecontentId13]", + "contentKind": "AnalyticsRule", + "displayName": "SharePoint File Operation via Previously Unseen IPs", + "contentProductId": "[variables('analyticRuleObject13')._analyticRulecontentProductId13]", + "id": "[variables('analyticRuleObject13')._analyticRulecontentProductId13]", + "version": "[variables('analyticRuleObject13').analyticRuleVersion13]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject14').analyticRuleTemplateSpecName14]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - SharePoint_Downloads_byNewUserAgent_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject14').analyticRuleVersion14]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject14')._analyticRulecontentId14]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies anomalies if the number of documents uploaded or downloaded from device(s) associated with a previously unseen user agent exceeds a threshold (default is 5) and deviation (default is 25%).", + "displayName": "SharePointFileOperation via devices with previously unseen user agents", + "enabled": false, + "query": "// Set threshold for the number of downloads/uploads from a new user agent\nlet threshold = 5;\n// Define constants for SharePoint file operations\nlet szSharePointFileOperation = \"SharePointFileOperation\";\nlet szOperations = dynamic([\"FileDownloaded\", \"FileUploaded\"]);\n// Define the historical activity for analysis\nlet starttime = 14d; // Define the start time for historical data (14 days ago)\nlet endtime = 1d; // Define the end time for historical data (1 day ago)\n// Extract the base events for analysis\nlet Baseevents =\n EnrichedMicrosoft365AuditLogs\n | where TimeGenerated between (ago(starttime) .. ago(endtime))\n | where RecordType == szSharePointFileOperation\n | where Operation in (szOperations)\n | extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent)\n | extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl)\n | where isnotempty(UserAgent);\n// Identify frequently occurring user agents\nlet FrequentUA = Baseevents\n | summarize FUACount = count() by UserAgent, RecordType, Operation\n | where FUACount >= threshold\n | distinct UserAgent;\n// Calculate a user baseline for further analysis\nlet UserBaseLine = Baseevents\n | summarize Count = count() by UserId, Operation, Site_Url\n | summarize AvgCount = avg(Count) by UserId, Operation, Site_Url;\n// Extract recent activity for analysis\nlet RecentActivity = EnrichedMicrosoft365AuditLogs\n | where TimeGenerated > ago(endtime)\n | where RecordType == szSharePointFileOperation\n | where Operation in (szOperations)\n | extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent)\n | extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl)\n | where isnotempty(UserAgent)\n | where UserAgent in (FrequentUA)\n | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ObjectIdCount = dcount(ObjectId), ObjectIdList = make_set(ObjectId), UserAgentSeenCount = count()\n by RecordType, Operation, UserAgent, UserId, ClientIp, Site_Url;\n// Analyze user behavior based on baseline and recent activity\nlet UserBehaviorAnalysis = UserBaseLine\n | join kind=inner (RecentActivity) on UserId, Operation, Site_Url\n | extend Deviation = abs(UserAgentSeenCount - AvgCount) / AvgCount;\n// Filter and format results for specific user behavior analysis\nUserBehaviorAnalysis\n | where Deviation > 0.25\n | extend UserIdName = tostring(split(UserId, '@')[0]), UserIdUPNSuffix = tostring(split(UserId, '@')[1])\n | project-reorder StartTime, EndTime, UserAgent, UserAgentSeenCount, UserId, ClientIp, Site_Url\n | order by UserAgentSeenCount desc, UserAgent asc, UserId asc, Site_Url asc\n", + "queryFrequency": "P1D", + "queryPeriod": "P14D", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Exfiltration" + ], + "techniques": [ + "T1030" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "UserIdName" + }, + { + "identifier": "UPNSuffix", + "columnName": "UserIdUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIp" + } + ] + }, + { + "entityType": "URL", + "fieldMappings": [ + { + "identifier": "Url", + "columnName": "Site_Url" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject14').analyticRuleId14,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 14", + "parentId": "[variables('analyticRuleObject14').analyticRuleId14]", + "contentId": "[variables('analyticRuleObject14')._analyticRulecontentId14]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject14').analyticRuleVersion14]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject14')._analyticRulecontentId14]", + "contentKind": "AnalyticsRule", + "displayName": "SharePointFileOperation via devices with previously unseen user agents", + "contentProductId": "[variables('analyticRuleObject14')._analyticRulecontentProductId14]", + "id": "[variables('analyticRuleObject14')._analyticRulecontentProductId14]", + "version": "[variables('analyticRuleObject14').analyticRuleVersion14]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject15').analyticRuleTemplateSpecName15]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - sharepoint_file_transfer_above_threshold_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject15').analyticRuleVersion15]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject15')._analyticRulecontentId15]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies Office365 Sharepoint File Transfers above a certain threshold in a 15-minute time period.\nPlease note that entity mapping for arrays is not supported, so when there is a single value in an array, we will pull that value from the array as a single string to populate the entity to support entity mapping features within Sentinel. Additionally, if the array is multivalued, we will input a string to indicate this with a unique hash so that matching will not occur.", + "displayName": "Office365 Sharepoint File Transfer Above Threshold", + "enabled": false, + "query": "let threshold = 5000;\nEnrichedMicrosoft365AuditLogs\n| where Workload has_any(\"SharePoint\", \"OneDrive\") and Operation has_any(\"FileDownloaded\", \"FileSyncDownloadedFull\", \"FileSyncUploadedFull\", \"FileUploaded\")\n| summarize count_distinct_ObjectId=dcount(ObjectId), fileslist=make_set(ObjectId, 10000) by UserId, ClientIp, bin(TimeGenerated, 15m)\n| where count_distinct_ObjectId >= threshold\n| extend FileSample = iff(array_length(fileslist) == 1, tostring(fileslist[0]), strcat(\"SeeFilesListField\",\"_\", tostring(hash(tostring(fileslist)))))\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "queryFrequency": "PT15M", + "queryPeriod": "PT15M", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Exfiltration" + ], + "techniques": [ + "T1020" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIp" + } + ] + }, + { + "entityType": "File", + "fieldMappings": [ + { + "identifier": "Name", + "columnName": "FileSample" + } + ] + } + ], + "customDetails": { + "TransferCount": "count_distinct_ObjectId", + "FilesList": "fileslist" + }, + "incidentConfiguration": { + "createIncident": true, + "groupingConfiguration": { + "enabled": true, + "lookbackDuration": "5h", + "groupByEntities": [ + "Account" + ], + "matchingMethod": "Selected", + "reopenClosedIncident": false + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject15').analyticRuleId15,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 15", + "parentId": "[variables('analyticRuleObject15').analyticRuleId15]", + "contentId": "[variables('analyticRuleObject15')._analyticRulecontentId15]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject15').analyticRuleVersion15]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject15')._analyticRulecontentId15]", + "contentKind": "AnalyticsRule", + "displayName": "Office365 Sharepoint File Transfer Above Threshold", + "contentProductId": "[variables('analyticRuleObject15')._analyticRulecontentProductId15]", + "id": "[variables('analyticRuleObject15')._analyticRulecontentProductId15]", + "version": "[variables('analyticRuleObject15').analyticRuleVersion15]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject16').analyticRuleTemplateSpecName16]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Office 365 - sharepoint_file_transfer_folders_above_threshold_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject16').analyticRuleVersion16]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject16')._analyticRulecontentId16]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies Office365 Sharepoint File Transfers with a distinct folder count above a certain threshold in a 15-minute time period.\nPlease note that entity mapping for arrays is not supported, so when there is a single value in an array, we will pull that value from the array as a single string to populate the entity to support entity mapping features within Sentinel. Additionally, if the array is multivalued, we will input a string to indicate this with a unique hash so that matching will not occur.", + "displayName": "Office365 Sharepoint File Transfer Above Threshold", + "enabled": false, + "query": "let threshold = 500;\nEnrichedMicrosoft365AuditLogs\n| where Workload has_any(\"SharePoint\", \"OneDrive\") and Operation has_any(\"FileDownloaded\", \"FileSyncDownloadedFull\", \"FileSyncUploadedFull\", \"FileUploaded\")\n| extend EventSource = tostring(parse_json(tostring(AdditionalProperties)).EventSource)\n| extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent)\n| summarize count_distinct_ObjectId = dcount(ObjectId), dirlist = make_set(ObjectId, 10000) by UserId, ClientIp, UserAgent, bin(TimeGenerated, 15m)\n| where count_distinct_ObjectId >= threshold\n| extend DirSample = iff(array_length(dirlist) == 1, tostring(dirlist[0]), strcat(\"SeeDirListField\",\"_\", tostring(hash(tostring(dirlist)))))\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "queryFrequency": "PT15M", + "queryPeriod": "PT15M", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Exfiltration" + ], + "techniques": [ + "T1020" + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "identifier": "FullName", + "columnName": "UserId" + }, + { + "identifier": "Name", + "columnName": "AccountName" + }, + { + "identifier": "UPNSuffix", + "columnName": "AccountUPNSuffix" + } + ] + }, + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "ClientIp" + } + ] + }, + { + "entityType": "File", + "fieldMappings": [ + { + "identifier": "Name", + "columnName": "DirSample" + } + ] + } + ], + "customDetails": { + "TransferCount": "count_distinct_ObjectId", + "FilesList": "dirlist" + }, + "incidentConfiguration": { + "createIncident": true, + "groupingConfiguration": { + "enabled": true, + "lookbackDuration": "5h", + "groupByEntities": [ + "Account" + ], + "matchingMethod": "Selected", + "reopenClosedIncident": false + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject16').analyticRuleId16,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 16", + "parentId": "[variables('analyticRuleObject16').analyticRuleId16]", + "contentId": "[variables('analyticRuleObject16')._analyticRulecontentId16]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject16').analyticRuleVersion16]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject16')._analyticRulecontentId16]", + "contentKind": "AnalyticsRule", + "displayName": "Office365 Sharepoint File Transfer Above Threshold", + "contentProductId": "[variables('analyticRuleObject16')._analyticRulecontentProductId16]", + "id": "[variables('analyticRuleObject16')._analyticRulecontentProductId16]", + "version": "[variables('analyticRuleObject16').analyticRuleVersion16]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject17').analyticRuleTemplateSpecName17]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "SWG - Abnormal Deny Rate_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject17').analyticRuleVersion17]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject17')._analyticRulecontentId17]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies abnormal deny rate for specific source IP to destination IP based on the normal average and standard deviation learned during a configured period. This can indicate potential exfiltration, initial access, or C2, where an attacker tries to exploit the same vulnerability on machines in the organization but is being blocked by firewall rules.", + "displayName": "Detect Abnormal Deny Rate for Source to Destination IP", + "enabled": false, + "query": "let NumOfStdsThreshold = 3;\nlet LearningPeriod = 5d;\nlet BinTime = 1h;\nlet MinThreshold = 5.0;\nlet MinLearningBuckets = 5;\nlet TrafficLogs = NetworkAccessTraffic\n | where Action == 'Denied'\n | where isnotempty(DestinationIp) and isnotempty(SourceIp);\nlet LearningSrcIpDenyRate = TrafficLogs\n | where TimeGenerated between (ago(LearningPeriod + 1d) .. ago(1d))\n | summarize count() by SourceIp, bin(TimeGenerated, BinTime), DestinationIp\n | summarize LearningTimeSrcIpDenyRateAvg = avg(count_), LearningTimeSrcIpDenyRateStd = stdev(count_), LearningTimeBuckets = count() by SourceIp, DestinationIp\n | where LearningTimeBuckets > MinLearningBuckets;\nlet AlertTimeSrcIpDenyRate = TrafficLogs\n | where TimeGenerated between (ago(1h) .. now())\n | summarize AlertTimeSrcIpDenyRateCount = count() by SourceIp, DestinationIp;\nAlertTimeSrcIpDenyRate\n | join kind=leftouter (LearningSrcIpDenyRate) on SourceIp, DestinationIp\n | extend LearningThreshold = max_of(LearningTimeSrcIpDenyRateAvg + NumOfStdsThreshold * LearningTimeSrcIpDenyRateStd, MinThreshold)\n | where AlertTimeSrcIpDenyRateCount > LearningThreshold\n | project SourceIp, DestinationIp, AlertTimeSrcIpDenyRateCount, LearningThreshold \n", + "queryFrequency": "PT1H", + "queryPeriod": "PT25H", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 1, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "NetworkAccessTrafficLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "InitialAccess", + "Exfiltration", + "CommandAndControl" + ], + "entityMappings": [ + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "SourceIp" + } + ] + }, + { + "entityType": "URL", + "fieldMappings": [ + { + "identifier": "Url", + "columnName": "DestinationIp" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject17').analyticRuleId17,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 17", + "parentId": "[variables('analyticRuleObject17').analyticRuleId17]", + "contentId": "[variables('analyticRuleObject17')._analyticRulecontentId17]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject17').analyticRuleVersion17]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject17')._analyticRulecontentId17]", + "contentKind": "AnalyticsRule", + "displayName": "Detect Abnormal Deny Rate for Source to Destination IP", + "contentProductId": "[variables('analyticRuleObject17')._analyticRulecontentProductId17]", + "id": "[variables('analyticRuleObject17')._analyticRulecontentProductId17]", + "version": "[variables('analyticRuleObject17').analyticRuleVersion17]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject18').analyticRuleTemplateSpecName18]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "SWG - Abnormal Port to Protocol_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject18').analyticRuleVersion18]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject18')._analyticRulecontentId18]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies changes in the protocol used for specific destination ports, comparing the current runtime with a learned baseline. This can indicate potential protocol misuse or configuration changes.", + "displayName": "Detect Protocol Changes for Destination Ports", + "enabled": false, + "query": "let LearningPeriod = 7d;\nlet RunTime = 1d;\nlet StartLearningPeriod = ago(LearningPeriod + RunTime);\nlet EndRunTime = ago(RunTime);\nlet LearningPortToProtocol = \n NetworkAccessTraffic\n | where TimeGenerated between (StartLearningPeriod .. EndRunTime)\n | where isnotempty(DestinationPort)\n | summarize LearningTimeCount = count() by LearningTimeDstPort = DestinationPort, LearningTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn;\nlet AlertTimePortToProtocol = \n NetworkAccessTraffic\n | where TimeGenerated between (EndRunTime .. now())\n | where isnotempty(DestinationPort)\n | summarize AlertTimeCount = count() by AlertTimeDstPort = DestinationPort, AlertTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn;\nAlertTimePortToProtocol\n | join kind=leftouter (LearningPortToProtocol) on $left.AlertTimeDstPort == $right.LearningTimeDstPort and $left.SourceIp == $right.SourceIp and $left.DestinationFqdn == $right.DestinationFqdn\n | where isnull(LearningTimeProtocol) or LearningTimeProtocol != AlertTimeProtocol\n | project AlertTimeDstPort, AlertTimeProtocol, LearningTimeProtocol, SourceIp, DestinationFqdn\n | extend IPCustomEntity = SourceIp, FqdnCustomEntity = DestinationFqdn\n", + "queryFrequency": "PT1H", + "queryPeriod": "P8D", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 1, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "DefenseEvasion", + "Exfiltration", + "CommandAndControl" + ], + "entityMappings": [ + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "IPCustomEntity" + } + ] + }, + { + "entityType": "URL", + "fieldMappings": [ + { + "identifier": "Url", + "columnName": "FqdnCustomEntity" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject18').analyticRuleId18,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 18", + "parentId": "[variables('analyticRuleObject18').analyticRuleId18]", + "contentId": "[variables('analyticRuleObject18')._analyticRulecontentId18]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject18').analyticRuleVersion18]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject18')._analyticRulecontentId18]", + "contentKind": "AnalyticsRule", + "displayName": "Detect Protocol Changes for Destination Ports", + "contentProductId": "[variables('analyticRuleObject18')._analyticRulecontentProductId18]", + "id": "[variables('analyticRuleObject18')._analyticRulecontentProductId18]", + "version": "[variables('analyticRuleObject18').analyticRuleVersion18]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleObject19').analyticRuleTemplateSpecName19]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "SWG - Source IP Port Scan_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleObject19').analyticRuleVersion19]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRuleObject19')._analyticRulecontentId19]", + "apiVersion": "2023-02-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Identifies a source IP scanning multiple open ports on Global Secure Access Firewall. This can indicate malicious scanning of ports by an attacker, trying to reveal open ports in the organization that can be compromised for initial access.", + "displayName": "Detect Source IP Scanning Multiple Open Ports", + "enabled": false, + "query": "let port_scan_time = 30s;\nlet min_ports_threshold = 100;\nNetworkAccessTraffic\n| where TimeGenerated > ago(1d)\n| where Action == 'Allowed'\n| summarize PortsScanned = dcount(DestinationPort) by SourceIp, bin(TimeGenerated, port_scan_time)\n| where PortsScanned > min_ports_threshold\n| project SourceIp, PortsScanned, TimeGenerated\n", + "queryFrequency": "P1D", + "queryPeriod": "P1D", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 1, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "EnrichedMicrosoft365AuditLogs" + ], + "connectorId": "AzureActiveDirectory" + } + ], + "tactics": [ + "Discovery" + ], + "techniques": [ + "T1046" + ], + "entityMappings": [ + { + "entityType": "IP", + "fieldMappings": [ + { + "identifier": "Address", + "columnName": "SourceIp" + } + ] + }, + { + "entityType": "URL", + "fieldMappings": [ + { + "identifier": "Url", + "columnName": "Fqdn" + } + ] + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject19').analyticRuleId19,'/'))))]", + "properties": { + "description": "Global Secure Access Analytics Rule 19", + "parentId": "[variables('analyticRuleObject19').analyticRuleId19]", + "contentId": "[variables('analyticRuleObject19')._analyticRulecontentId19]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleObject19').analyticRuleVersion19]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('analyticRuleObject19')._analyticRulecontentId19]", + "contentKind": "AnalyticsRule", + "displayName": "Detect Source IP Scanning Multiple Open Ports", + "contentProductId": "[variables('analyticRuleObject19')._analyticRulecontentProductId19]", + "id": "[variables('analyticRuleObject19')._analyticRulecontentProductId19]", + "version": "[variables('analyticRuleObject19').analyticRuleVersion19]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject1').huntingQueryTemplateSpecName1]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "AnomolousUserAccessingOtherUsersMailbox_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject1').huntingQueryVersion1]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_1", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Anomalous access to other users' mailboxes", + "category": "Hunting Queries", + "query": "let starttime = todatetime('{{StartTimeISO}}');\nlet endtime = todatetime('{{EndTimeISO}}');\nlet lookback = totimespan((endtime - starttime) * 2);\n// Adjust this value to alter how many mailbox (other than their own) a user needs to access before being included in results\nlet user_threshold = 1;\n// Adjust this value to alter how many mailbox folders in other's email accounts a users needs to access before being included in results.\nlet folder_threshold = 5;\n// Exclude historical as known good (set lookback and timeframe to same value to skip this)\nEnrichedMicrosoft365AuditLogs\n| where TimeGenerated between (ago(lookback)..starttime)\n| where Operation =~ \"MailItemsAccessed\"\n| where ResultStatus =~ \"Succeeded\"\n| extend MailboxOwnerUPN = tostring(parse_json(AdditionalProperties).MailboxOwnerUPN)\n| where tolower(MailboxOwnerUPN) != tolower(UserId)\n| join kind=rightanti (\n EnrichedMicrosoft365AuditLogs\n | where TimeGenerated between (starttime..endtime)\n | where Operation =~ \"MailItemsAccessed\"\n | where ResultStatus =~ \"Succeeded\"\n | extend MailboxOwnerUPN = tostring(parse_json(AdditionalProperties).MailboxOwnerUPN)\n | where tolower(MailboxOwnerUPN) != tolower(UserId)\n) on MailboxOwnerUPN, UserId\n| where isnotempty(tostring(parse_json(AdditionalProperties).Folders))\n| mv-expand Folders = parse_json(AdditionalProperties).Folders\n| extend folders = tostring(Folders.Path)\n| extend ClientIP = iif(ClientIp startswith \"[\", extract(\"\\\\[([^\\\\]]*)\", 1, ClientIp), ClientIp)\n| extend ClientInfoString = tostring(parse_json(AdditionalProperties).ClientInfoString)\n| extend MailboxGuid = tostring(parse_json(AdditionalProperties).MailboxGuid)\n| summarize StartTime = max(TimeGenerated), EndTime = min(TimeGenerated), set_folders = make_set(folders, 100000), set_ClientInfoString = make_set(ClientInfoString, 100000), set_ClientIP = make_set(ClientIP, 100000), set_MailboxGuid = make_set(MailboxGuid, 100000), set_MailboxOwnerUPN = make_set(MailboxOwnerUPN, 100000) by UserId\n| extend folder_count = array_length(set_folders)\n| extend user_count = array_length(set_MailboxGuid)\n| where user_count > user_threshold or folder_count > folder_threshold\n| extend Reason = case(user_count > user_threshold and folder_count > folder_threshold, \"Both User and Folder Threshold Exceeded\", folder_count > folder_threshold and user_count < user_threshold, \"Folder Count Threshold Exceeded\", \"User Threshold Exceeded\")\n| sort by user_count desc\n| project-reorder UserId, user_count, folder_count, set_MailboxOwnerUPN, set_ClientIP, set_ClientInfoString, set_folders\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n| extend Account_0_Name = AccountName\n| extend Account_0_UPNSuffix = AccountUPNSuffix\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Looks for users accessing multiple other users' mailboxes or accessing multiple folders in another users mailbox." + }, + { + "name": "tactics", + "value": "Collection" + }, + { + "name": "techniques", + "value": "T1114.002" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject1')._huntingQuerycontentId1),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 1", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject1')._huntingQuerycontentId1)]", + "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject1').huntingQueryVersion1]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", + "contentKind": "HuntingQuery", + "displayName": "Anomalous access to other users' mailboxes", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject1')._huntingQuerycontentId1,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject1')._huntingQuerycontentId1,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject2').huntingQueryTemplateSpecName2]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "ExternalUserAddedRemovedInTeams_HuntVersion_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject2').huntingQueryVersion2]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_2", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "External User Added and Removed in a Short Timeframe", + "category": "Hunting Queries", + "query": "// If you want to look at user added further than 7 days ago adjust this value\n// If you want to change the timeframe of how quickly accounts need to be added and removed change this value\nlet time_delta = 1h;\nEnrichedMicrosoft365AuditLogs\n| where Workload == \"MicrosoftTeams\"\n| where Operation == \"MemberAdded\"\n| extend UPN = tostring(parse_json(tostring(AdditionalProperties)).UPN) // Assuming UPN is stored in AdditionalProperties\n| where UPN contains \"#EXT#\"\n| project TimeAdded = TimeGenerated, Operation, UPN, UserWhoAdded = UserId, TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName), TeamGuid = tostring(parse_json(tostring(AdditionalProperties)).TeamGuid)\n| join kind=innerunique (\n EnrichedMicrosoft365AuditLogs\n | where Workload == \"MicrosoftTeams\"\n | where Operation == \"MemberRemoved\"\n | extend UPN = tostring(parse_json(tostring(AdditionalProperties)).UPN) // Assuming UPN is stored in AdditionalProperties\n | where UPN contains \"#EXT#\"\n | project TimeDeleted = TimeGenerated, Operation, UPN, UserWhoDeleted = UserId, TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName), TeamGuid = tostring(parse_json(tostring(AdditionalProperties)).TeamGuid)\n) on UPN, TeamGuid\n| where TimeDeleted < (TimeAdded + time_delta)\n| project TimeAdded, TimeDeleted, UPN, UserWhoAdded, UserWhoDeleted, TeamName, TeamGuid\n| extend AccountName = tostring(split(UPN, \"@\")[0]), AccountUPNSuffix = tostring(split(UPN, \"@\")[1])\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This hunting query identifies external user accounts that are added to a Team and then removed within one hour." + }, + { + "name": "tactics", + "value": "Persistence" + }, + { + "name": "techniques", + "value": "T1136" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject2')._huntingQuerycontentId2),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 2", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject2')._huntingQuerycontentId2)]", + "contentId": "[variables('huntingQueryObject2')._huntingQuerycontentId2]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject2').huntingQueryVersion2]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject2')._huntingQuerycontentId2]", + "contentKind": "HuntingQuery", + "displayName": "External User Added and Removed in a Short Timeframe", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject2')._huntingQuerycontentId2,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject2')._huntingQuerycontentId2,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject3').huntingQueryTemplateSpecName3]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "ExternalUserFromNewOrgAddedToTeams_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject3').huntingQueryVersion3]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_3", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "External user from a new organisation added to Teams", + "category": "Hunting Queries", + "query": "let starttime = todatetime('{{StartTimeISO}}');\nlet endtime = todatetime('{{EndTimeISO}}');\nlet lookback = totimespan((endtime - starttime) * 7);\nlet known_orgs = (\n EnrichedMicrosoft365AuditLogs\n | where TimeGenerated between (ago(lookback) .. starttime)\n | where Workload == \"MicrosoftTeams\"\n | where Operation in (\"MemberAdded\", \"TeamsSessionStarted\")\n // Extract the correct UPN and parse our external organization domain\n | extend Members = parse_json(tostring(AdditionalProperties.Members))\n | extend UPN = iif(Operation == \"MemberAdded\", tostring(Members[0].UPN), UserId)\n | extend Organization = tostring(split(split(UPN, \"_\")[1], \"#\")[0])\n | where isnotempty(Organization)\n | summarize by Organization\n);\nEnrichedMicrosoft365AuditLogs\n| where TimeGenerated between (starttime .. endtime)\n| where Workload == \"MicrosoftTeams\"\n| where Operation == \"MemberAdded\"\n| extend Members = parse_json(tostring(AdditionalProperties.Members))\n| extend UPN = tostring(Members[0].UPN)\n| extend Organization = tostring(split(split(UPN, \"_\")[1], \"#\")[0])\n| where isnotempty(Organization)\n| where Organization !in (known_orgs)\n| extend AccountName = tostring(split(UPN, \"@\")[0]), AccountUPNSuffix = tostring(split(UPN, \"@\")[1])\n| extend Account_0_Name = AccountName\n| extend Account_0_UPNSuffix = AccountUPNSuffix\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This query identifies external users added to Teams where the user's domain is not one previously seen in Teams data." + }, + { + "name": "tactics", + "value": "Persistence" + }, + { + "name": "techniques", + "value": "T1136" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject3')._huntingQuerycontentId3),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 3", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject3')._huntingQuerycontentId3)]", + "contentId": "[variables('huntingQueryObject3')._huntingQuerycontentId3]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject3').huntingQueryVersion3]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject3')._huntingQuerycontentId3]", + "contentKind": "HuntingQuery", + "displayName": "External user from a new organisation added to Teams", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject3')._huntingQuerycontentId3,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject3')._huntingQuerycontentId3,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject4').huntingQueryTemplateSpecName4]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "Mail_redirect_via_ExO_transport_rule_hunting_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject4').huntingQueryVersion4]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_4", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Mail Redirect via ExO Transport Rule", + "category": "Hunting Queries", + "query": "EnrichedMicrosoft365AuditLogs\n| where Workload == \"Exchange\"\n| where Operation in (\"New-TransportRule\", \"Set-TransportRule\")\n| mv-apply DynamicParameters = todynamic(AdditionalProperties.Parameters) on (summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value)))\n| extend RuleName = case(\n Operation == \"Set-TransportRule\", ObjectId,\n Operation == \"New-TransportRule\", ParsedParameters.Name,\n \"Unknown\")\n| mv-expand ExpandedParameters = todynamic(AdditionalProperties.Parameters)\n| where ExpandedParameters.Name in (\"BlindCopyTo\", \"RedirectMessageTo\") and isnotempty(ExpandedParameters.Value)\n| extend RedirectTo = ExpandedParameters.Value\n| extend ClientIPValues = extract_all(@'\\[?(::ffff:)?(?P(\\d+\\.\\d+\\.\\d+\\.\\d+)|[^\\]]+)\\]?([-:](?P\\d+))?', dynamic([\"IPAddress\", \"Port\"]), ClientIp)[0]\n| project TimeGenerated, RedirectTo, IPAddress = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1]), UserId, Operation, RuleName, AdditionalProperties\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Identifies when Exchange Online transport rule is configured to forward emails.\nThis could be an adversary mailbox configured to collect mail from multiple user accounts." + }, + { + "name": "tactics", + "value": "Collection,Exfiltration" + }, + { + "name": "techniques", + "value": "T1114,T1020" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject4')._huntingQuerycontentId4),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 4", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject4')._huntingQuerycontentId4)]", + "contentId": "[variables('huntingQueryObject4')._huntingQuerycontentId4]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject4').huntingQueryVersion4]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject4')._huntingQuerycontentId4]", + "contentKind": "HuntingQuery", + "displayName": "Mail Redirect via ExO Transport Rule", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject4')._huntingQuerycontentId4,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject4')._huntingQuerycontentId4,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject5').huntingQueryTemplateSpecName5]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "MultiTeamBot_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject5').huntingQueryVersion5]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_5", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Bots added to multiple teams", + "category": "Hunting Queries", + "query": "// Adjust these thresholds to suit your environment.\nlet threshold = 2;\nlet time_threshold = timespan(5m);\nEnrichedMicrosoft365AuditLogs\n | where Workload == \"MicrosoftTeams\"\n | where Operation == \"BotAddedToTeam\"\n | extend TeamName = tostring(parse_json(tostring(AdditionalProperties)).TeamName)\n | summarize Start = max(TimeGenerated), End = min(TimeGenerated), Teams = make_set(TeamName, 10000) by UserId\n | extend CountOfTeams = array_length(Teams)\n | extend TimeDelta = End - Start\n | where CountOfTeams > threshold\n | where TimeDelta <= time_threshold\n | project Start, End, Teams, CountOfTeams, UserId\n | extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n | extend Account_0_Name = AccountName\n | extend Account_0_UPNSuffix = AccountUPNSuffix\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This hunting query helps identify bots added to multiple Teams in a short space of time." + }, + { + "name": "tactics", + "value": "Persistence,Collection" + }, + { + "name": "techniques", + "value": "T1176,T1119" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject5')._huntingQuerycontentId5),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 5", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject5')._huntingQuerycontentId5)]", + "contentId": "[variables('huntingQueryObject5')._huntingQuerycontentId5]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject5').huntingQueryVersion5]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject5')._huntingQuerycontentId5]", + "contentKind": "HuntingQuery", + "displayName": "Bots added to multiple teams", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject5')._huntingQuerycontentId5,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject5')._huntingQuerycontentId5,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject6').huntingQueryTemplateSpecName6]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "MultiTeamOwner_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject6').huntingQueryVersion6]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_6", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "User made Owner of multiple teams", + "category": "Hunting Queries", + "query": "// Adjust this value to change how many teams a user is made owner of before detecting\nlet max_owner_count = 3;\n// Identify users who have been made owner of multiple Teams\nlet high_owner_count = (\n EnrichedMicrosoft365AuditLogs\n | where Workload == \"MicrosoftTeams\"\n | where Operation == \"MemberRoleChanged\"\n | extend Member = tostring(UserId)\n | extend NewRole = toint(parse_json(tostring(AdditionalProperties)).Role)\n | where NewRole == 2\n | summarize TeamCount = dcount(ObjectId) by Member\n | where TeamCount > max_owner_count\n | project Member\n);\nEnrichedMicrosoft365AuditLogs\n| where Workload == \"MicrosoftTeams\"\n| where Operation == \"MemberRoleChanged\"\n| extend Member = tostring(UserId)\n| extend NewRole = toint(parse_json(tostring(AdditionalProperties)).Role)\n| where NewRole == 2\n| where Member in (high_owner_count)\n| extend AccountName = tostring(split(Member, \"@\")[0]), AccountUPNSuffix = tostring(split(Member, \"@\")[1])\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This hunting query identifies users who have been made Owner of multiple Teams." + }, + { + "name": "tactics", + "value": "PrivilegeEscalation" + }, + { + "name": "techniques", + "value": "T1078" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject6')._huntingQuerycontentId6),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 6", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject6')._huntingQuerycontentId6)]", + "contentId": "[variables('huntingQueryObject6')._huntingQuerycontentId6]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject6').huntingQueryVersion6]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject6')._huntingQuerycontentId6]", + "contentKind": "HuntingQuery", + "displayName": "User made Owner of multiple teams", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject6')._huntingQuerycontentId6,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject6')._huntingQuerycontentId6,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject7').huntingQueryTemplateSpecName7]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "MultipleTeamsDeletes_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject7').huntingQueryVersion7]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_7", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Multiple Teams deleted by a single user", + "category": "Hunting Queries", + "query": "// Adjust this value to change how many Teams should be deleted before including\nlet max_delete = 3;\nlet deleting_users = (\n EnrichedMicrosoft365AuditLogs\n | where Workload == \"MicrosoftTeams\"\n | where Operation == \"TeamDeleted\"\n | summarize count_ = count() by UserId\n | where count_ > max_delete\n | project UserId\n);\nEnrichedMicrosoft365AuditLogs\n| where Workload == \"MicrosoftTeams\"\n| where Operation == \"TeamDeleted\"\n| where UserId in (deleting_users)\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n| extend Account_0_Name = AccountName\n| extend Account_0_UPNSuffix = AccountUPNSuffix\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This hunting query identifies where multiple Teams have been deleted by a single user in a short timeframe." + }, + { + "name": "tactics", + "value": "Impact" + }, + { + "name": "techniques", + "value": "T1485,T1489" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject7')._huntingQuerycontentId7),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 7", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject7')._huntingQuerycontentId7)]", + "contentId": "[variables('huntingQueryObject7')._huntingQuerycontentId7]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject7').huntingQueryVersion7]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject7')._huntingQuerycontentId7]", + "contentKind": "HuntingQuery", + "displayName": "Multiple Teams deleted by a single user", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject7')._huntingQuerycontentId7,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject7')._huntingQuerycontentId7,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject8').huntingQueryTemplateSpecName8]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "NewBotAddedToTeams_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject8').huntingQueryVersion8]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_8", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Previously Unseen Bot or Application Added to Teams", + "category": "Hunting Queries", + "query": "let starttime = todatetime('{{StartTimeISO}}');\nlet endtime = todatetime('{{EndTimeISO}}');\nlet lookback = starttime - 14d;\nlet historical_bots = \n EnrichedMicrosoft365AuditLogs\n | where TimeGenerated between (lookback .. starttime)\n | where Workload == \"MicrosoftTeams\"\n | extend AddonName = tostring(parse_json(tostring(AdditionalProperties)).AddonName)\n | where isnotempty(AddonName)\n | distinct AddonName;\nEnrichedMicrosoft365AuditLogs\n| where TimeGenerated between (starttime .. endtime)\n| where Workload == \"MicrosoftTeams\"\n| extend AddonName = tostring(parse_json(tostring(AdditionalProperties)).AddonName)\n| where AddonName !in (historical_bots)\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This hunting query helps identify new, and potentially unapproved applications or bots being added to Teams." + }, + { + "name": "tactics", + "value": "Persistence,Collection" + }, + { + "name": "techniques", + "value": "T1176,T1119" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject8')._huntingQuerycontentId8),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 8", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject8')._huntingQuerycontentId8)]", + "contentId": "[variables('huntingQueryObject8')._huntingQuerycontentId8]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject8').huntingQueryVersion8]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject8')._huntingQuerycontentId8]", + "contentKind": "HuntingQuery", + "displayName": "Previously Unseen Bot or Application Added to Teams", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject8')._huntingQuerycontentId8,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject8')._huntingQuerycontentId8,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject9').huntingQueryTemplateSpecName9]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "New_WindowsReservedFileNamesOnOfficeFileServices_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject9').huntingQueryVersion9]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_9", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "New Windows Reserved Filenames staged on Office file services", + "category": "Hunting Queries", + "query": "let starttime = todatetime('{{StartTimeISO}}');\nlet endtime = todatetime('{{EndTimeISO}}');\nlet lookback = totimespan((endtime - starttime) * 7);\nlet Reserved = dynamic(['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9']);\nEnrichedMicrosoft365AuditLogs\n| where TimeGenerated between (starttime .. endtime)\n| extend FileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)\n| extend ClientUserAgent = tostring(parse_json(tostring(AdditionalProperties)).ClientUserAgent)\n| extend SiteUrl = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl)\n| where isnotempty(ObjectId)\n| where ObjectId !~ FileName\n| where ObjectId in (Reserved) or FileName in (Reserved)\n| where ClientUserAgent !has \"Mac OS\"\n| project TimeGenerated, Id, Workload, RecordType, Operation, UserType, UserKey, UserId, ClientIp, ClientUserAgent, SiteUrl, ObjectId, FileName\n| join kind=leftanti (\n EnrichedMicrosoft365AuditLogs\n | where TimeGenerated between (ago(lookback) .. starttime)\n | extend FileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)\n | extend ClientUserAgent = tostring(parse_json(tostring(AdditionalProperties)).ClientUserAgent)\n | extend SiteUrl = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl)\n | where isnotempty(ObjectId)\n | where ObjectId !~ FileName\n | where ObjectId in (Reserved) or FileName in (Reserved)\n | where ClientUserAgent !has \"Mac OS\"\n | summarize PrevSeenCount = count() by ObjectId, UserId, FileName\n) on ObjectId\n| extend SiteUrlUserFolder = tolower(split(SiteUrl, '/')[-2])\n| extend UserIdUserFolderFormat = tolower(replace_regex(UserId, '@|\\\\.', '_'))\n| extend UserIdDiffThanUserFolder = iff(SiteUrl has '/personal/' and SiteUrlUserFolder != UserIdUserFolderFormat, true, false)\n| summarize TimeGenerated = make_list(TimeGenerated, 100000), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Operations = make_list(Operation, 100000), UserAgents = make_list(ClientUserAgent, 100000),\n Ids = make_list(Id, 100000), SourceRelativeUrls = make_list(ObjectId, 100000), FileNames = make_list(FileName, 100000)\n by Workload, RecordType, UserType, UserKey, UserId, ClientIp, SiteUrl, ObjectId, SiteUrlUserFolder, UserIdUserFolderFormat, UserIdDiffThanUserFolder\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n| extend IP_0_Address = ClientIp\n| extend Account_0_Name = AccountName\n| extend Account_0_UPNSuffix = AccountUPNSuffix\n| extend URL_0_Url = SiteUrl\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This identifies new Windows Reserved Filenames on Office services like SharePoint and OneDrive in the past 7 days. It also detects when a user uploads these files to another user's workspace, which may indicate malicious activity." + }, + { + "name": "tactics", + "value": "CommandAndControl" + }, + { + "name": "techniques", + "value": "T1105" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject9')._huntingQuerycontentId9),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 9", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject9')._huntingQuerycontentId9)]", + "contentId": "[variables('huntingQueryObject9')._huntingQuerycontentId9]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject9').huntingQueryVersion9]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject9')._huntingQuerycontentId9]", + "contentKind": "HuntingQuery", + "displayName": "New Windows Reserved Filenames staged on Office file services", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject9')._huntingQuerycontentId9,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject9')._huntingQuerycontentId9,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject10').huntingQueryTemplateSpecName10]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "OfficeMailForwarding_hunting_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject10').huntingQueryVersion10]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_10", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Office Mail Forwarding - Hunting Version", + "category": "Hunting Queries", + "query": "EnrichedMicrosoft365AuditLogs\n| where Workload == \"Exchange\"\n| where (Operation == \"Set-Mailbox\" and tostring(parse_json(tostring(AdditionalProperties))) contains 'ForwardingSmtpAddress') \n or (Operation in ('New-InboxRule', 'Set-InboxRule') and (tostring(parse_json(tostring(AdditionalProperties))) contains 'ForwardTo' or tostring(parse_json(tostring(AdditionalProperties))) contains 'RedirectTo'))\n| extend parsed = parse_json(tostring(AdditionalProperties))\n| extend fwdingDestination_initial = iif(Operation == \"Set-Mailbox\", tostring(parsed.ForwardingSmtpAddress), coalesce(tostring(parsed.ForwardTo), tostring(parsed.RedirectTo)))\n| where isnotempty(fwdingDestination_initial)\n| extend fwdingDestination = iff(fwdingDestination_initial has \"smtp\", (split(fwdingDestination_initial, \":\")[1]), fwdingDestination_initial)\n| parse fwdingDestination with * '@' ForwardedtoDomain \n| parse UserId with *'@' UserDomain\n| extend subDomain = ((split(strcat(tostring(split(UserDomain, '.')[-2]), '.', tostring(split(UserDomain, '.')[-1])), '.'))[0])\n| where ForwardedtoDomain !contains subDomain\n| extend Result = iff(ForwardedtoDomain != UserDomain, \"Mailbox rule created to forward to External Domain\", \"Forward rule for Internal domain\")\n| extend ClientIPAddress = case(ClientIp has \".\", tostring(split(ClientIp, \":\")[0]), ClientIp has \"[\", tostring(trim_start(@'[[]', tostring(split(ClientIp, \"]\")[0]))), ClientIp)\n| extend Port = case(\n ClientIp has \".\",\n (split(ClientIp, \":\")[1]),\n ClientIp has \"[\",\n tostring(split(ClientIp, \"]:\")[1]),\n ClientIp\n )\n| project\n TimeGenerated,\n UserId,\n UserDomain,\n subDomain,\n Operation,\n ForwardedtoDomain,\n ClientIPAddress,\n Result,\n Port,\n ObjectId,\n fwdingDestination,\n AdditionalProperties\n| extend\n AccountName = tostring(split(UserId, \"@\")[0]),\n AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n| extend Host = tostring(parse_json(tostring(AdditionalProperties)).OriginatingServer)\n| extend HostName = tostring(split(Host, \".\")[0])\n| extend DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))\n| extend Account_0_Name = AccountName\n| extend Account_0_UPNSuffix = AccountUPNSuffix\n| extend IP_0_Address = ClientIPAddress\n| extend Host_0_HostName = HostName\n| extend Host_0_DnsDomain = DnsDomain\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Adversaries often abuse email-forwarding rules to monitor victim activities, steal information, and gain intelligence on the victim or their organization. This query highlights cases where user mail is being forwarded, including to external domains." + }, + { + "name": "tactics", + "value": "Collection,Exfiltration" + }, + { + "name": "techniques", + "value": "T1114,T1020" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject10')._huntingQuerycontentId10),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 10", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject10')._huntingQuerycontentId10)]", + "contentId": "[variables('huntingQueryObject10')._huntingQuerycontentId10]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject10').huntingQueryVersion10]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject10')._huntingQuerycontentId10]", + "contentKind": "HuntingQuery", + "displayName": "Office Mail Forwarding - Hunting Version", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject10')._huntingQuerycontentId10,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject10')._huntingQuerycontentId10,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject11').huntingQueryTemplateSpecName11]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "TeamsFilesUploaded_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject11').huntingQueryVersion11]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_11", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Files uploaded to teams and access summary", + "category": "Hunting Queries", + "query": "EnrichedMicrosoft365AuditLogs\n| where RecordType == \"SharePointFileOperation\"\n| where Operation == \"FileUploaded\"\n| where UserId != \"app@sharepoint\"\n| where ObjectId has \"Microsoft Teams Chat Files\"\n| extend SourceFileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)\n| join kind=leftouter (\n EnrichedMicrosoft365AuditLogs\n | where RecordType == \"SharePointFileOperation\"\n | where Operation == \"FileDownloaded\" or Operation == \"FileAccessed\"\n | where UserId != \"app@sharepoint\"\n | where ObjectId has \"Microsoft Teams Chat Files\"\n | extend UserId1 = UserId, ClientIp1 = ClientIp\n) on ObjectId\n| extend userBag = bag_pack(\"UserId1\", UserId1, \"ClientIp1\", ClientIp1)\n| summarize AccessedBy = make_bag(userBag), make_set(UserId1, 10000) by bin(TimeGenerated, 1h), UserId, ObjectId, SourceFileName\n| extend NumberOfUsersAccessed = array_length(bag_keys(AccessedBy))\n| project timestamp = TimeGenerated, UserId, FileLocation = ObjectId, FileName = SourceFileName, AccessedBy, NumberOfUsersAccessed\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n| extend Account_0_Name = AccountName\n| extend Account_0_UPNSuffix = AccountUPNSuffix\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This hunting query identifies files uploaded to SharePoint via a Teams chat and\nsummarizes users and IP addresses that have accessed these files. This allows for \nidentification of anomalous file sharing patterns." + }, + { + "name": "tactics", + "value": "InitialAccess,Exfiltration" + }, + { + "name": "techniques", + "value": "T1199,T1102,T1078" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject11')._huntingQuerycontentId11),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 11", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject11')._huntingQuerycontentId11)]", + "contentId": "[variables('huntingQueryObject11')._huntingQuerycontentId11]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject11').huntingQueryVersion11]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject11')._huntingQuerycontentId11]", + "contentKind": "HuntingQuery", + "displayName": "Files uploaded to teams and access summary", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject11')._huntingQuerycontentId11,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject11')._huntingQuerycontentId11,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject12').huntingQueryTemplateSpecName12]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "UserAddToTeamsAndUploadsFile_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject12').huntingQueryVersion12]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_12", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "User added to Teams and immediately uploads file", + "category": "Hunting Queries", + "query": "let threshold = 1m;\nlet MemberAddedEvents = EnrichedMicrosoft365AuditLogs\n | where Workload == \"MicrosoftTeams\"\n | where Operation == \"MemberAdded\"\n | extend TeamName = tostring(parse_json(AdditionalProperties).TeamName)\n | project TimeGenerated, UploaderID = UserId, TeamName;\nlet FileUploadEvents = EnrichedMicrosoft365AuditLogs\n | where RecordType == \"SharePointFileOperation\"\n | where ObjectId has \"Microsoft Teams Chat Files\"\n | where Operation == \"FileUploaded\"\n | extend SourceFileName = tostring(parse_json(AdditionalProperties).SourceFileName)\n | project UploadTime = TimeGenerated, UploaderID = UserId, FileLocation = ObjectId, SourceFileName;\nMemberAddedEvents\n | join kind=inner (FileUploadEvents) on UploaderID\n | where UploadTime > TimeGenerated and UploadTime < TimeGenerated + threshold\n | extend timestamp = TimeGenerated, AccountCustomEntity = UploaderID\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This hunting query identifies users who are added to a Teams Channel or Teams chat\nand within 1 minute of being added upload a file via the chat. This might be\nan indicator of suspicious activity." + }, + { + "name": "tactics", + "value": "InitialAccess" + }, + { + "name": "techniques", + "value": "T1566" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject12')._huntingQuerycontentId12),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 12", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject12')._huntingQuerycontentId12)]", + "contentId": "[variables('huntingQueryObject12')._huntingQuerycontentId12]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject12').huntingQueryVersion12]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject12')._huntingQuerycontentId12]", + "contentKind": "HuntingQuery", + "displayName": "User added to Teams and immediately uploads file", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject12')._huntingQuerycontentId12,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject12')._huntingQuerycontentId12,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject13').huntingQueryTemplateSpecName13]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "WindowsReservedFileNamesOnOfficeFileServices_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject13').huntingQueryVersion13]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_13", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Windows Reserved Filenames Staged on Office File Services", + "category": "Hunting Queries", + "query": "// Reserved FileNames/Extension for Windows\nlet Reserved = dynamic(['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9']);\nEnrichedMicrosoft365AuditLogs\n| extend SourceFileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)\n| extend UserAgent = tostring(parse_json(tostring(AdditionalProperties)).UserAgent)\n| extend Site_Url = tostring(parse_json(tostring(AdditionalProperties)).SiteUrl)\n| where isnotempty(ObjectId)\n| where ObjectId in (Reserved) or SourceFileName in (Reserved)\n| where UserAgent !has \"Mac OS\"\n| extend SiteUrlUserFolder = tolower(split(Site_Url, '/')[-2])\n| extend UserIdUserFolderFormat = tolower(replace_regex(UserId, '@|\\\\.', '_'))\n// identify when UserId is not a match to the specific site url personal folder reference\n| extend UserIdDiffThanUserFolder = iff(Site_Url has '/personal/' and SiteUrlUserFolder != UserIdUserFolderFormat, true, false)\n| summarize TimeGenerated = make_list(TimeGenerated, 100000), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Operations = make_list(Operation, 100000), UserAgents = make_list(UserAgent, 100000), ObjectIds = make_list(Id, 100000), SourceRelativeUrls = make_list(ObjectId, 100000), FileNames = make_list(SourceFileName, 100000)\nby Workload, RecordType, UserType, UserKey, UserId, ClientIp, Site_Url, ObjectId, SiteUrlUserFolder, UserIdUserFolderFormat, UserIdDiffThanUserFolder\n// Use mvexpand on any list items and you can expand out the exact time and other metadata about the hit\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n| extend IP_0_Address = ClientIp\n| extend Account_0_Name = AccountName\n| extend Account_0_UPNSuffix = AccountUPNSuffix\n| extend URL_0_Url = Site_Url\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This identifies Windows Reserved Filenames on Office services like SharePoint and OneDrive. It also detects when a user uploads these files to another user's workspace, which may indicate malicious activity." + }, + { + "name": "tactics", + "value": "CommandAndControl" + }, + { + "name": "techniques", + "value": "T1105" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject13')._huntingQuerycontentId13),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 13", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject13')._huntingQuerycontentId13)]", + "contentId": "[variables('huntingQueryObject13')._huntingQuerycontentId13]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject13').huntingQueryVersion13]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject13')._huntingQuerycontentId13]", + "contentKind": "HuntingQuery", + "displayName": "Windows Reserved Filenames Staged on Office File Services", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject13')._huntingQuerycontentId13,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject13')._huntingQuerycontentId13,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject14').huntingQueryTemplateSpecName14]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "double_file_ext_exes_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject14').huntingQueryVersion14]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_14", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Exes with double file extension and access summary", + "category": "Hunting Queries", + "query": "let known_ext = dynamic([\"lnk\", \"log\", \"option\", \"config\", \"manifest\", \"partial\"]);\nlet excluded_users = dynamic([\"app@sharepoint\"]);\nEnrichedMicrosoft365AuditLogs\n| where RecordType == \"SharePointFileOperation\" and isnotempty(ObjectId)\n| where ObjectId has \".exe.\" \n and not(ObjectId endswith \".lnk\") \n and not(ObjectId endswith \".log\") \n and not(ObjectId endswith \".option\") \n and not(ObjectId endswith \".config\") \n and not(ObjectId endswith \".manifest\") \n and not(ObjectId endswith \".partial\")\n| extend Extension = extract(\"[^.]*\\\\.[^.]*$\", 0, ObjectId)\n| extend SourceFileName = tostring(parse_json(tostring(AdditionalProperties)).SourceFileName)\n| join kind=leftouter (\n EnrichedMicrosoft365AuditLogs\n | where RecordType == \"SharePointFileOperation\" and (Operation == \"FileDownloaded\" or Operation == \"FileAccessed\")\n | where not(ObjectId endswith \".lnk\") \n and not(ObjectId endswith \".log\") \n and not(ObjectId endswith \".option\") \n and not(ObjectId endswith \".config\") \n and not(ObjectId endswith \".manifest\") \n and not(ObjectId endswith \".partial\")\n) on ObjectId\n| where UserId1 !in (excluded_users)\n| extend userBag = bag_pack(\"UserId\", UserId1, \"ClientIp\", ClientIp1)\n| summarize make_set(UserId1, 10000), userBag = make_bag(userBag), UploadTime = max(TimeGenerated) by UserId, ObjectId, SourceFileName, Extension\n| extend NumberOfUsers = array_length(bag_keys(userBag))\n| project UploadTime, Uploader = UserId, FileLocation = ObjectId, FileName = SourceFileName, AccessedBy = userBag, Extension, NumberOfUsers\n| extend UploaderName = tostring(split(Uploader, \"@\")[0]), UploaderUPNSuffix = tostring(split(Uploader, \"@\")[1])\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Provides a summary of executable files with double file extensions in SharePoint \n and the users and IP addresses that have accessed them." + }, + { + "name": "tactics", + "value": "DefenseEvasion" + }, + { + "name": "techniques", + "value": "T1036" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject14')._huntingQuerycontentId14),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 14", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject14')._huntingQuerycontentId14)]", + "contentId": "[variables('huntingQueryObject14')._huntingQuerycontentId14]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject14').huntingQueryVersion14]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject14')._huntingQuerycontentId14]", + "contentKind": "HuntingQuery", + "displayName": "Exes with double file extension and access summary", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject14')._huntingQuerycontentId14,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject14')._huntingQuerycontentId14,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject15').huntingQueryTemplateSpecName15]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "new_adminaccountactivity_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject15').huntingQueryVersion15]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_15", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "New Admin Account Activity Seen Which Was Not Seen Historically", + "category": "Hunting Queries", + "query": "let starttime = todatetime('{{StartTimeISO}}');\nlet endtime = todatetime('{{EndTimeISO}}');\nlet lookback = starttime - 14d;\nlet historicalActivity =\n EnrichedMicrosoft365AuditLogs\n | where TimeGenerated between (lookback .. starttime)\n | where RecordType == \"ExchangeAdmin\" and UserType in (\"Admin\", \"DcAdmin\")\n | summarize historicalCount = count() by UserId;\nlet recentActivity = EnrichedMicrosoft365AuditLogs\n | where TimeGenerated between (starttime .. endtime)\n | where UserType in (\"Admin\", \"DcAdmin\")\n | summarize recentCount = count() by UserId;\nrecentActivity\n| join kind=leftanti (historicalActivity) on UserId\n| project UserId, recentCount\n| join kind=rightsemi (\n EnrichedMicrosoft365AuditLogs\n | where TimeGenerated between (starttime .. endtime)\n | where RecordType == \"ExchangeAdmin\" \n | where UserType in (\"Admin\", \"DcAdmin\")\n ) on UserId\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by RecordType, Operation, UserType, UserId, ResultStatus\n| extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId)\n| extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '')\n| extend AccountName = iff(UserId contains '\\\\', tostring(split(UserId, '\\\\')[1]), AccountName)\n| extend AccountNTDomain = iff(UserId contains '\\\\', tostring(split(UserId, '\\\\')[0]), '')\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "This will help you discover any new admin account activity which was seen and were not seen historically.\nAny new accounts seen in the results can be validated and investigated for any suspicious activities." + }, + { + "name": "tactics", + "value": "PrivilegeEscalation,Collection" + }, + { + "name": "techniques", + "value": "T1078,T1114" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject15')._huntingQuerycontentId15),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 15", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject15')._huntingQuerycontentId15)]", + "contentId": "[variables('huntingQueryObject15')._huntingQuerycontentId15]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject15').huntingQueryVersion15]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject15')._huntingQuerycontentId15]", + "contentKind": "HuntingQuery", + "displayName": "New Admin Account Activity Seen Which Was Not Seen Historically", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject15')._huntingQuerycontentId15,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject15')._huntingQuerycontentId15,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject16').huntingQueryTemplateSpecName16]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "new_sharepoint_downloads_by_IP_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject16').huntingQueryVersion16]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_16", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "SharePointFileOperation via previously unseen IPs", + "category": "Hunting Queries", + "query": "let starttime = todatetime('{{StartTimeISO}}');\nlet endtime = todatetime('{{EndTimeISO}}');\nlet lookback = starttime - 14d;\nlet BLOCK_THRESHOLD = 1.0;\nlet HighBlockRateASNs =\n SigninLogs\n | where TimeGenerated > lookback\n | where isnotempty(AutonomousSystemNumber)\n | summarize make_set(IPAddress), TotalIps = dcount(IPAddress), BlockedSignins = countif(ResultType == \"50053\"), TotalSignins = count() by AutonomousSystemNumber\n | extend BlockRatio = 1.00 * BlockedSignins / TotalSignins\n | where BlockRatio >= BLOCK_THRESHOLD\n | distinct AutonomousSystemNumber;\nlet ASNIPs =\n SigninLogs\n | where TimeGenerated > lookback\n | where AutonomousSystemNumber in (HighBlockRateASNs)\n | distinct IPAddress, AutonomousSystemNumber;\nEnrichedMicrosoft365AuditLogs\n| where TimeGenerated between (starttime .. endtime)\n| where RecordType == \"SharePointFileOperation\"\n| where Operation in (\"FileDownloaded\", \"FileUploaded\")\n| where ClientIp in (ASNIPs)\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), RecentFileActivities = count() by ClientIp\n| extend IP_0_Address = ClientIp\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Shows SharePoint upload/download volume by IPs with high-risk ASNs. New IPs with volume spikes may be unauthorized and exfiltrating documents." + }, + { + "name": "tactics", + "value": "Exfiltration" + }, + { + "name": "techniques", + "value": "T1030" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject16')._huntingQuerycontentId16),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 16", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject16')._huntingQuerycontentId16)]", + "contentId": "[variables('huntingQueryObject16')._huntingQuerycontentId16]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject16').huntingQueryVersion16]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject16')._huntingQuerycontentId16]", + "contentKind": "HuntingQuery", + "displayName": "SharePointFileOperation via previously unseen IPs", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject16')._huntingQuerycontentId16,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject16')._huntingQuerycontentId16,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject17').huntingQueryTemplateSpecName17]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "new_sharepoint_downloads_by_UserAgent_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject17').huntingQueryVersion17]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_17", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "SharePointFileOperation via devices with previously unseen user agents", + "category": "Hunting Queries", + "query": "let starttime = todatetime('{{StartTimeISO}}');\nlet endtime = todatetime('{{EndTimeISO}}');\nlet lookback = starttime - 14d;\nlet MINIMUM_BLOCKS = 10;\nlet SUCCESS_THRESHOLD = 0.2;\nlet HistoricalActivity = \n SigninLogs\n | where TimeGenerated > lookback\n | where isnotempty(ClientAppUsed)\n | summarize SuccessfulSignins = countif(ResultType == \"0\"), BlockedSignins = countif(ResultType == \"50053\") by ClientAppUsed\n | extend SuccessBlockRatio = 1.00 * SuccessfulSignins / BlockedSignins\n | where SuccessBlockRatio < SUCCESS_THRESHOLD\n | where BlockedSignins > MINIMUM_BLOCKS;\nEnrichedMicrosoft365AuditLogs\n| where TimeGenerated between (starttime .. endtime)\n| where RecordType == \"SharePointFileOperation\"\n| where Operation in (\"FileDownloaded\", \"FileUploaded\")\n| extend ClientAppUsed = tostring(parse_json(AdditionalProperties).UserAgent)\n| extend SiteUrl = tostring(parse_json(AdditionalProperties).SiteUrl)\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), RecentFileActivities = count() by ClientAppUsed, UserId, ClientIp, SiteUrl\n| join kind=innerunique (HistoricalActivity) on ClientAppUsed\n| project-away ClientAppUsed1\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n| extend IP_0_Address = ClientIp\n| extend Account_0_Name = AccountName\n| extend Account_0_UPNSuffix = AccountUPNSuffix\n| extend URL_0_Url = SiteUrl\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Tracking via user agent is one way to differentiate between types of connecting device.\nIn homogeneous enterprise environments the user agent associated with an attacker device may stand out as unusual." + }, + { + "name": "tactics", + "value": "Exfiltration" + }, + { + "name": "techniques", + "value": "T1030" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject17')._huntingQuerycontentId17),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 17", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject17')._huntingQuerycontentId17)]", + "contentId": "[variables('huntingQueryObject17')._huntingQuerycontentId17]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject17').huntingQueryVersion17]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject17')._huntingQuerycontentId17]", + "contentKind": "HuntingQuery", + "displayName": "SharePointFileOperation via devices with previously unseen user agents", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject17')._huntingQuerycontentId17,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject17')._huntingQuerycontentId17,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject18').huntingQueryTemplateSpecName18]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "nonowner_MailboxLogin_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject18').huntingQueryVersion18]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_18", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Non-owner mailbox login activity", + "category": "Hunting Queries", + "query": "EnrichedMicrosoft365AuditLogs\n| where Workload == \"Exchange\"\n| where Operation == \"MailboxLogin\"\n| extend Logon_Type = tostring(parse_json(tostring(AdditionalProperties)).LogonType)\n| extend MailboxOwnerUPN = tostring(parse_json(tostring(AdditionalProperties)).MailboxOwnerUPN)\n| where Logon_Type != \"Owner\"\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by Operation, UserType, UserId, MailboxOwnerUPN, Logon_Type, ClientIp\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n| extend IP_0_Address = ClientIp\n| extend Account_0_Name = AccountName\n| extend Account_0_UPNSuffix = AccountUPNSuffix\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Finds non-owner mailbox access by admin/delegate permissions. Whitelist valid users and check others for unauthorized access." + }, + { + "name": "tactics", + "value": "Collection,Exfiltration" + }, + { + "name": "techniques", + "value": "T1114,T1020" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject18')._huntingQuerycontentId18),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 18", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject18')._huntingQuerycontentId18)]", + "contentId": "[variables('huntingQueryObject18')._huntingQuerycontentId18]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject18').huntingQueryVersion18]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject18')._huntingQuerycontentId18]", + "contentKind": "HuntingQuery", + "displayName": "Non-owner mailbox login activity", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject18')._huntingQuerycontentId18,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject18')._huntingQuerycontentId18,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject19').huntingQueryTemplateSpecName19]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "powershell_or_nonbrowser_MailboxLogin_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject19').huntingQueryVersion19]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_19", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "PowerShell or non-browser mailbox login activity", + "category": "Hunting Queries", + "query": "EnrichedMicrosoft365AuditLogs\n| where Workload == \"Exchange\" and Operation == \"MailboxLogin\"\n| extend ClientApplication = tostring(parse_json(AdditionalProperties).ClientInfoString)\n| where ClientApplication == \"Client=Microsoft.Exchange.Powershell; Microsoft WinRM Client\"\n| extend TenantName = tostring(parse_json(AdditionalProperties).TenantName)\n| extend MailboxOwner = tostring(parse_json(AdditionalProperties).MailboxOwnerUPN)\n| extend LogonType = tostring(parse_json(AdditionalProperties).LogonType)\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by Operation, TenantName, UserType, UserId, MailboxOwner, LogonType, ClientApplication\n| extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId)\n| extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '')\n| extend AccountName = iff(UserId contains '\\\\', tostring(split(UserId, '\\\\')[1]), AccountName)\n| extend AccountNTDomain = iff(UserId contains '\\\\', tostring(split(UserId, '\\\\')[0]), '')\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Detects mailbox login from Exchange PowerShell. All accounts can use it by default, but admins can change it. Whitelist benign activities." + }, + { + "name": "tactics", + "value": "Execution,Persistence,Collection" + }, + { + "name": "techniques", + "value": "T1059,T1098,T1114" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject19')._huntingQuerycontentId19),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 19", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject19')._huntingQuerycontentId19)]", + "contentId": "[variables('huntingQueryObject19')._huntingQuerycontentId19]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject19').huntingQueryVersion19]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject19')._huntingQuerycontentId19]", + "contentKind": "HuntingQuery", + "displayName": "PowerShell or non-browser mailbox login activity", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject19')._huntingQuerycontentId19,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject19')._huntingQuerycontentId19,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject20').huntingQueryTemplateSpecName20]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "sharepoint_downloads_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject20').huntingQueryVersion20]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_20", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "SharePoint File Operation via Client IP with Previously Unseen User Agents", + "category": "Hunting Queries", + "query": "let starttime = todatetime('{{StartTimeISO}}');\nlet endtime = todatetime('{{EndTimeISO}}');\nlet lookback = starttime - 14d;\nlet historicalUA = EnrichedMicrosoft365AuditLogs\n| where RecordType == \"SharePointFileOperation\"\n| where Operation in (\"FileDownloaded\", \"FileUploaded\")\n| where TimeGenerated between(lookback..starttime)\n| extend ClientApplication = tostring(parse_json(AdditionalProperties).UserAgent)\n| summarize by ClientIp, ClientApplication;\nlet recentUA = EnrichedMicrosoft365AuditLogs\n| where RecordType == \"SharePointFileOperation\"\n| where Operation in (\"FileDownloaded\", \"FileUploaded\")\n| where TimeGenerated between(starttime..endtime)\n| extend ClientApplication = tostring(parse_json(AdditionalProperties).UserAgent)\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by ClientIp, ClientApplication;\nrecentUA | join kind=leftanti (\n historicalUA\n) on ClientIp, ClientApplication\n// Some EnrichedMicrosoft365AuditLogs records do not contain ClientIp information - exclude these for fewer results\n| where not(isempty(ClientIp))\n| extend IP_0_Address = ClientIp\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "New user agents associated with a client IP for SharePoint file uploads/downloads." + }, + { + "name": "tactics", + "value": "Exfiltration" + }, + { + "name": "techniques", + "value": "T1030" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject20')._huntingQuerycontentId20),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 20", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject20')._huntingQuerycontentId20)]", + "contentId": "[variables('huntingQueryObject20')._huntingQuerycontentId20]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject20').huntingQueryVersion20]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject20')._huntingQuerycontentId20]", + "contentKind": "HuntingQuery", + "displayName": "SharePoint File Operation via Client IP with Previously Unseen User Agents", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject20')._huntingQuerycontentId20,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject20')._huntingQuerycontentId20,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('huntingQueryObject21').huntingQueryTemplateSpecName21]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "MultipleUsersEmailForwardedToSameDestination_HuntingQueries Hunting Query with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('huntingQueryObject21').huntingQueryVersion21]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.OperationalInsights/savedSearches", + "apiVersion": "2022-10-01", + "name": "Global_Secure_Access_Hunting_Query_21", + "location": "[parameters('workspace-location')]", + "properties": { + "eTag": "*", + "displayName": "Multiple Users Email Forwarded to Same Destination", + "category": "Hunting Queries", + "query": "let queryfrequency = 1d;\nlet queryperiod = 7d;\nEnrichedMicrosoft365AuditLogs\n| where TimeGenerated > ago(queryperiod)\n| where Workload == \"Exchange\"\n| where AdditionalProperties has_any (\"ForwardTo\", \"RedirectTo\", \"ForwardingSmtpAddress\")\n| mv-apply DynamicParameters = todynamic(AdditionalProperties) on (summarize ParsedParameters = make_bag(bag_pack(tostring(DynamicParameters.Name), DynamicParameters.Value)))\n| evaluate bag_unpack(ParsedParameters, columnsConflict='replace_source')\n| extend DestinationMailAddress = tolower(case(\n isnotempty(column_ifexists(\"ForwardTo\", \"\")), column_ifexists(\"ForwardTo\", \"\"),\n isnotempty(column_ifexists(\"RedirectTo\", \"\")), column_ifexists(\"RedirectTo\", \"\"),\n isnotempty(column_ifexists(\"ForwardingSmtpAddress\", \"\")), trim_start(@\"smtp:\", column_ifexists(\"ForwardingSmtpAddress\", \"\")),\n \"\"))\n| where isnotempty(DestinationMailAddress)\n| mv-expand split(DestinationMailAddress, \";\")\n| extend ClientIPValues = extract_all(@'\\[?(::ffff:)?(?P(\\d+\\.\\d+\\.\\d+\\.\\d+)|[^\\]]+)\\]?([-:](?P\\d+))?', dynamic([\"IPAddress\", \"Port\"]), ClientIp)[0]\n| extend ClientIp = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1])\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), DistinctUserCount = dcount(UserId), UserId = make_set(UserId, 250), Ports = make_set(Port, 250), EventCount = count() by tostring(DestinationMailAddress), ClientIp\n| where DistinctUserCount > 1 and EndTime > ago(queryfrequency)\n| mv-expand UserId to typeof(string)\n| extend AccountName = tostring(split(UserId, \"@\")[0]), AccountUPNSuffix = tostring(split(UserId, \"@\")[1])\n", + "version": 2, + "tags": [ + { + "name": "description", + "value": "Identifies when multiple (more than one) users' mailboxes are configured to forward to the same destination. \nThis could be an attacker-controlled destination mailbox configured to collect mail from multiple compromised user accounts." + }, + { + "name": "tactics", + "value": "Collection,Exfiltration" + }, + { + "name": "techniques", + "value": "T1114,T1020" + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject21')._huntingQuerycontentId21),'/'))))]", + "properties": { + "description": "Global Secure Access Hunting Query 21", + "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject21')._huntingQuerycontentId21)]", + "contentId": "[variables('huntingQueryObject21')._huntingQuerycontentId21]", + "kind": "HuntingQuery", + "version": "[variables('huntingQueryObject21').huntingQueryVersion21]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('huntingQueryObject21')._huntingQuerycontentId21]", + "contentKind": "HuntingQuery", + "displayName": "Multiple Users Email Forwarded to Same Destination", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject21')._huntingQuerycontentId21,'-', '2.0.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject21')._huntingQuerycontentId21,'-', '2.0.1')))]", + "version": "2.0.1" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", + "apiVersion": "2023-04-01-preview", + "location": "[parameters('workspace-location')]", + "properties": { + "version": "3.0.0", + "kind": "Solution", + "contentSchemaVersion": "3.0.0", + "displayName": "Global Secure Access", + "publisherDisplayName": "Microsoft Corporation", + "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n

• Review the solution Release Notes

\n

• There may be known issues pertaining to this Solution, please refer to them before installing.

\n

Global Secure Access is a domain solution and does not include any data connectors. The content in this solution requires one of the product solutions below.

\n

Prerequisite:

\n

Install one or more of the listed solutions to unlock the value provided by this solution.

\n
    \n
  1. Microsoft Entra ID
  2. \n
\n

Underlying Microsoft Technologies used:

\n

This solution depends on the following technologies, and some of these dependencies may either be in Preview state or might result in additional ingestion or operational costs:

\n
    \n
  1. Product solutions as described above
  2. \n
\n

Workbooks: 2, Analytic Rules: 19, Hunting Queries: 21

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "contentKind": "Solution", + "contentProductId": "[variables('_solutioncontentProductId')]", + "id": "[variables('_solutioncontentProductId')]", + "icon": "", + "contentId": "[variables('_solutionId')]", + "parentId": "[variables('_solutionId')]", + "source": { + "kind": "Solution", + "name": "Global Secure Access", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "tier": "Partner", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "kind": "Workbook", + "contentId": "[variables('_workbookContentId1')]", + "version": "[variables('workbookVersion1')]" + }, + { + "kind": "Workbook", + "contentId": "[variables('_workbookContentId2')]", + "version": "[variables('workbookVersion2')]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]", + "version": "[variables('analyticRuleObject1').analyticRuleVersion1]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]", + "version": "[variables('analyticRuleObject2').analyticRuleVersion2]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]", + "version": "[variables('analyticRuleObject3').analyticRuleVersion3]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject4')._analyticRulecontentId4]", + "version": "[variables('analyticRuleObject4').analyticRuleVersion4]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject5')._analyticRulecontentId5]", + "version": "[variables('analyticRuleObject5').analyticRuleVersion5]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject6')._analyticRulecontentId6]", + "version": "[variables('analyticRuleObject6').analyticRuleVersion6]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject7')._analyticRulecontentId7]", + "version": "[variables('analyticRuleObject7').analyticRuleVersion7]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject8')._analyticRulecontentId8]", + "version": "[variables('analyticRuleObject8').analyticRuleVersion8]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject9')._analyticRulecontentId9]", + "version": "[variables('analyticRuleObject9').analyticRuleVersion9]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject10')._analyticRulecontentId10]", + "version": "[variables('analyticRuleObject10').analyticRuleVersion10]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject11')._analyticRulecontentId11]", + "version": "[variables('analyticRuleObject11').analyticRuleVersion11]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject12')._analyticRulecontentId12]", + "version": "[variables('analyticRuleObject12').analyticRuleVersion12]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject13')._analyticRulecontentId13]", + "version": "[variables('analyticRuleObject13').analyticRuleVersion13]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject14')._analyticRulecontentId14]", + "version": "[variables('analyticRuleObject14').analyticRuleVersion14]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject15')._analyticRulecontentId15]", + "version": "[variables('analyticRuleObject15').analyticRuleVersion15]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject16')._analyticRulecontentId16]", + "version": "[variables('analyticRuleObject16').analyticRuleVersion16]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject17')._analyticRulecontentId17]", + "version": "[variables('analyticRuleObject17').analyticRuleVersion17]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject18')._analyticRulecontentId18]", + "version": "[variables('analyticRuleObject18').analyticRuleVersion18]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRuleObject19')._analyticRulecontentId19]", + "version": "[variables('analyticRuleObject19').analyticRuleVersion19]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]", + "version": "[variables('huntingQueryObject1').huntingQueryVersion1]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject2')._huntingQuerycontentId2]", + "version": "[variables('huntingQueryObject2').huntingQueryVersion2]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject3')._huntingQuerycontentId3]", + "version": "[variables('huntingQueryObject3').huntingQueryVersion3]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject4')._huntingQuerycontentId4]", + "version": "[variables('huntingQueryObject4').huntingQueryVersion4]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject5')._huntingQuerycontentId5]", + "version": "[variables('huntingQueryObject5').huntingQueryVersion5]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject6')._huntingQuerycontentId6]", + "version": "[variables('huntingQueryObject6').huntingQueryVersion6]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject7')._huntingQuerycontentId7]", + "version": "[variables('huntingQueryObject7').huntingQueryVersion7]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject8')._huntingQuerycontentId8]", + "version": "[variables('huntingQueryObject8').huntingQueryVersion8]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject9')._huntingQuerycontentId9]", + "version": "[variables('huntingQueryObject9').huntingQueryVersion9]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject10')._huntingQuerycontentId10]", + "version": "[variables('huntingQueryObject10').huntingQueryVersion10]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject11')._huntingQuerycontentId11]", + "version": "[variables('huntingQueryObject11').huntingQueryVersion11]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject12')._huntingQuerycontentId12]", + "version": "[variables('huntingQueryObject12').huntingQueryVersion12]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject13')._huntingQuerycontentId13]", + "version": "[variables('huntingQueryObject13').huntingQueryVersion13]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject14')._huntingQuerycontentId14]", + "version": "[variables('huntingQueryObject14').huntingQueryVersion14]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject15')._huntingQuerycontentId15]", + "version": "[variables('huntingQueryObject15').huntingQueryVersion15]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject16')._huntingQuerycontentId16]", + "version": "[variables('huntingQueryObject16').huntingQueryVersion16]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject17')._huntingQuerycontentId17]", + "version": "[variables('huntingQueryObject17').huntingQueryVersion17]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject18')._huntingQuerycontentId18]", + "version": "[variables('huntingQueryObject18').huntingQueryVersion18]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject19')._huntingQuerycontentId19]", + "version": "[variables('huntingQueryObject19').huntingQueryVersion19]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject20')._huntingQuerycontentId20]", + "version": "[variables('huntingQueryObject20').huntingQueryVersion20]" + }, + { + "kind": "HuntingQuery", + "contentId": "[variables('huntingQueryObject21')._huntingQuerycontentId21]", + "version": "[variables('huntingQueryObject21').huntingQueryVersion21]" + } + ] + }, + "firstPublishDate": "2024-04-08", + "providers": [ + "Microsoft" + ], + "categories": { + "domains": [ + "Identity" + ] + } + }, + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('_solutionId'))]" + } + ], + "outputs": {} +} diff --git a/Solutions/Global Secure Access/Package/testParameters.json b/Solutions/Global Secure Access/Package/testParameters.json new file mode 100644 index 00000000000..8dd674f5956 --- /dev/null +++ b/Solutions/Global Secure Access/Package/testParameters.json @@ -0,0 +1,40 @@ +{ + "location": { + "type": "string", + "minLength": 1, + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Not used, but needed to pass arm-ttk test `Location-Should-Not-Be-Hardcoded`. We instead use the `workspace-location` which is derived from the LA workspace" + } + }, + "workspace-location": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "[concat('Region to deploy solution resources -- separate from location selection',parameters('location'))]" + } + }, + "workspace": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Workspace name for Log Analytics where Microsoft Sentinel is setup" + } + }, + "workbook1-name": { + "type": "string", + "defaultValue": "Microsoft Global Secure Access Enriched M365 Logs", + "minLength": 1, + "metadata": { + "description": "Name for the workbook" + } + }, + "workbook2-name": { + "type": "string", + "defaultValue": "Microsoft Global Secure Access Traffic Logs", + "minLength": 1, + "metadata": { + "description": "Name for the workbook" + } + } +} diff --git a/Solutions/Global Secure Access/ReleaseNotes.md b/Solutions/Global Secure Access/ReleaseNotes.md new file mode 100644 index 00000000000..15e70717f1e --- /dev/null +++ b/Solutions/Global Secure Access/ReleaseNotes.md @@ -0,0 +1,3 @@ +| **Version** | **Date Modified (DD-MM-YYYY)** | **Change History** | +|-------------|--------------------------------|-----------------------------------------------------------------------------------------| +| 3.0.0 | 05-09-2024 | Initial Solution release | diff --git a/Solutions/Global Secure Access/SolutionMetadata.json b/Solutions/Global Secure Access/SolutionMetadata.json new file mode 100644 index 00000000000..0465917cd42 --- /dev/null +++ b/Solutions/Global Secure Access/SolutionMetadata.json @@ -0,0 +1,15 @@ +{ + "publisherId": "azuresentinel", + "offerId": "azure-sentinel-solution-globalsecureaccess", + "firstPublishDate": "2024-04-08", + "providers": [ "Microsoft" ], + "categories": { + "domains": [ "Identity"] + }, + "support": { + "tier": "Partner", + "name": "Microsoft Corporation", + "email": "GSASentinelSupport@microsoft.com", + "link": "https://learn.microsoft.com/en-us/entra/global-secure-access/overview-what-is-global-secure-access" + } +} \ No newline at end of file diff --git a/Solutions/Global Secure Access/Workbooks/GSAM365EnrichedEvents.json b/Solutions/Global Secure Access/Workbooks/GSAM365EnrichedEvents.json new file mode 100644 index 00000000000..461ba0feb57 --- /dev/null +++ b/Solutions/Global Secure Access/Workbooks/GSAM365EnrichedEvents.json @@ -0,0 +1,418 @@ +{ + "version": "Notebook/1.0", + "items": [ + { + "type": 1, + "content": { + "json": "## Traffic Logs workbook\n---\n\nLog information in the dashboard is limited to 30 days." + }, + "name": "text - 1" + }, + { + "type": 9, + "content": { + "version": "KqlParameterItem/1.0", + "crossComponentResources": [], + "parameters": [ + { + "id": "ff8b2a55-1849-4848-acf8-eab5452e9f10", + "version": "KqlParameterItem/1.0", + "name": "LogAnalyticWorkspace", + "label": "Log Analytic Workspace", + "type": 5, + "description": "The log analytic workspace in which to execute the queries", + "isRequired": true, + "query": "resources\r\n| where type == \"microsoft.operationalinsights/workspaces\"\r\n| project id", + "typeSettings": { + "resourceTypeFilter": { + "microsoft.operationalinsights/workspaces": true + }, + "additionalResourceOptions": [ + "value::1" + ], + "showDefault": false + }, + "timeContext": { + "durationMs": 86400000 + }, + "defaultValue": "value::1", + "queryType": 1, + "resourceType": "microsoft.resourcegraph/resources" + }, + { + "id": "f15f34d8-8e2d-4c39-8dee-be2f979c86a8", + "version": "KqlParameterItem/1.0", + "name": "TimeRange", + "label": "Time Range", + "type": 4, + "isRequired": true, + "typeSettings": { + "selectableValues": [ + { + "durationMs": 300000 + }, + { + "durationMs": 900000 + }, + { + "durationMs": 1800000 + }, + { + "durationMs": 3600000 + }, + { + "durationMs": 14400000 + }, + { + "durationMs": 43200000 + }, + { + "durationMs": 86400000 + }, + { + "durationMs": 172800000 + }, + { + "durationMs": 259200000 + }, + { + "durationMs": 604800000 + }, + { + "durationMs": 1209600000 + }, + { + "durationMs": 2419200000 + }, + { + "durationMs": 2592000000 + } + ], + "allowCustom": true + }, + "timeContext": { + "durationMs": 86400000 + }, + "value": { + "durationMs": 2592000000 + } + }, + { + "id": "8bab511b-53b3-4220-9d1c-372345b06728", + "version": "KqlParameterItem/1.0", + "name": "Users", + "type": 2, + "isRequired": true, + "multiSelect": true, + "quote": "'", + "delimiter": ",", + "query": "EnrichedMicrosoft365AuditLogsDemos_CL\r\n| summarize Count = count() by UserId_s\r\n| order by Count desc, UserId_s asc\r\n| project Value = UserId_s, Label = strcat(UserId_s, ' - ', Count, ' Logs'), Selected = false", + "typeSettings": { + "limitSelectTo": 20, + "additionalResourceOptions": [ + "value::all" + ], + "selectAllValue": "*", + "showDefault": false + }, + "timeContext": { + "durationMs": 0 + }, + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "value": [ + "value::all" + ] + } + ], + "style": "pills", + "queryType": 1, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "name": "parameters - 15" + }, + { + "type": 11, + "content": { + "version": "LinkItem/1.0", + "style": "tabs", + "links": [ + { + "id": "2b2cd1be-9d25-412c-8444-f005c4789b55", + "cellValue": "tabSel", + "linkTarget": "parameter", + "linkLabel": "Overview", + "subTarget": "Overview", + "style": "link" + }, + { + "id": "cc3e67f2-f20f-4430-8dee-d0773b90d9ce", + "cellValue": "tabSel", + "linkTarget": "parameter", + "linkLabel": "All Traffic", + "subTarget": "AllTraffic", + "style": "link" + } + ] + }, + "name": "links - 7" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| project \r\n Timestamp = createdDateTime_t,\r\n User = userPrincipalName_s,\r\n SourceIP = SourceIP,\r\n DestinationIP = destinationIp_s,\r\n DestinationPort = destinationPort_d,\r\n Action = action_s,\r\n PolicyName = policyName_s,\r\n TransportProtocol = transportProtocol_s,\r\n TrafficType = trafficType_s,\r\n DestinationURL = destinationUrl_s,\r\n ReceivedBytes = receivedBytes_d,\r\n SentBytes = sentBytes_d,\r\n DeviceOS = deviceOperatingSystem_s,\r\n PolicyRuleID = policyRuleId_s\r\n| order by Timestamp desc", + "size": 3, + "showAnalytics": true, + "title": "Log", + "timeContextFromParameter": "TimeRange", + "showExportToExcel": true, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "gridSettings": { + "rowLimit": 1000, + "filter": true + } + }, + "conditionalVisibility": { + "parameterName": "tabSel", + "comparison": "isEqualTo", + "value": "AllTraffic" + }, + "name": "query - 6" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "// Unique Users\nNetworkAccessDemo_CL\n| extend GeoInfo = geo_info_from_ip_address(SourceIP) // Extend each row with geolocation info\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\n| project SourceIP, Country = tostring(GeoInfo.country), State = tostring(GeoInfo.state), City = tostring(GeoInfo.city), Latitude = tostring(GeoInfo.latitude), Longitude = tostring(GeoInfo.longitude)\n| summarize UniqueUsers=dcount(Country)\n| extend snapshot = \"Total Locations\"\n| project col1 = UniqueUsers, snapshot\n\n// Union with Unique Devices\n| union (\n NetworkAccessDemo_CL\n | where userPrincipalName_s in ({Users}) or '*' in ({Users})\n | extend BytesInGB = todouble(sentBytes_d + receivedBytes_d) / (1024 * 1024 * 1024) // Convert bytes to gigabytes\n | summarize TotalBytesGB = sum(BytesInGB)\n | extend snapshot = \"Total Bytes (GB)\"\n | project col1 = tolong(TotalBytesGB), snapshot\n)\n\n// Union with Total Internet Access\n| union (\n NetworkAccessDemo_CL\n | where userPrincipalName_s in ({Users}) or '*' in ({Users})\n | summarize TotalTransactions = count()\n | extend snapshot = \"Total Transactions\"\n | project col1 = TotalTransactions, snapshot\n)\n\n// Union with Total Private Access\n// Order by Snapshot for consistent tile ordering on dashboard\n| order by snapshot", + "size": 4, + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "tiles", + "tileSettings": { + "titleContent": { + "columnMatch": "snapshot", + "formatter": 1 + }, + "leftContent": { + "columnMatch": "col1", + "formatter": 12, + "formatOptions": { + "palette": "auto" + } + }, + "showBorder": true, + "size": "auto" + }, + "mapSettings": { + "locInfo": "LatLong", + "sizeSettings": "ExistingClients", + "sizeAggregation": "Sum", + "legendMetric": "ExistingClients", + "legendAggregation": "Sum", + "itemColorSettings": { + "type": "heatmap", + "colorAggregation": "Sum", + "nodeColorField": "ExistingClients", + "heatmapPalette": "greenRed" + } + }, + "textSettings": { + "style": "bignumber" + } + }, + "conditionalVisibility": { + "parameterName": "tabSel", + "comparison": "isEqualTo", + "value": "Overview" + }, + "name": "query - 2" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| extend BytesInGB = todouble(sentBytes_d + receivedBytes_d) / (1024 * 1024 * 1024) // Convert bytes to gigabytes\r\n| summarize TotalBytesGB = sum(BytesInGB) by bin(createdDateTime_t, 1h), trafficType_s\r\n| order by bin(createdDateTime_t, 1h) asc, trafficType_s asc\r\n| project createdDateTime_t, trafficType_s, TotalBytesGB\r\n", + "size": 2, + "title": "Usage over Time (GB)", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "barchart" + }, + "conditionalVisibility": { + "parameterName": "tabSel", + "comparison": "isEqualTo", + "value": "Overview" + }, + "name": "query - 0" + } + ] + }, + "name": "group - 5" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| extend GeoInfo = geo_info_from_ip_address(SourceIP) // Extend each row with geolocation info\r\n| project createdDateTime_t, SourceIP, Country = tostring(GeoInfo.country), State = tostring(GeoInfo.state), City = tostring(GeoInfo.city), Latitude = tostring(GeoInfo.latitude), Longitude = tostring(GeoInfo.longitude)\r\n| where Country != \"\"\r\n| summarize Count = count() by City, State, Country\r\n", + "size": 0, + "title": "Locations", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "map", + "mapSettings": { + "locInfo": "CountryRegion", + "locInfoColumn": "Country", + "latitude": "Latitude", + "longitude": "Longitude", + "sizeSettings": "Count", + "sizeAggregation": "Sum", + "labelSettings": "Country", + "legendMetric": "Country", + "legendAggregation": "Count", + "itemColorSettings": { + "nodeColorField": "Count", + "colorAggregation": "Sum", + "type": "heatmap", + "heatmapPalette": "turquoise" + } + } + }, + "customWidth": "50", + "name": "query - 0" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| where tolower(action_s) == \"allow\" and destinationWebCategory_displayName_s != '' // Filter for allowed traffic\r\n| extend firstCategory = tostring(split(destinationWebCategory_displayName_s, ',')[0]) // Split and get the first category\r\n| summarize Count = count() by firstCategory\r\n| top 10 by Count\r\n", + "size": 2, + "title": "Top Allowed Web Categories", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart" + }, + "customWidth": "50", + "name": "query - 7" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| where tolower(action_s) == \"block\" and destinationFQDN_s != '' // Filter for allowed traffic\r\n| summarize Count = count() by destinationFQDN_s\r\n| top 100 by Count", + "size": 0, + "title": "Top Blocked Destinations", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "query - 5" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| where tolower(action_s) == \"block\" and destinationWebCategory_displayName_s != '' // Filter for blocked traffic\r\n| extend firstCategory = tostring(split(destinationWebCategory_displayName_s, ',')[0]) // Split and get the first category\r\n| summarize Count = count() by firstCategory\r\n| top 10 by Count\r\n", + "size": 3, + "title": "Top Blocked Web Categories", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart" + }, + "customWidth": "50", + "name": "query - 6" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| where sentBytes_d > 0\r\n| where tolower(action_s) != \"block\" \r\n| summarize Count = count() , Sent = sum(sentBytes_d), Recived = sum(receivedBytes_d), Total = sum(receivedBytes_d+ sentBytes_d) by destinationFQDN_s\r\n| order by Count desc\r\n", + "size": 0, + "title": "Top Allowed Destinations", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Count", + "formatter": 4, + "formatOptions": { + "palette": "magenta" + } + }, + { + "columnMatch": "Recived", + "formatter": 4, + "formatOptions": { + "palette": "turquoise" + } + }, + { + "columnMatch": "Total", + "formatter": 4, + "formatOptions": { + "palette": "pink" + } + }, + { + "columnMatch": "Sent", + "formatter": 4, + "formatOptions": { + "palette": "blue" + } + } + ] + } + }, + "customWidth": "50", + "name": "query - 1" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| where transportProtocol_s != ''\r\n| summarize Count = count() by toupper(transportProtocol_s)\r\n| top 10 by Count\r\n", + "size": 2, + "title": "Protocol Distburion", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart" + }, + "customWidth": "50", + "name": "query - 3" + } + ] + }, + "conditionalVisibility": { + "parameterName": "tabSel", + "comparison": "isEqualTo", + "value": "Overview" + }, + "name": "group - 4" + } + ], + "fallbackResourceIds": [], + "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json" +} diff --git a/Solutions/Global Secure Access/Workbooks/GSANetworkTraffic.json b/Solutions/Global Secure Access/Workbooks/GSANetworkTraffic.json new file mode 100644 index 00000000000..46e0e43e6ff --- /dev/null +++ b/Solutions/Global Secure Access/Workbooks/GSANetworkTraffic.json @@ -0,0 +1,421 @@ +{ + "version": "Notebook/1.0", + "items": [ + { + "type": 1, + "content": { + "json": "## Traffic Logs workbook\n---\n\nLog information in the dashboard is limited to 30 days." + }, + "name": "text - 0" + }, + { + "type": 9, + "content": { + "version": "KqlParameterItem/1.0", + "crossComponentResources": [ + "" + ], + "parameters": [ + { + "id": "ff8b2a55-1849-4848-acf8-eab5452e9f10", + "version": "KqlParameterItem/1.0", + "name": "LogAnalyticWorkspace", + "label": "Log Analytic Workspace", + "type": 5, + "description": "The log analytic workspace in which to execute the queries", + "isRequired": true, + "query": "resources\r\n| where type == \"microsoft.operationalinsights/workspaces\"\r\n| project id", + "typeSettings": { + "resourceTypeFilter": { + "microsoft.operationalinsights/workspaces": true + }, + "additionalResourceOptions": [ + "value::1" + ], + "showDefault": false + }, + "timeContext": { + "durationMs": 86400000 + }, + "defaultValue": "value::1", + "queryType": 1, + "resourceType": "microsoft.resourcegraph/resources" + }, + { + "id": "f15f34d8-8e2d-4c39-8dee-be2f979c86a8", + "version": "KqlParameterItem/1.0", + "name": "TimeRange", + "label": "Time Range", + "type": 4, + "isRequired": true, + "typeSettings": { + "selectableValues": [ + { + "durationMs": 300000 + }, + { + "durationMs": 900000 + }, + { + "durationMs": 1800000 + }, + { + "durationMs": 3600000 + }, + { + "durationMs": 14400000 + }, + { + "durationMs": 43200000 + }, + { + "durationMs": 86400000 + }, + { + "durationMs": 172800000 + }, + { + "durationMs": 259200000 + }, + { + "durationMs": 604800000 + }, + { + "durationMs": 1209600000 + }, + { + "durationMs": 2419200000 + }, + { + "durationMs": 2592000000 + } + ], + "allowCustom": true + }, + "timeContext": { + "durationMs": 86400000 + }, + "value": { + "durationMs": 2592000000 + } + }, + { + "id": "8bab511b-53b3-4220-9d1c-372345b06728", + "version": "KqlParameterItem/1.0", + "name": "Users", + "type": 2, + "isRequired": true, + "multiSelect": true, + "quote": "'", + "delimiter": ",", + "query": "EnrichedMicrosoft365AuditLogsDemos_CL\r\n| summarize Count = count() by UserId_s\r\n| order by Count desc, UserId_s asc\r\n| project Value = UserId_s, Label = strcat(UserId_s, ' - ', Count, ' Logs'), Selected = false", + "typeSettings": { + "limitSelectTo": 20, + "additionalResourceOptions": [ + "value::all" + ], + "selectAllValue": "*", + "showDefault": false + }, + "timeContext": { + "durationMs": 0 + }, + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "value": [ + "value::all" + ] + } + ], + "style": "pills", + "queryType": 1, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "name": "parameters - 15" + }, + { + "type": 11, + "content": { + "version": "LinkItem/1.0", + "style": "tabs", + "links": [ + { + "id": "2b2cd1be-9d25-412c-8444-f005c4789b55", + "cellValue": "tabSel", + "linkTarget": "parameter", + "linkLabel": "Overview", + "subTarget": "Overview", + "style": "link" + }, + { + "id": "cc3e67f2-f20f-4430-8dee-d0773b90d9ce", + "cellValue": "tabSel", + "linkTarget": "parameter", + "linkLabel": "All Traffic", + "subTarget": "AllTraffic", + "style": "link" + } + ] + }, + "name": "links - 7" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| project \r\n Timestamp = createdDateTime_t,\r\n User = userPrincipalName_s,\r\n SourceIP = SourceIP,\r\n DestinationIP = destinationIp_s,\r\n DestinationPort = destinationPort_d,\r\n Action = action_s,\r\n PolicyName = policyName_s,\r\n TransportProtocol = transportProtocol_s,\r\n TrafficType = trafficType_s,\r\n DestinationURL = destinationUrl_s,\r\n ReceivedBytes = receivedBytes_d,\r\n SentBytes = sentBytes_d,\r\n DeviceOS = deviceOperatingSystem_s,\r\n PolicyRuleID = policyRuleId_s\r\n| order by Timestamp desc", + "size": 3, + "showAnalytics": true, + "title": "Log", + "timeContextFromParameter": "TimeRange", + "showExportToExcel": true, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "gridSettings": { + "rowLimit": 1000, + "filter": true + } + }, + "conditionalVisibility": { + "parameterName": "tabSel", + "comparison": "isEqualTo", + "value": "AllTraffic" + }, + "name": "query - 6" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "// Unique Users\nNetworkAccessDemo_CL\n| extend GeoInfo = geo_info_from_ip_address(SourceIP) // Extend each row with geolocation info\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\n| project SourceIP, Country = tostring(GeoInfo.country), State = tostring(GeoInfo.state), City = tostring(GeoInfo.city), Latitude = tostring(GeoInfo.latitude), Longitude = tostring(GeoInfo.longitude)\n| summarize UniqueUsers=dcount(Country)\n| extend snapshot = \"Total Locations\"\n| project col1 = UniqueUsers, snapshot\n\n// Union with Unique Devices\n| union (\n NetworkAccessDemo_CL\n | where userPrincipalName_s in ({Users}) or '*' in ({Users})\n | extend BytesInGB = todouble(sentBytes_d + receivedBytes_d) / (1024 * 1024 * 1024) // Convert bytes to gigabytes\n | summarize TotalBytesGB = sum(BytesInGB)\n | extend snapshot = \"Total Bytes (GB)\"\n | project col1 = tolong(TotalBytesGB), snapshot\n)\n\n// Union with Total Internet Access\n| union (\n NetworkAccessDemo_CL\n | where userPrincipalName_s in ({Users}) or '*' in ({Users})\n | summarize TotalTransactions = count()\n | extend snapshot = \"Total Trasnacations\"\n | project col1 = TotalTransactions, snapshot\n)\n\n// Union with Total Private Access\n// Order by Snapshot for consistent tile ordering on dashboard\n| order by snapshot", + "size": 4, + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "tiles", + "tileSettings": { + "titleContent": { + "columnMatch": "snapshot", + "formatter": 1 + }, + "leftContent": { + "columnMatch": "col1", + "formatter": 12, + "formatOptions": { + "palette": "auto" + } + }, + "showBorder": true, + "size": "auto" + }, + "mapSettings": { + "locInfo": "LatLong", + "sizeSettings": "ExistingClients", + "sizeAggregation": "Sum", + "legendMetric": "ExistingClients", + "legendAggregation": "Sum", + "itemColorSettings": { + "type": "heatmap", + "colorAggregation": "Sum", + "nodeColorField": "ExistingClients", + "heatmapPalette": "greenRed" + } + }, + "textSettings": { + "style": "bignumber" + } + }, + "conditionalVisibility": { + "parameterName": "tabSel", + "comparison": "isEqualTo", + "value": "Overview" + }, + "name": "query - 2" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| extend BytesInGB = todouble(sentBytes_d + receivedBytes_d) / (1024 * 1024 * 1024) // Convert bytes to gigabytes\r\n| summarize TotalBytesGB = sum(BytesInGB) by bin(createdDateTime_t, 1h), trafficType_s\r\n| order by bin(createdDateTime_t, 1h) asc, trafficType_s asc\r\n| project createdDateTime_t, trafficType_s, TotalBytesGB\r\n", + "size": 2, + "title": "Usage over Time (GB)", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "barchart" + }, + "conditionalVisibility": { + "parameterName": "tabSel", + "comparison": "isEqualTo", + "value": "Overview" + }, + "name": "query - 0" + } + ] + }, + "name": "group - 5" + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| extend GeoInfo = geo_info_from_ip_address(SourceIP) // Extend each row with geolocation info\r\n| project createdDateTime_t, SourceIP, Country = tostring(GeoInfo.country), State = tostring(GeoInfo.state), City = tostring(GeoInfo.city), Latitude = tostring(GeoInfo.latitude), Longitude = tostring(GeoInfo.longitude)\r\n| where Country != \"\"\r\n| summarize Count = count() by City, State, Country\r\n\r\n", + "size": 0, + "title": "Locations", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "map", + "mapSettings": { + "locInfo": "CountryRegion", + "locInfoColumn": "Country", + "latitude": "Latitude", + "longitude": "Longitude", + "sizeSettings": "Count", + "sizeAggregation": "Sum", + "labelSettings": "Country", + "legendMetric": "Country", + "legendAggregation": "Count", + "itemColorSettings": { + "nodeColorField": "Count", + "colorAggregation": "Sum", + "type": "heatmap", + "heatmapPalette": "turquoise" + } + } + }, + "customWidth": "50", + "name": "query - 0" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| where tolower(action_s) == \"allow\" and destinationWebCategory_displayName_s != '' // Filter for allowed traffic\r\n| extend firstCategory = tostring(split(destinationWebCategory_displayName_s, ',')[0]) // Split and get the first category\r\n| summarize Count = count() by firstCategory\r\n| top 10 by Count\r\n", + "size": 2, + "title": "Top Allowed Web Categories", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart" + }, + "customWidth": "50", + "name": "query - 7" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| where tolower(action_s) == \"block\" and destinationFQDN_s != '' // Filter for allowed traffic\r\n| summarize Count = count() by destinationFQDN_s\r\n| top 100 by Count", + "size": 0, + "title": "Top Blocked Destinations", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces" + }, + "customWidth": "50", + "name": "query - 5" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| where tolower(action_s) == \"block\" and destinationWebCategory_displayName_s != '' // Filter for blocked traffic\r\n| extend firstCategory = tostring(split(destinationWebCategory_displayName_s, ',')[0]) // Split and get the first category\r\n| summarize Count = count() by firstCategory\r\n| top 10 by Count\r\n", + "size": 3, + "title": "Top Blocked Web Categories", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart" + }, + "customWidth": "50", + "name": "query - 6" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| where sentBytes_d > 0\r\n| where tolower(action_s) != \"block\" \r\n| summarize Count = count() , Sent = sum(sentBytes_d), Recived = sum(receivedBytes_d), Total = sum(receivedBytes_d+ sentBytes_d) by destinationFQDN_s\r\n| order by Count desc\r\n", + "size": 0, + "title": "Top Allowed Destinations", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Count", + "formatter": 4, + "formatOptions": { + "palette": "magenta" + } + }, + { + "columnMatch": "Recived", + "formatter": 4, + "formatOptions": { + "palette": "turquoise" + } + }, + { + "columnMatch": "Total", + "formatter": 4, + "formatOptions": { + "palette": "pink" + } + }, + { + "columnMatch": "Sent", + "formatter": 4, + "formatOptions": { + "palette": "blue" + } + } + ] + } + }, + "customWidth": "50", + "name": "query - 1" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "NetworkAccessDemo_CL\r\n| where userPrincipalName_s in ({Users}) or '*' in ({Users})\r\n| where transportProtocol_s != ''\r\n| summarize Count = count() by toupper(transportProtocol_s)\r\n| top 10 by Count\r\n", + "size": 2, + "title": "Protocol Distburion", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "visualization": "piechart" + }, + "customWidth": "50", + "name": "query - 3" + } + ] + }, + "conditionalVisibility": { + "parameterName": "tabSel", + "comparison": "isEqualTo", + "value": "Overview" + }, + "name": "group - 4" + } + ], + "fallbackResourceIds": [ + ], + "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json" +} \ No newline at end of file