← Back to SOC feed Coverage →

Inactive or new account signins

kql MEDIUM Azure-Sentinel
T1078
AuditLogsBehaviorAnalyticsSigninLogs
huntingmicrosoftofficial
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-06-04T11:00:00Z · Confidence: medium

Hunt Hypothesis

Adversaries may be exploiting newly created or reactivated accounts to gain unauthorized access, leveraging these accounts to move laterally or exfiltrate data. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential compromise of dormant accounts that could be used as entry points for advanced threats.

KQL Query


let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let lookback = starttime - 14d;
let midtime = starttime - 7d;
let SigninsSummary = SigninLogs
| where TimeGenerated between(starttime..endtime)
// successful sign-in only
| where ResultType == 0
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), SigninLogs_ItemIds = make_set(_ItemId), loginCountToday=count() by UserPrincipalName, UserId, UserType, IPAddress
| join kind=leftanti (
   SigninLogs
   // historical successful sign-in
   | where TimeGenerated between(lookback..starttime)
   | where ResultType == 0
   | summarize by UserId
) on UserId;
// need to help BehaviorAnalytics query to limit only to Signins we are interested in
let onlyInactive = SigninsSummary | summarize make_set(UserPrincipalName);
let SigninsWithUEBA =
BehaviorAnalytics
| where TimeGenerated between(starttime..endtime)
| where ActionType in ('Sign-in','InteractiveLogon')
| where UserPrincipalName in~ (onlyInactive)
| extend ActivityInsights = parse_xml(ActivityInsights)
// only looked where FirstTimeUser items are True
| where ActivityInsights matches regex '\"FirstTimeUser([A-Za-z0-9]+)\":\"True\"'
// only exclude when Uncommon Among Peers is false as this helps remove expected first time usage, exception is we always show FirstTimeUserConnectedFromCountry == True
// also always keep InvestigationPriority if 1 or more
| where (not(ActivityInsights.FirstTimeUserUsedApp == 'True' and ActivityInsights.AppUncommonlyUsedAmongPeers == 'False') or InvestigationPriority > 0)
| where (not(ActivityInsights.FirstTimeUserConnectedViaBrowser == 'True' and ActivityInsights.BrowserUncommonlyUsedAmongPeers == 'False') or InvestigationPriority > 0)
| where (not(ActivityInsights.FirstTimeUserAccessedResource == 'True' and ActivityInsights.ResourceUncommonlyUsedAmongPeers == 'False') or InvestigationPriority > 0)
// for ISP, it makes more sense to exclude if Uncommon in Tenant or Uncommon among peers is false.
| where (not(ActivityInsights.FirstTimeUserConnectedViaISP == 'True' and (ActivityInsights.ISPUncommonlyUsedInTenant == 'False' or ActivityInsights.ISPUncommonlyUsedAmongPeers == 'False')) or InvestigationPriority > 0)
| extend UEBA_Insights = pack_dictionary("TimeGenerated", TimeGenerated, "ActivityInsights", ActivityInsights, "UsersInsights", UsersInsights, "DevicesInsights", DevicesInsights)
| summarize UEBA_ItemIds = make_set(_ItemId), UEBA_SourceRecordIds = make_set(SourceRecordId), UEBA_Insights = make_set(UEBA_Insights) by
UEBA_UserPrincipalName = UserPrincipalName, JoinedWithType = Type, UEBA_ActionType = ActionType, UEBA_SourceIPAddress = SourceIPAddress, UEBA_SourceIPLocation = SourceIPLocation, UEBA_InvestigationPriority = InvestigationPriority
| extend UEBA_Info = pack_dictionary("UEBA_Insights", UEBA_Insights, "UEBA_ItemIds", UEBA_ItemIds, "UEBA_SourceRecordIds", UEBA_SourceRecordIds)
| project-away UEBA_ItemIds, UEBA_SourceRecordIds, UEBA_Insights
| join kind=inner ( 
  SigninsSummary
) on $left.UEBA_UserPrincipalName == $right.UserPrincipalName, $left.UEBA_SourceIPAddress == $right.IPAddress
| project-reorder StartTime, EndTime, UserPrincipalName, UserId, IPAddress, UserType, loginCountToday, JoinedWithType
;
SigninsWithUEBA 
| join kind= leftanti (
   // filter out newly created user accounts from last 7 days
   AuditLogs
   | where TimeGenerated between(midtime..endtime)
   | where OperationName == "Add user"
   | summarize by NewUserId = tostring(TargetResources[0].id)
) on $left.UserId == $right.NewUserId
| extend timestamp = StartTime, AccountCustomEntity = UserPrincipalName, IPCustomEntity = IPAddress

