-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11187 from Azure/v-prasadboke-GSA_new
Adding GSA Solution with new ID's for Analytic Rules
- Loading branch information
Showing
49 changed files
with
8,834 additions
and
0 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
Solutions/Global Secure Access/Analytic Rules/Identity - AfterHoursActivity.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
47 changes: 47 additions & 0 deletions
47
Solutions/Global Secure Access/Analytic Rules/Identity - SharedSessions.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
86 changes: 86 additions & 0 deletions
86
...Analytic Rules/Office 365 - External User added to Team and immediately uploads file.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
71 changes: 71 additions & 0 deletions
71
...ons/Global Secure Access/Analytic Rules/Office 365 - ExternalUserAddedRemovedInTeams.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
55 changes: 55 additions & 0 deletions
55
...lobal Secure Access/Analytic Rules/Office 365 - Mail_redirect_via_ExO_transport_rule.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\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 |
56 changes: 56 additions & 0 deletions
56
Solutions/Global Secure Access/Analytic Rules/Office 365 - Malicious_Inbox_Rule.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.