From 703068b60efeadf3b39665fc3dd1de4c55d756a1 Mon Sep 17 00:00:00 2001 From: Shain <45466083+shainw@users.noreply.github.com> Date: Wed, 15 Nov 2023 18:47:49 -0800 Subject: [PATCH 1/5] accountEleveted rule updated --- .../AccountElevatedtoNewRole.yaml | 64 ++++++++++++++----- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AccountElevatedtoNewRole.yaml b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AccountElevatedtoNewRole.yaml index 345665e2c7a..212b0574c97 100644 --- a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AccountElevatedtoNewRole.yaml +++ b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AccountElevatedtoNewRole.yaml @@ -20,32 +20,62 @@ relevantTechniques: tags: - AADSecOpsGuide query: | - AuditLogs - | where TimeGenerated between(ago(14d)..ago(1d)) + let auditList = + AuditLogs + | where TimeGenerated >= ago(14d) | where OperationName =~ "Add member to role completed (PIM activation)" | where Result =~ "success" - | extend ElevatedUser = tostring(TargetResources[2].userPrincipalName) + | extend TargetUserPrincipalName = tostring(TargetResources[2].userPrincipalName) | extend displayName = tostring(TargetResources[0].displayName) | extend displayName2 = tostring(TargetResources[3].displayName) | extend ElevatedRole = iif(displayName =~ "Member", displayName2, displayName) - | join kind = rightanti (AuditLogs + ; + let lookbackList = auditList + | where TimeGenerated between(ago(14d)..ago(1d)) + ; + let recentList = auditList | where TimeGenerated > ago(1d) - | where OperationName =~ "Add member to role completed (PIM activation)" - | where Result =~ "success" - | extend ElevatedUser = tostring(TargetResources[2].userPrincipalName) - | extend displayName = tostring(TargetResources[0].displayName) - | extend displayName2 = tostring(TargetResources[3].displayName) - | extend ElevatedRole = iif(displayName =~ "Member", displayName2, displayName) - | extend ElevatedBy = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)) on ElevatedRole, ElevatedUser - | project-reorder ElevatedUser, ElevatedRole, ResultReason,ElevatedBy + ; + let newlyElevated = recentList + | join kind = leftanti lookbackList on ElevatedRole, TargetUserPrincipalName + ; + newlyElevated | project Id, AdditionalDetails + | mv-expand bagexpansion=array AdditionalDetails + | evaluate bag_unpack(AdditionalDetails) + | evaluate pivot(key, make_set(value)) + | extend ipaddr = todynamic(column_ifexists("ipaddr", "")) + | mv-expand ipaddr + | project Id, InitiatingIPAddress = tostring(ipaddr) + | join kind=rightouter newlyElevated on Id + | extend InitiatingAppName = tostring(InitiatedBy.app.displayName) + | extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId) + | extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName) + | extend InitiatingAadUserId = tostring(InitiatedBy.user.id) + | extend InitiatingIPAddress = iff(isnotempty(tostring(InitiatedBy.user.ipAddress)), tostring(InitiatedBy.user.ipAddress), InitiatingIPAddress) + | extend ElevatedBy = iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName) + | extend ElevatedUser = TargetUserPrincipalName + | extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1]) + | extend TargetAccountName = tostring(split(TargetUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(TargetUserPrincipalName, "@")[1]) + | project-reorder ElevatedUser, ElevatedRole, ResultReason, ElevatedBy, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingIPAddress, TargetUserPrincipalName + entityMappings: - entityType: Account fieldMappings: - - identifier: FullName - columnName: ElevatedUser + - identifier: Name + columnName: InitiatingAccountName + - identifier: UPNSuffix + columnName: InitiatingAccountUPNSuffix + - identifier: AadUserId + columnName: InitiatingAadUserId - entityType: Account fieldMappings: - - identifier: FullName - columnName: ElevatedBy -version: 1.0.1 + - identifier: Name + columnName: TargetAccountName + - identifier: UPNSuffix + columnName: TargetAccountUPNSuffix + - entityType: IP + fieldMappings: + - identifier: Address + columnName: InitiatingIPAddress +version: 1.1.0 kind: Scheduled \ No newline at end of file From 4432f6b75c816f5979d5f6ff2dba90bc4081e495 Mon Sep 17 00:00:00 2001 From: Shain <45466083+shainw@users.noreply.github.com> Date: Wed, 15 Nov 2023 19:00:50 -0800 Subject: [PATCH 2/5] AuthMethod updated --- ...tionMethodChangedforPrivilegedAccount.yaml | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AuthenticationMethodChangedforPrivilegedAccount.yaml b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AuthenticationMethodChangedforPrivilegedAccount.yaml index a98c548244a..90d7ac13336 100644 --- a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AuthenticationMethodChangedforPrivilegedAccount.yaml +++ b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AuthenticationMethodChangedforPrivilegedAccount.yaml @@ -1,7 +1,7 @@ id: feb0a2fb-ae75-4343-8cbc-ed545f1da289 name: Authentication Method Changed for Privileged Account description: | - 'Identifies authentication methods being changed for a privileged account. This could be an indicated of an attacker adding an auth method to the account so they can have continued access. + 'Identifies authentication methods being changed for a privileged account. This could be an indication of an attacker adding an auth method to the account so they can have continued access. Ref : https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-privileged-accounts#things-to-monitor-1' severity: High requiredDataConnectors: @@ -22,23 +22,41 @@ relevantTechniques: tags: - AADSecOpsGuide query: | - let VIPUsers = (IdentityInfo - | where AssignedRoles contains "Admin" - | summarize by tolower(AccountUPN)); - AuditLogs - | where Category =~ "UserManagement" - | where ActivityDisplayName =~ "User registered security info" - | where LoggedByService =~ "Authentication Methods" - | extend AccountCustomEntity = tostring(TargetResources[0].userPrincipalName), IPCustomEntity = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) - | where AccountCustomEntity in (VIPUsers) + let VIPUsers = (IdentityInfo + | where AssignedRoles contains "Admin" + | summarize by tolower(AccountUPN)); + AuditLogs + | where Category =~ "UserManagement" + | where ActivityDisplayName =~ "User registered security info" + | where LoggedByService =~ "Authentication Methods" + | extend TargetUserPrincipalName = tostring(TargetResources[0].userPrincipalName) + | where tolower(TargetUserPrincipalName) in (VIPUsers) + | extend TargetAadUserId = tostring(TargetResources[0].id) + | extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName) + | extend InitiatingAadUserId = tostring(InitiatedBy.user.id) + | extend InitiatingIPAddress = tostring(InitiatedBy.user.ipAddress) + | extend TargetAccountName = tostring(split(TargetUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(TargetUserPrincipalName, "@")[1]) + | extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1]) entityMappings: - entityType: Account fieldMappings: - - identifier: FullName - columnName: AccountCustomEntity + - identifier: Name + columnName: TargetAccountName + - identifier: UPNSuffix + columnName: TargetAccountUPNSuffix + - identifier: AadUserId + columnName: TargetAadUserId + - entityType: Account + fieldMappings: + - identifier: Name + columnName: InitiatingAccountName + - identifier: UPNSuffix + columnName: InitiatingAccountUPNSuffix + - identifier: AadUserId + columnName: InitiatingAadUserId - entityType: IP fieldMappings: - identifier: Address - columnName: IPCustomEntity + columnName: InitiatingIPAddress version: 1.0.2 kind: Scheduled From 54eeee240db9ce8e3c08f5c748ec5c318f71d96a Mon Sep 17 00:00:00 2001 From: Shain <45466083+shainw@users.noreply.github.com> Date: Mon, 20 Nov 2023 20:12:14 -0800 Subject: [PATCH 3/5] Updated entity mappings for PrivAccountPermChange, BECRelatedDocs, UserAddedToAdmin --- .../PrivilegedAccountPermissionsChanged.yaml | 39 +++++++++++++------ ...SuspiciousAccessOfBECRelatedDocuments.yaml | 2 +- .../Analytic Rules/UserAddedtoAdminRole.yaml | 34 +++++++++++----- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/PrivilegedAccountPermissionsChanged.yaml b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/PrivilegedAccountPermissionsChanged.yaml index 604c7ab615d..01406d5af5c 100644 --- a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/PrivilegedAccountPermissionsChanged.yaml +++ b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/PrivilegedAccountPermissionsChanged.yaml @@ -31,26 +31,41 @@ query: | AuditLogs | where Category =~ "RoleManagement" | where OperationName has "Add eligible member" - | extend userPrincipalName = tostring(TargetResources[0].userPrincipalName) - | where tolower(userPrincipalName) in (admin_users) + | extend TargetUserPrincipalName = tostring(TargetResources[0].userPrincipalName) + | where tolower(TargetUserPrincipalName) in (admin_users) + | extend TargetAadUserId = tostring(TargetResources[0].id) | extend Group = tostring(TargetResources[0].displayName) - | extend AddedTo = iif(isnotempty(userPrincipalName), userPrincipalName, Group) + | extend RoleAddedTo = iif(isnotempty(TargetUserPrincipalName), TargetUserPrincipalName, Group) | extend mod_props = TargetResources[0].modifiedProperties - | extend appName = tostring(parse_json(tostring(InitiatedBy.app)).displayName) - | extend UPN = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) - | extend AddedBy = iif(isnotempty(appName), appName, UPN) + | extend InitiatingAppName = tostring(InitiatedBy.app.displayName) + | extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId) + | extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName) + | extend InitiatingAadUserId = tostring(InitiatedBy.user.id) + | extend InitiatingIPAddress = tostring(InitiatedBy.user.ipAddress) + | extend RoleAddedBy = iif(isnotempty(InitiatingAppName), InitiatingAppName, InitiatingUserPrincipalName) | mv-expand mod_props | where mod_props.displayName == "Role.DisplayName" + | extend UserAgent = tostring(AdditionalDetails[0].value) | extend RoleAdded = tostring(parse_json(tostring(mod_props.newValue))) - | project-reorder TimeGenerated, OperationName, AddedTo, RoleAdded, AddedBy + | extend TargetAccountName = tostring(split(TargetUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(TargetUserPrincipalName, "@")[1]) + | extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1]) + | project-reorder TimeGenerated, OperationName, TargetUserPrincipalName, RoleAddedTo, RoleAdded, RoleAddedBy, InitiatingUserPrincipalName, InitiatingAppName entityMappings: - entityType: Account fieldMappings: - - identifier: FullName - columnName: userPrincipalName + - identifier: Name + columnName: TargetAccountName + - identifier: UPNSuffix + columnName: TargetAccountUPNSuffix + - identifier: AadUserId + columnName: TargetAadUserId - entityType: Account fieldMappings: - - identifier: FullName - columnName: AddedBy -version: 1.0.3 + - identifier: Name + columnName: InitiatingAccountName + - identifier: UPNSuffix + columnName: InitiatingAccountUPNSuffix + - identifier: AadUserId + columnName: InitiatingAadUserId +version: 1.0.4 kind: Scheduled diff --git a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/SuspiciousAccessOfBECRelatedDocuments.yaml b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/SuspiciousAccessOfBECRelatedDocuments.yaml index b66382311fd..9c700bd79bf 100644 --- a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/SuspiciousAccessOfBECRelatedDocuments.yaml +++ b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/SuspiciousAccessOfBECRelatedDocuments.yaml @@ -41,7 +41,7 @@ query: | | join kind=inner(Events | extend TimeStamp = startofday(TimeGenerated)) on TimeStamp, User | extend IpAddr = column_ifexists("IpAddr", SrcIpAddr) | extend Name = iif(User contains "@", split(User, "@")[0], User) - | extend UPNSuffix = iif(User contains "@", split(User, "@")[1], User) + | extend UPNSuffix = iif(User contains "@", split(User, "@")[1], "") | project-reorder TimeGenerated, User, EventType, EventResult, EventProduct, FilePath, HttpUserAgent, IpAddr, CountOfDocs, Baseline, Score entityMappings: - entityType: Account diff --git a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/UserAddedtoAdminRole.yaml b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/UserAddedtoAdminRole.yaml index ce6c0d9f595..c6e90824e38 100644 --- a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/UserAddedtoAdminRole.yaml +++ b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/UserAddedtoAdminRole.yaml @@ -33,18 +33,34 @@ query: | | extend RoleName = trim('"',tostring(Property.newValue)) ) | where RoleName contains "admin" - | extend InitiatingApp = tostring(InitiatedBy.app.displayName) - | extend Initiator = iif(isnotempty(InitiatingApp), InitiatingApp, tostring(InitiatedBy.user.userPrincipalName)) - | extend AddedUser = iff(OperationName=="Add member to role",tostring(TargetResources[0].userPrincipalName),tostring(TargetResources[2].userPrincipalName)) - | project-reorder TimeGenerated, AddedUser, RoleName, Initiator + | extend InitiatingAppName = tostring(InitiatedBy.app.displayName) + | extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId) + | extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName) + | extend InitiatingAadUserId = tostring(InitiatedBy.user.id) + | extend InitiatingIPAddress = tostring(InitiatedBy.user.ipAddress) + | extend InitiatedBy = iif(isnotempty(InitiatingAppName), InitiatingAppName, InitiatingUserPrincipalName) + | extend TargetUserPrincipalName = iff(OperationName=="Add member to role",tostring(TargetResources[0].userPrincipalName),tostring(TargetResources[2].userPrincipalName)) + | extend TargetAadUserId = iff(OperationName=="Add member to role", tostring(TargetResources[0].id), tostring(TargetResources[2].id)) + | extend AddedUser = TargetUserPrincipalName + | extend TargetAccountName = tostring(split(TargetUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(TargetUserPrincipalName, "@")[1]) + | extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1]) + | project-reorder TimeGenerated, AddedUser, RoleName, InitiatedBy entityMappings: - entityType: Account fieldMappings: - - identifier: FullName - columnName: Initiator + - identifier: Name + columnName: TargetAccountName + - identifier: UPNSuffix + columnName: TargetAccountUPNSuffix + - identifier: AadUserId + columnName: TargetAadUserId - entityType: Account fieldMappings: - - identifier: FullName - columnName: AddedUser -version: 1.0.2 + - identifier: Name + columnName: InitiatingAccountName + - identifier: UPNSuffix + columnName: InitiatingAccountUPNSuffix + - identifier: AadUserId + columnName: InitiatingAadUserId +version: 1.0.3 kind: Scheduled From 07e4a9036e227afb8674e3f1d590a6cc6cf75146 Mon Sep 17 00:00:00 2001 From: Shain <45466083+shainw@users.noreply.github.com> Date: Mon, 20 Nov 2023 20:15:00 -0800 Subject: [PATCH 4/5] version --- .../AuthenticationMethodChangedforPrivilegedAccount.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AuthenticationMethodChangedforPrivilegedAccount.yaml b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AuthenticationMethodChangedforPrivilegedAccount.yaml index 90d7ac13336..8e25bfa3a0e 100644 --- a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AuthenticationMethodChangedforPrivilegedAccount.yaml +++ b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/AuthenticationMethodChangedforPrivilegedAccount.yaml @@ -58,5 +58,5 @@ entityMappings: fieldMappings: - identifier: Address columnName: InitiatingIPAddress -version: 1.0.2 +version: 1.0.3 kind: Scheduled From 70f698d1b015df0673f90976b5541526bc7b7578 Mon Sep 17 00:00:00 2001 From: Shain <45466083+shainw@users.noreply.github.com> Date: Mon, 20 Nov 2023 20:15:30 -0800 Subject: [PATCH 5/5] version --- .../Analytic Rules/SuspiciousAccessOfBECRelatedDocuments.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/SuspiciousAccessOfBECRelatedDocuments.yaml b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/SuspiciousAccessOfBECRelatedDocuments.yaml index 9c700bd79bf..5f00f692322 100644 --- a/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/SuspiciousAccessOfBECRelatedDocuments.yaml +++ b/Solutions/Business Email Compromise - Financial Fraud/Analytic Rules/SuspiciousAccessOfBECRelatedDocuments.yaml @@ -70,5 +70,5 @@ alertDetailsOverride: alertDescriptionFormat: | This query looks for users (in this case {{User}}) with suspicious spikes in the number of files accessed (in this case {{number_of_files_accessed}} events) that relate to topics commonly accessed as part of Business Email Compromise (BEC) attacks. The query looks for access to files in storage that relate to topics such as invoices or payments, and then looks for users accessing these files in significantly higher numbers than in the previous 14 days. Incidents raised by this analytic should be investigated to see if the user accessing these files should be accessing them, and if the volume they accessed them at was related to a legitimate business need. This query contains thresholds to reduce the chance of false positives, these can be adjusted to suit individual environments. In addition false positives could be generated by legitimate, scheduled actions that occur less often than every 14 days, additional exclusions can be added for these actions on username or IP address entities. This query uses the imFileEvent schema from ASIM, you will first need to ensure you have ASIM deployed in your environment. Ref https://learn.microsoft.com/azure/sentinel/normalization-about-parsers -version: 1.0.1 +version: 1.0.2 kind: Scheduled \ No newline at end of file