Analytic Rule Definition

id: 847c2652-547d-4d5f-9b71-d2f8d81eac62
name: Inactive or new account signins
description: |
  'Query for new sign-ins from stale/inactive accounts. UEBA filters based on ActivityInsights. Results for accounts created in the last 7 days are filtered out.'
description_detailed: |
  'Query for accounts seen signing in for the first time. These could be associated with stale/inactive accounts that ought to have been deleted 
  but were not and may have been subsequently compromised.
  UEBA is used to filter out based on ActivityInsights where we see certain First Time User events identified as true.
  Results for user accounts created in the last 7 days are filtered out.'
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
      - AuditLogs
  - connectorId: BehaviorAnalytics
    dataTypes:
      - BehaviorAnalytics
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |

  let starttime = todatetime('{{StartTimeISO}}');
  let endtime = todatetime('{{EndTimeISO}}');
  let lookback = starttime - 14d;
  let midtime = starttime - 7d;
  let SigninsSummary = SigninLogs
  | where TimeGenerated between(starttime..endtime)
  // successful sign-in only
  | where ResultType == 0
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), SigninLogs_ItemIds = make_set(_ItemId), loginCountToday=count() by UserPrincipalName, UserId, UserType, IPAddress
  | join kind=leftanti (
     SigninLogs
     // historical successful sign-in
     | where TimeGenerated between(lookback..starttime)
     | where ResultType == 0
     | summarize by UserId
  ) on UserId;
  // need to help BehaviorAnalytics query to limit only to Signins we are interested in
  let onlyInactive = SigninsSummary | summarize make_set(UserPrincipalName);
  let SigninsWithUEBA =
  BehaviorAnalytics
  | where TimeGenerated between(starttime..endtime)
  | where ActionType in ('Sign-in','InteractiveLogon')
  | where UserPrincipalName in~ (onlyInactive)
  | extend ActivityInsights = parse_xml(ActivityInsights)
  // only looked where FirstTimeUser items are True
  | where ActivityInsights matches regex '\"FirstTimeUser([A-Za-z0-9]+)\":\"True\"'
  // only exclude when Uncommon Among Peers is false as this helps remove expected first time usage, exception is we always show FirstTimeUserConnectedFromCountry == True
  // also always keep InvestigationPriority if 1 or more
  | where (not(ActivityInsights.FirstTimeUserUsedApp == 'True' and ActivityInsights.AppUncommonlyUsedAmongPeers == 'False') or InvestigationPriority > 0)
  | where (not(ActivityInsights.FirstTimeUserConnectedViaBrowser == 'True' and ActivityInsights.BrowserUncommonlyUsedAmongPeers == 'False') or InvestigationPriority > 0)
  | where (not(ActivityInsights.FirstTimeUserAccessedResource == 'True' and ActivityInsights.ResourceUncommonlyUsedAmongPeers == 'False') or InvestigationPriority > 0)
  // for ISP, it makes more sense to exclude if Uncommon in Tenant or Uncommon among pee

Required Data Sources

Sentinel TableNotes
AuditLogsEnsure this data connector is enabled
BehaviorAnalyticsEnsure this data connector is enabled
SigninLogsEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/SigninLogs/InactiveAccounts.yaml