Azure Portal Signin from another Azure Tenant
Id | 87210ca1-49a4-4a7d-bb4a-4988752f978c |
Rulename | Azure Portal Signin from another Azure Tenant |
Description | This query looks for sign in attempts to the Azure Portal where the user who is signing in from another Azure tenant, and the IP address the login attempt is from is an Azure IP. A threat actor who compromises an Azure tenant may look to pivot to other tenants leveraging cross-tenant delegated access in this manner. |
Severity | Medium |
Tactics | InitialAccess |
Techniques | T1199 |
Required data connectors | AzureActiveDirectory |
Kind | Scheduled |
Query frequency | 1h |
Query period | 1h |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/AzurePortalSigninfromanotherAzureTenant.yaml |
Version | 1.4.0 |
Arm template | 87210ca1-49a4-4a7d-bb4a-4988752f978c.json |
// Get details of current Azure Ranges (note this URL updates regularly so will need to be manually updated over time)
// You may find the name of the new JSON here: https://www.microsoft.com/download/details.aspx?id=56519
// On the downloads page, click the 'details' button, and then replace just the filename in the URL below
let azure_ranges = externaldata(changeNumber: string, cloud: string, values: dynamic)
["https://raw.githubusercontent.com/microsoft/mstic/master/PublicFeeds/MSFTIPRanges/ServiceTags_Public.json"] with(format='multijson')
| mv-expand values
| extend Name = values.name, AddressPrefixes = values.properties.addressPrefixes
| mv-expand AddressPrefixes
| summarize by tostring(AddressPrefixes);
SigninLogs
// Limiting to Azure Portal really reduces false positives and helps focus on potential admin activity
| where AppDisplayName =~ "Azure Portal"
// Only get logons where the IP address is in an Azure range
| evaluate ipv4_lookup(azure_ranges, IPAddress, AddressPrefixes)
// Limit to where the user is external to the tenant
| where HomeTenantId != ResourceTenantId
// Further limit it to just access to the current tenant (you can drop this if you wanted to look elsewhere as well but it helps reduce FPs)
| where ResourceTenantId == AADTenantId
| summarize FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), make_set(ResourceDisplayName) by UserPrincipalName, IPAddress, UserAgent, Location, HomeTenantId, ResourceTenantId
| extend AccountCustomEntity = UserPrincipalName, IPCustomEntity = IPAddress
name: Azure Portal Signin from another Azure Tenant
query: |
// Get details of current Azure Ranges (note this URL updates regularly so will need to be manually updated over time)
// You may find the name of the new JSON here: https://www.microsoft.com/download/details.aspx?id=56519
// On the downloads page, click the 'details' button, and then replace just the filename in the URL below
let azure_ranges = externaldata(changeNumber: string, cloud: string, values: dynamic)
["https://raw.githubusercontent.com/microsoft/mstic/master/PublicFeeds/MSFTIPRanges/ServiceTags_Public.json"] with(format='multijson')
| mv-expand values
| extend Name = values.name, AddressPrefixes = values.properties.addressPrefixes
| mv-expand AddressPrefixes
| summarize by tostring(AddressPrefixes);
SigninLogs
// Limiting to Azure Portal really reduces false positives and helps focus on potential admin activity
| where AppDisplayName =~ "Azure Portal"
// Only get logons where the IP address is in an Azure range
| evaluate ipv4_lookup(azure_ranges, IPAddress, AddressPrefixes)
// Limit to where the user is external to the tenant
| where HomeTenantId != ResourceTenantId
// Further limit it to just access to the current tenant (you can drop this if you wanted to look elsewhere as well but it helps reduce FPs)
| where ResourceTenantId == AADTenantId
| summarize FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), make_set(ResourceDisplayName) by UserPrincipalName, IPAddress, UserAgent, Location, HomeTenantId, ResourceTenantId
| extend AccountCustomEntity = UserPrincipalName, IPCustomEntity = IPAddress
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/AzurePortalSigninfromanotherAzureTenant.yaml
queryFrequency: 1h
triggerThreshold: 0
requiredDataConnectors:
- dataTypes:
- SigninLogs
connectorId: AzureActiveDirectory
version: 1.4.0
status: Available
queryPeriod: 1h
id: 87210ca1-49a4-4a7d-bb4a-4988752f978c
triggerOperator: gt
entityMappings:
- fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
entityType: Account
- fieldMappings:
- identifier: Address
columnName: IPCustomEntity
entityType: IP
relevantTechniques:
- T1199
severity: Medium
description: |
'This query looks for sign in attempts to the Azure Portal where the user who is signing in from another Azure tenant,
and the IP address the login attempt is from is an Azure IP. A threat actor who compromises an Azure tenant may look
to pivot to other tenants leveraging cross-tenant delegated access in this manner.'
kind: Scheduled
tactics:
- InitialAccess
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/87210ca1-49a4-4a7d-bb4a-4988752f978c')]",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/87210ca1-49a4-4a7d-bb4a-4988752f978c')]",
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
"kind": "Scheduled",
"apiVersion": "2022-11-01",
"properties": {
"displayName": "Azure Portal Signin from another Azure Tenant",
"description": "'This query looks for sign in attempts to the Azure Portal where the user who is signing in from another Azure tenant,\n and the IP address the login attempt is from is an Azure IP. A threat actor who compromises an Azure tenant may look\n to pivot to other tenants leveraging cross-tenant delegated access in this manner.'\n",
"severity": "Medium",
"enabled": true,
"query": "// Get details of current Azure Ranges (note this URL updates regularly so will need to be manually updated over time)\n// You may find the name of the new JSON here: https://www.microsoft.com/download/details.aspx?id=56519\n// On the downloads page, click the 'details' button, and then replace just the filename in the URL below\nlet azure_ranges = externaldata(changeNumber: string, cloud: string, values: dynamic)\n[\"https://raw.githubusercontent.com/microsoft/mstic/master/PublicFeeds/MSFTIPRanges/ServiceTags_Public.json\"] with(format='multijson')\n| mv-expand values\n| extend Name = values.name, AddressPrefixes = values.properties.addressPrefixes\n| mv-expand AddressPrefixes\n| summarize by tostring(AddressPrefixes);\nSigninLogs\n// Limiting to Azure Portal really reduces false positives and helps focus on potential admin activity\n| where AppDisplayName =~ \"Azure Portal\"\n// Only get logons where the IP address is in an Azure range\n| evaluate ipv4_lookup(azure_ranges, IPAddress, AddressPrefixes)\n// Limit to where the user is external to the tenant\n| where HomeTenantId != ResourceTenantId\n// Further limit it to just access to the current tenant (you can drop this if you wanted to look elsewhere as well but it helps reduce FPs)\n| where ResourceTenantId == AADTenantId\n| summarize FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), make_set(ResourceDisplayName) by UserPrincipalName, IPAddress, UserAgent, Location, HomeTenantId, ResourceTenantId\n| extend AccountCustomEntity = UserPrincipalName, IPCustomEntity = IPAddress\n",
"queryFrequency": "PT1H",
"queryPeriod": "PT1H",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0,
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"InitialAccess"
],
"techniques": [
"T1199"
],
"alertRuleTemplateName": "87210ca1-49a4-4a7d-bb4a-4988752f978c",
"customDetails": null,
"entityMappings": [
{
"fieldMappings": [
{
"identifier": "FullName",
"columnName": "AccountCustomEntity"
}
],
"entityType": "Account"
},
{
"fieldMappings": [
{
"identifier": "Address",
"columnName": "IPCustomEntity"
}
],
"entityType": "IP"
}
],
"status": "Available",
"templateVersion": "1.4.0",
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/AzurePortalSigninfromanotherAzureTenant.yaml"
}
}
]
}