From 1de7e82caa6a5447d7f04a1841d2f189432aaf29 Mon Sep 17 00:00:00 2001 From: IshaDave <137959176+IshaDave@users.noreply.github.com> Date: Mon, 30 Oct 2023 00:38:02 -0700 Subject: [PATCH 1/7] Correlating Disabled Acc IP Signin & Risky O365 Op --- .../DisabledAccIPSigninWithRareRiskyOps.yaml | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Hunting Queries/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml diff --git a/Hunting Queries/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml b/Hunting Queries/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml new file mode 100644 index 00000000000..e2c721dc550 --- /dev/null +++ b/Hunting Queries/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml @@ -0,0 +1,91 @@ +id: 9adbd1c3-a4be-44ef-ac2f-503fd25692ee +name: Successful Signin from IP with Disabled Account - Rare and potentially high-risk Office operations +description: | + 'It is possible that a disabled user account is compromised and another account on the same IP is used to perform operations that are not typical for that user. + This query looks for operations that are not common, are potentially high-risk and are performed using an ip associated with a disabled account.' +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - SigninLogs + - connectorId: Office365 + dataTypes: + - OfficeActivity +tactics: + - InitialAccess + - Persistence + - Collection +relevantTechniques: + - T1078 + - T1098 + - T1114 +query: | + let threshold = 100; + let timeRange = ago(14d); + let timeBuffer = 1; + SigninLogs + | where TimeGenerated > timeRange + | where ResultType == "50057" + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), disabledAccountLoginAttempts = count(), + disabledAccountsTargeted = dcount(UserPrincipalName), applicationsTargeted = dcount(AppDisplayName), disabledAccountSet = make_set(UserPrincipalName), + applicationSet = make_set(AppDisplayName) by IPAddress, AppId + | order by disabledAccountLoginAttempts desc + | join kind=inner ( + // IPs are considered suspicious - and any related successful sign-ins are detected + SigninLogs + | where TimeGenerated > timeRange + | where ResultType == 0 + | summarize successSigninStart = min(TimeGenerated), successSigninEnd = max(TimeGenerated), successfulAccountSigninCount = dcount(UserPrincipalName), successfulAccountSigninSet = make_set(UserPrincipalName, 15) by IPAddress + // Assume IPs associated with sign-ins from 100+ distinct user accounts are safe + | where successfulAccountSigninCount < threshold + ) on IPAddress + // IPs where attempts to authenticate as disabled user accounts originated, and had a non-zero success rate for some other account + | where successfulAccountSigninCount != 0 + // Successful Account Signins occur within the same lookback period as the failed + | extend SuccessBeforeFailure = iff(successSigninStart >= StartTime and successSigninEnd <= EndTime, true, false) + | project StartTime, EndTime, IPAddress, disabledAccountLoginAttempts, disabledAccountsTargeted, disabledAccountSet, applicationSet, + successfulAccountSigninCount, successfulAccountSigninSet, successSigninStart, successSigninEnd, AppId + | order by disabledAccountLoginAttempts + // Break up the string of Succesfully signed into accounts into individual events + | mvexpand successfulAccountSigninSet + | extend IPCustomEntity = IPAddress + | join kind = inner ( + OfficeActivity + | where TimeGenerated > timeRange + | 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 (w3wp)', 'devilfish-applicationaccount')) + // Remove port from the end of the IP and/or square brackets around IP, if they exist + | extend IPCustomEntity = case( + ClientIP matches regex @'\[((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\]-\d{1,5}', tostring(extract('\\[([0-9]+\\.[0-9]+\\.[0-9]+)\\]-[0-9]+', 1, ClientIP)), + ClientIP matches regex @'\[((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\]', tostring(extract('\\[([0-9]+\\.[0-9]+\\.[0-9]+)\\]', 1, ClientIP)), + ClientIP matches regex @'(((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?))-\d{1,5}', tostring(extract('([0-9]+\\.[0-9]+\\.[0-9]+)-[0-9]+', 1, ClientIP)), + ClientIP matches regex @'((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)', ClientIP, + ClientIP matches regex @'\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})\]-\d{1,5}', tostring(extract('\\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\\d{1,3}(?:\\.\\d{1,3}){3})\\]-[0-9]+', 1, ClientIP)), + ClientIP matches regex @'\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})\]', tostring(extract('\\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\\d{1,3}(?:\\.\\d{1,3}){3})\\]', 1, ClientIP)), + ClientIP matches regex @'((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})-\d{1,5}', tostring(extract('((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\\d{1,3}(?:\\.\\d{1,3}){3})-[0-9]+', 1, ClientIP)), + ClientIP matches regex @'((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})', ClientIP, + "") + | where isnotempty(IPCustomEntity) + | extend OfficeTimeStamp = ElevationTime + ) on IPCustomEntity + // Rare and risky operations only happen within a certain time range of the successful sign-in + | where OfficeTimeStamp >= successSigninStart and datetime_diff('day', OfficeTimeStamp, successSigninEnd) <= timeBuffer +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserPrincipalName + - identifier: Name + columnName: AppDisplayName + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IPCustomEntity + - identifier: Address + columnName: IPAddress + - identifier: Address + columnName: ClientIP + - entityType: CloudApplication + fieldMappings: + - identifier: AppId + columnName: ApplicationId + #AccountObjectId does not exist in SigninLogs +version: 1.0.0 \ No newline at end of file From 5357830d016f738b4c6617e46e509dcf5506c0a9 Mon Sep 17 00:00:00 2001 From: IshaDave <137959176+IshaDave@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:23:31 -0700 Subject: [PATCH 2/7] hunting query --> detection, enhanced description --- .../DisabledAccIPSigninWithRareRiskyOps.yaml | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml diff --git a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml new file mode 100644 index 00000000000..c6432134351 --- /dev/null +++ b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml @@ -0,0 +1,101 @@ +id: 9adbd1c3-a4be-44ef-ac2f-503fd25692ee +name: High risk Office operation conducted by IP Address that recently attempted to log into a disabled account +description: | + ' + It is possible that a disabled user account is compromised and another account on the same IP is used to perform operations that are not typical for that user. + The query filters the SigninLogs for entries where ResultType is indicates a disabled account and the TimeGenerated is within a defined time range. + It then summarizes these entries by IPAddress and AppId, calculating various statistics such as number of login attempts, distinct UPNs, App IDs etc and joins these results with another set of results from SigninLogs, filtering for entries with less than normal number of successful sign-ins. + It then filters out entries where there were no successful sign-ins or where successful sign-ins did not occur within the same lookback period as the failed sign-ins, later projecting relevant fields by the count of login attempts, and expands the set of successful sign-ins into individual events. + Finally, it joins these results with entries from OfficeActivity where certain operations deemed rare and high risk have been performed, ensuring their occurrance within a certain time range of the successful sign-ins. + ' +severity: Medium +requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - SigninLogs + - connectorId: Office365 + dataTypes: + - OfficeActivity +queryFrequency: 1d +queryPeriod: 8d +triggerOperator: gt +triggerThreshold: 0 +tactics: + - InitialAccess + - Persistence + - Collection +relevantTechniques: + - T1078 + - T1098 + - T1114 +query: | + let threshold = 100; + let timeRange = ago(7d); + let timeBuffer = 1; + SigninLogs + | where TimeGenerated > timeRange + | where ResultType == "50057" + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), disabledAccountLoginAttempts = count(), + disabledAccountsTargeted = dcount(UserPrincipalName), applicationsTargeted = dcount(AppDisplayName), disabledAccountSet = make_set(UserPrincipalName), + applicationSet = make_set(AppDisplayName) by IPAddress, AppId + | order by disabledAccountLoginAttempts desc + | join kind=inner ( + // IPs are considered suspicious - and any related successful sign-ins are detected + SigninLogs + | where TimeGenerated > timeRange + | where ResultType == 0 + | summarize successSigninStart = min(TimeGenerated), successSigninEnd = max(TimeGenerated), successfulAccountSigninCount = dcount(UserPrincipalName), successfulAccountSigninSet = make_set(UserPrincipalName, 15) by IPAddress + // Assume IPs associated with sign-ins from 100+ distinct user accounts are safe + | where successfulAccountSigninCount < threshold + ) on IPAddress + // IPs where attempts to authenticate as disabled user accounts originated, and had a non-zero success rate for some other account + | where successfulAccountSigninCount != 0 + // Successful Account Signins occur within the same lookback period as the failed + | extend SuccessBeforeFailure = iff(successSigninStart >= StartTime and successSigninEnd <= EndTime, true, false) + | project StartTime, EndTime, IPAddress, disabledAccountLoginAttempts, disabledAccountsTargeted, disabledAccountSet, applicationSet, + successfulAccountSigninCount, successfulAccountSigninSet, successSigninStart, successSigninEnd, AppId + | order by disabledAccountLoginAttempts + // Break up the string of Succesfully signed into accounts into individual events + | mvexpand successfulAccountSigninSet + | extend IPCustomEntity = IPAddress + | join kind = inner ( + OfficeActivity + | where TimeGenerated > timeRange + | 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 (w3wp)', 'devilfish-applicationaccount')) + // Remove port from the end of the IP and/or square brackets around IP, if they exist + | extend IPCustomEntity = case( + ClientIP matches regex @'\[((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\]-\d{1,5}', tostring(extract('\\[([0-9]+\\.[0-9]+\\.[0-9]+)\\]-[0-9]+', 1, ClientIP)), + ClientIP matches regex @'\[((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\]', tostring(extract('\\[([0-9]+\\.[0-9]+\\.[0-9]+)\\]', 1, ClientIP)), + ClientIP matches regex @'(((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?))-\d{1,5}', tostring(extract('([0-9]+\\.[0-9]+\\.[0-9]+)-[0-9]+', 1, ClientIP)), + ClientIP matches regex @'((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)', ClientIP, + ClientIP matches regex @'\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})\]-\d{1,5}', tostring(extract('\\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\\d{1,3}(?:\\.\\d{1,3}){3})\\]-[0-9]+', 1, ClientIP)), + ClientIP matches regex @'\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})\]', tostring(extract('\\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\\d{1,3}(?:\\.\\d{1,3}){3})\\]', 1, ClientIP)), + ClientIP matches regex @'((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})-\d{1,5}', tostring(extract('((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\\d{1,3}(?:\\.\\d{1,3}){3})-[0-9]+', 1, ClientIP)), + ClientIP matches regex @'((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})', ClientIP, + "") + | where isnotempty(IPCustomEntity) + | extend OfficeTimeStamp = ElevationTime + ) on IPCustomEntity + // Rare and risky operations only happen within a certain time range of the successful sign-in + | where OfficeTimeStamp >= successSigninStart and datetime_diff('day', OfficeTimeStamp, successSigninEnd) <= timeBuffer +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: UserPrincipalName + - identifier: Name + columnName: AppDisplayName + - entityType: IP + fieldMappings: + - identifier: Address + columnName: IPCustomEntity + - identifier: Address + columnName: IPAddress + - identifier: Address + columnName: ClientIP + - entityType: CloudApplication + fieldMappings: + - identifier: AppId + columnName: ApplicationId + #AccountObjectId does not exist in SigninLogs +version: 1.0.0 \ No newline at end of file From caf52b8e87918891ea77a8faa5be88f7507d23d7 Mon Sep 17 00:00:00 2001 From: IshaDave <137959176+IshaDave@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:49:17 -0700 Subject: [PATCH 3/7] Fixed YAML syntax --- .../DisabledAccIPSigninWithRareRiskyOps.yaml | 13 ++- .../DisabledAccIPSigninWithRareRiskyOps.yaml | 91 ------------------- 2 files changed, 11 insertions(+), 93 deletions(-) delete mode 100644 Hunting Queries/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml diff --git a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml index c6432134351..e687c1e5154 100644 --- a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml +++ b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml @@ -97,5 +97,14 @@ entityMappings: fieldMappings: - identifier: AppId columnName: ApplicationId - #AccountObjectId does not exist in SigninLogs -version: 1.0.0 \ No newline at end of file +version: 1.0.0 +kind: Scheduled +metadata: + source: + kind: Community + author: + name: Isha Dave + support: + tier: Community + categories: + domains: [ "Security - Threat Intelligence", "Security - Others", "Identity" ] diff --git a/Hunting Queries/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml b/Hunting Queries/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml deleted file mode 100644 index e2c721dc550..00000000000 --- a/Hunting Queries/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml +++ /dev/null @@ -1,91 +0,0 @@ -id: 9adbd1c3-a4be-44ef-ac2f-503fd25692ee -name: Successful Signin from IP with Disabled Account - Rare and potentially high-risk Office operations -description: | - 'It is possible that a disabled user account is compromised and another account on the same IP is used to perform operations that are not typical for that user. - This query looks for operations that are not common, are potentially high-risk and are performed using an ip associated with a disabled account.' -requiredDataConnectors: - - connectorId: AzureActiveDirectory - dataTypes: - - SigninLogs - - connectorId: Office365 - dataTypes: - - OfficeActivity -tactics: - - InitialAccess - - Persistence - - Collection -relevantTechniques: - - T1078 - - T1098 - - T1114 -query: | - let threshold = 100; - let timeRange = ago(14d); - let timeBuffer = 1; - SigninLogs - | where TimeGenerated > timeRange - | where ResultType == "50057" - | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), disabledAccountLoginAttempts = count(), - disabledAccountsTargeted = dcount(UserPrincipalName), applicationsTargeted = dcount(AppDisplayName), disabledAccountSet = make_set(UserPrincipalName), - applicationSet = make_set(AppDisplayName) by IPAddress, AppId - | order by disabledAccountLoginAttempts desc - | join kind=inner ( - // IPs are considered suspicious - and any related successful sign-ins are detected - SigninLogs - | where TimeGenerated > timeRange - | where ResultType == 0 - | summarize successSigninStart = min(TimeGenerated), successSigninEnd = max(TimeGenerated), successfulAccountSigninCount = dcount(UserPrincipalName), successfulAccountSigninSet = make_set(UserPrincipalName, 15) by IPAddress - // Assume IPs associated with sign-ins from 100+ distinct user accounts are safe - | where successfulAccountSigninCount < threshold - ) on IPAddress - // IPs where attempts to authenticate as disabled user accounts originated, and had a non-zero success rate for some other account - | where successfulAccountSigninCount != 0 - // Successful Account Signins occur within the same lookback period as the failed - | extend SuccessBeforeFailure = iff(successSigninStart >= StartTime and successSigninEnd <= EndTime, true, false) - | project StartTime, EndTime, IPAddress, disabledAccountLoginAttempts, disabledAccountsTargeted, disabledAccountSet, applicationSet, - successfulAccountSigninCount, successfulAccountSigninSet, successSigninStart, successSigninEnd, AppId - | order by disabledAccountLoginAttempts - // Break up the string of Succesfully signed into accounts into individual events - | mvexpand successfulAccountSigninSet - | extend IPCustomEntity = IPAddress - | join kind = inner ( - OfficeActivity - | where TimeGenerated > timeRange - | 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 (w3wp)', 'devilfish-applicationaccount')) - // Remove port from the end of the IP and/or square brackets around IP, if they exist - | extend IPCustomEntity = case( - ClientIP matches regex @'\[((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\]-\d{1,5}', tostring(extract('\\[([0-9]+\\.[0-9]+\\.[0-9]+)\\]-[0-9]+', 1, ClientIP)), - ClientIP matches regex @'\[((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\]', tostring(extract('\\[([0-9]+\\.[0-9]+\\.[0-9]+)\\]', 1, ClientIP)), - ClientIP matches regex @'(((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?))-\d{1,5}', tostring(extract('([0-9]+\\.[0-9]+\\.[0-9]+)-[0-9]+', 1, ClientIP)), - ClientIP matches regex @'((25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]2[0-4][0-9]|[01]?[0-9][0-9]?)', ClientIP, - ClientIP matches regex @'\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})\]-\d{1,5}', tostring(extract('\\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\\d{1,3}(?:\\.\\d{1,3}){3})\\]-[0-9]+', 1, ClientIP)), - ClientIP matches regex @'\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})\]', tostring(extract('\\[((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\\d{1,3}(?:\\.\\d{1,3}){3})\\]', 1, ClientIP)), - ClientIP matches regex @'((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})-\d{1,5}', tostring(extract('((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\\d{1,3}(?:\\.\\d{1,3}){3})-[0-9]+', 1, ClientIP)), - ClientIP matches regex @'((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})', ClientIP, - "") - | where isnotempty(IPCustomEntity) - | extend OfficeTimeStamp = ElevationTime - ) on IPCustomEntity - // Rare and risky operations only happen within a certain time range of the successful sign-in - | where OfficeTimeStamp >= successSigninStart and datetime_diff('day', OfficeTimeStamp, successSigninEnd) <= timeBuffer -entityMappings: - - entityType: Account - fieldMappings: - - identifier: FullName - columnName: UserPrincipalName - - identifier: Name - columnName: AppDisplayName - - entityType: IP - fieldMappings: - - identifier: Address - columnName: IPCustomEntity - - identifier: Address - columnName: IPAddress - - identifier: Address - columnName: ClientIP - - entityType: CloudApplication - fieldMappings: - - identifier: AppId - columnName: ApplicationId - #AccountObjectId does not exist in SigninLogs -version: 1.0.0 \ No newline at end of file From 9c345295b62a4a590a11446e74120988175b7967 Mon Sep 17 00:00:00 2001 From: IshaDave <137959176+IshaDave@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:55:44 -0700 Subject: [PATCH 4/7] Added UserId mapping from OfficeActivity --- .../DisabledAccIPSigninWithRareRiskyOps.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml index e687c1e5154..70c6d4ed5e4 100644 --- a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml +++ b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml @@ -85,6 +85,8 @@ entityMappings: columnName: UserPrincipalName - identifier: Name columnName: AppDisplayName + - identifier: FullName + columnName: UserId - entityType: IP fieldMappings: - identifier: Address From d5be3f33c474ac40568b5ca6010c68d631dd87f6 Mon Sep 17 00:00:00 2001 From: IshaDave <137959176+IshaDave@users.noreply.github.com> Date: Mon, 30 Oct 2023 17:24:25 -0700 Subject: [PATCH 5/7] Minor quotes fix --- .../DisabledAccIPSigninWithRareRiskyOps.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml index 70c6d4ed5e4..404bbd3c6d7 100644 --- a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml +++ b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml @@ -1,13 +1,11 @@ id: 9adbd1c3-a4be-44ef-ac2f-503fd25692ee name: High risk Office operation conducted by IP Address that recently attempted to log into a disabled account description: | - ' - It is possible that a disabled user account is compromised and another account on the same IP is used to perform operations that are not typical for that user. + 'It is possible that a disabled user account is compromised and another account on the same IP is used to perform operations that are not typical for that user. The query filters the SigninLogs for entries where ResultType is indicates a disabled account and the TimeGenerated is within a defined time range. It then summarizes these entries by IPAddress and AppId, calculating various statistics such as number of login attempts, distinct UPNs, App IDs etc and joins these results with another set of results from SigninLogs, filtering for entries with less than normal number of successful sign-ins. It then filters out entries where there were no successful sign-ins or where successful sign-ins did not occur within the same lookback period as the failed sign-ins, later projecting relevant fields by the count of login attempts, and expands the set of successful sign-ins into individual events. - Finally, it joins these results with entries from OfficeActivity where certain operations deemed rare and high risk have been performed, ensuring their occurrance within a certain time range of the successful sign-ins. - ' + Finally, it joins these results with entries from OfficeActivity where certain operations deemed rare and high risk have been performed, ensuring their occurrance within a certain time range of the successful sign-ins.' severity: Medium requiredDataConnectors: - connectorId: AzureActiveDirectory From 15515ffcfff07fdf8b80eb877665f4336122f3b1 Mon Sep 17 00:00:00 2001 From: IshaDave <137959176+IshaDave@users.noreply.github.com> Date: Mon, 30 Oct 2023 17:44:41 -0700 Subject: [PATCH 6/7] Fixed entityMappings --- .../DisabledAccIPSigninWithRareRiskyOps.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml index 404bbd3c6d7..db13cfd922c 100644 --- a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml +++ b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml @@ -72,7 +72,7 @@ query: | ClientIP matches regex @'((?:[0-9a-fA-F]{1,4}::?){1,8}[0-9a-fA-F]{1,4}|\d{1,3}(?:\.\d{1,3}){3})', ClientIP, "") | where isnotempty(IPCustomEntity) - | extend OfficeTimeStamp = ElevationTime + | extend OfficeTimeStamp = ElevationTime, UserPrincipalName = UserId ) on IPCustomEntity // Rare and risky operations only happen within a certain time range of the successful sign-in | where OfficeTimeStamp >= successSigninStart and datetime_diff('day', OfficeTimeStamp, successSigninEnd) <= timeBuffer @@ -83,14 +83,16 @@ entityMappings: columnName: UserPrincipalName - identifier: Name columnName: AppDisplayName - - identifier: FullName - columnName: UserId - entityType: IP fieldMappings: - identifier: Address columnName: IPCustomEntity + - entityType: IP + fieldMappings: - identifier: Address columnName: IPAddress + - entityType: IP + fieldMappings: - identifier: Address columnName: ClientIP - entityType: CloudApplication From db4615e6813e9775635c998d000e490c17a3ac02 Mon Sep 17 00:00:00 2001 From: IshaDave <137959176+IshaDave@users.noreply.github.com> Date: Tue, 31 Oct 2023 10:20:03 -0700 Subject: [PATCH 7/7] author name change --- .../DisabledAccIPSigninWithRareRiskyOps.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml index db13cfd922c..dad937be440 100644 --- a/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml +++ b/Detections/MultipleDataSources/DisabledAccIPSigninWithRareRiskyOps.yaml @@ -105,7 +105,7 @@ metadata: source: kind: Community author: - name: Isha Dave + name: Microsoft Security Research support: tier: Community categories: