↳ Source GitHubRègle analytiqueMedium

Brute force attack against Azure Portal

Description

Detects Azure Portal brute force attacks by monitoring for multiple authentication failures and a successful login within a 20-minute window. Default settings: 10 failures, 25 deviations. Ref: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes.
Type de règle
Scheduled
Version
2.1.4
Statut déclaré
Available
Fréquence
1d
Période analysée
7d
Déclenchement
gt 0

Couverture MITRE déclarée

Sources déclarées

Métadonnées du fichier source. Aucune dépendance déduite du KQL.

Connecteurs

Types de données

Requête KQL

Requête originale, sans modification.

// Set threshold value for deviation
let threshold = 25;
// Set the time range for the query
let timeRange = 24h;
// Set the authentication window duration
let authenticationWindow = 20m;
// Define a reusable function 'aadFunc' that takes a table name as input
let aadFunc = (tableName: string) {
  // Query the specified table
  table(tableName)
  // Filter data within the last 24 hours
  | where TimeGenerated > ago(1d)
  // Filter records related to "Azure Portal" applications
  | where AppDisplayName has "Azure Portal"
  // Extract and transform some fields
  | extend
      DeviceDetail = todynamic(DeviceDetail),
      LocationDetails = todynamic(LocationDetails)
  | extend
      OS = tostring(DeviceDetail.operatingSystem),
      Browser = tostring(DeviceDetail.browser),
      State = tostring(LocationDetails.state),
      City = tostring(LocationDetails.city),
      Region = tostring(LocationDetails.countryOrRegion)
  // Categorize records as Success or Failure based on ResultType
  | extend FailureOrSuccess = iff(ResultType in ("0", "50125", "50140", "70043", "70044"), "Success", "Failure")
  // Sort and identify sessions
  | sort by UserPrincipalName asc, TimeGenerated asc
  | extend SessionStartedUtc = row_window_session(TimeGenerated, timeRange, authenticationWindow, UserPrincipalName != prev(UserPrincipalName) or prev(FailureOrSuccess) == "Success")
  // Summarize data
  | summarize FailureOrSuccessCount = count() by  FailureOrSuccess, UserId, UserDisplayName, AppDisplayName, IPAddress, Browser, OS, State, City, Region, Type, CorrelationId, bin(TimeGenerated, authenticationWindow), ResultType, UserPrincipalName, SessionStartedUtc
  | summarize FailureCountBeforeSuccess = sumif(FailureOrSuccessCount, FailureOrSuccess == "Failure"), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), makelist(FailureOrSuccess), IPAddress = make_set(IPAddress, 15), make_set(Browser, 15), make_set(City, 15), make_set(State, 15), make_set(Region, 15), make_set(ResultType, 15) by SessionStartedUtc, UserPrincipalName, CorrelationId, AppDisplayName, UserId, Type
  // Filter records where "Success" occurs in the middle of a session
  | where array_index_of(list_FailureOrSuccess, "Success") != 0
  | where array_index_of(list_FailureOrSuccess, "Success") == array_length(list_FailureOrSuccess) - 1
  // Remove unnecessary columns from the output
  | project-away SessionStartedUtc, list_FailureOrSuccess
  // Join with another table and calculate deviation
  | join kind=inner (
      table(tableName)
      | where TimeGenerated > ago(7d)
      | where AppDisplayName has "Azure Portal"
      | extend FailureOrSuccess = iff(ResultType in ("0", "50125", "50140", "70043", "70044"), "Success", "Failure")
      | summarize avgFailures = avg(todouble(FailureOrSuccess == "Failure")) by UserPrincipalName
  ) on UserPrincipalName
  | extend Deviation = abs(FailureCountBeforeSuccess - avgFailures) / avgFailures
  // Filter records based on deviation and failure count criteria
  | where Deviation > threshold and FailureCountBeforeSuccess >= 10
  // Expand the IPAddress array
  | mv-expand IPAddress
  | extend IPAddress = tostring(IPAddress)
  | extend timestamp = StartTime
};
// Call 'aadFunc' with different table names and union the results
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
union isfuzzy=true aadSignin, aadNonInt
// Additional transformation - Split UserPrincipalName
| extend Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])

Entités déclarées

AccountIP

Contenus associés

Liens établis à partir des identifiants déclarés et des manifests des solutions.

Traçabilité de la source

GitHub

Les valeurs affichées proviennent des fichiers du dépôt Azure/Azure-Sentinel. Elles décrivent le modèle publié, pas la configuration de votre workspace.

Identifiant source
28b42356-45af-40a6-a0b4-a554cdfd5d8a
Autres fichiers source 3Solutions/Microsoft Entra ID/Analytic Rules/SigninBruteForce-AzurePortal.yamlsource ↗Detections/SigninLogs/SigninBruteForce-AzurePortal.yamlmigration-note ↗Solutions/Microsoft Entra ID/Data/Solution_AAD.jsonsolution-membership ↗
GSTEP / SUIVI DU CATALOGUE

Ajouté au catalogue : 16 sept. 2026 · 05:49 UTC
Dernier changement observé : 16 sept. 2026 · 05:49 UTC

Dates de synchronisation GSTEP, distinctes des dates de publication du contenu source.