Query Details

Multiple Unexpected Account Using A Power Shell App In Entra ID

Query

let query_frequency = 1h;
let query_period = 14d;
let _ShellApps = toscalar(
    _GetWatchlist("UUID-EntraIdApps")
    | where Notes has "[PowerShell]"
    | summarize make_list(AppId)
);
// let _HomeTenantId = toscalar(
//     _GetWatchlist("UUID-EntraIdTenantIds")
//     | where Notes has "[HomeTenant]"
//     | summarize make_list(TenantId)
// );
let _ExpectedResultTypes = toscalar(
    _GetWatchlist("ResultType-SignInLogsErrorCodes")
    | where isnotempty(ResultDescription) and not(Notes has_any ("[Success]", "[Expired]"))
    | summarize make_list(ResultType)
);
let MicrosoftAzureCLI_appid = "04b07795-8ddb-461a-bbee-02f9e1bf7b46";
let _MicrosoftAzureCLIExpectedResultTypes = toscalar(
    _GetWatchlist("ResultType-SignInLogsErrorCodes")
    | where (Notes has_all ("[Success]", "[Interrupt]") and Notes has_any ("[MFA]", "[NotCompliant]")) or (Notes has_all ("[Token]", "[Expired]"))
    | summarize make_list(ResultType)
);
let _ExpectedADGroups = toscalar(
    _GetWatchlist("RegEx-PrivDomainGroups")
    | where Notes has "[PowerShell]"
    | summarize RegEx = make_list(RegEx)
    | extend RegEx = strcat(@'^(', strcat_array(RegEx, '|'), @')$')
);
let _ExpectedAADRoles = toscalar(
    _GetWatchlist("RegEx-PrivAADRoles")
    | where Notes has "[PowerShell]"
    | summarize RegEx = make_list(RegEx)
    | extend RegEx = strcat(@'^(', strcat_array(RegEx, '|'), @')$')
);
let _Users =
    IdentityInfo
    | where TimeGenerated between (ago(query_period) .. ago(query_frequency))
    | summarize arg_max(TimeGenerated, *) by AccountObjectId
    | project-away TimeGenerated
;
let _ExpectedUsers =
    _Users
    | mv-expand GroupMembership, AssignedRoles
    | where GroupMembership matches regex _ExpectedADGroups or AssignedRoles matches regex _ExpectedAADRoles
    | distinct AccountObjectId
;
union SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(query_frequency)
| where AppId in (_ShellApps)
//| where ResourceTenantId in (_HomeTenantId) // Remove events where the accessed resource is not from our tenant
| where not(ResultType in (_ExpectedResultTypes))
| where not(UserId in (_ExpectedUsers))
// Remove unsuccessful guest signins from app "Microsoft Azure CLI", when they use this app we assume a signin event happens in all tenants where they are a guest
| where not(AppId == MicrosoftAzureCLI_appid and ResultType in (_MicrosoftAzureCLIExpectedResultTypes))// and (UserType != "Member" or not(HomeTenantId in (_HomeTenantId))))
| summarize arg_max(TimeGenerated, *) by UserId, SessionId, ResultType, IPAddress, AppDisplayName, AppId, ResourceDisplayName, ResourceIdentity
| extend DeviceDetail = coalesce(tostring(DeviceDetail_dynamic), DeviceDetail_string)
| summarize
    StartTime = min(TimeGenerated),
    EndTime = max(TimeGenerated),
    Resources = make_set(ResourceDisplayName),
    SessionIds = make_set(SessionId),
    CorrelationIds = make_set(CorrelationId),
    ResultTypes = make_set(ResultType),
    take_anyif(UserPrincipalName, not(UserPrincipalName == UserId)),
    take_any(Location, ClientAppUsed, UserAgent, AuthenticationDetails, RiskEventTypes, RiskLevelDuringSignIn, RiskLevelAggregated, DeviceDetail)
    by UserId, IPAddress, AppDisplayName, AppId
| join kind=leftouter _Users on $left.UserId == $right.AccountObjectId
| extend UserPrincipalName = tolower(coalesce(UserPrincipalName, MailAddress, AccountUPN))
| project
    StartTime,
    EndTime,
    UserPrincipalName,
    AccountDisplayName,
    IPAddress,
    Location,
    ResultTypes,
    AppDisplayName,
    Resources,
    ClientAppUsed,
    DeviceDetail,
    UserAgent,
    UserType,
    GroupMembership,
    AssignedRoles,
    AccountSID,
    OnPremisesDistinguishedName,
    MailAddress,
    JobTitle,
    Department,
    Manager,
    UserAccountControl,
    RiskLevel,
    RiskState,
    AuthenticationDetails,
    SessionIds,
    CorrelationIds,
    RiskEventTypes,
    RiskLevelDuringSignIn,
    RiskLevelAggregated,
    AppId,
    UserId

Explanation

This KQL (Kusto Query Language) query is designed to analyze and summarize sign-in activities related to specific applications and user roles within a given time frame. Here's a simplified breakdown of what the query does:

  1. Define Time Frames:

    • query_frequency is set to 1 hour, meaning the query looks at data from the last hour.
    • query_period is set to 14 days, which is used to gather user information over the past 14 days.
  2. Identify Relevant Applications and Roles:

    • _ShellApps: Retrieves a list of application IDs related to PowerShell from a watchlist.
    • _ExpectedResultTypes: Retrieves a list of error codes from sign-in logs that are not considered successful or expired.
    • _MicrosoftAzureCLIExpectedResultTypes: Retrieves specific error codes for the "Microsoft Azure CLI" application that indicate unsuccessful sign-ins.
    • _ExpectedADGroups and _ExpectedAADRoles: Retrieve regular expressions for identifying privileged domain groups and Azure AD roles related to PowerShell.
  3. Gather User Information:

    • _Users: Collects the most recent information about users from the past 14 days.
    • _ExpectedUsers: Filters users who belong to specific privileged groups or roles.
  4. Filter Sign-In Logs:

    • The query examines sign-in logs from the past hour for applications identified in _ShellApps.
    • It excludes events with expected error codes and users who are part of expected groups or roles.
    • It also filters out unsuccessful guest sign-ins for the "Microsoft Azure CLI" application.
  5. Summarize and Join Data:

    • The query summarizes the filtered sign-in events, grouping them by user and session details.
    • It joins this data with user information to enrich the results with additional user attributes.
  6. Output:

    • The final result includes details such as start and end times of sign-in events, user principal names, IP addresses, application names, and various user attributes like job title and department.

Overall, this query is used to identify and summarize unusual or unexpected sign-in activities related to specific applications and roles, helping to detect potential security issues or unauthorized access attempts.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: July 30, 2025

Tables

UUID-EntraIdAppsResultType-SignInLogsErrorCodesRegEx-PrivDomainGroupsRegEx-PrivAADRolesIdentityInfoSigninLogsAADNonInteractiveUserSignInLogs

Keywords

QueryFrequencyQueryPeriodShellAppsAppIdResultTypesMicrosoftAzureCLIExpectedResultTypesExpectedADGroupsExpectedAADRolesUsersIdentityInfoSigninLogsAADNonInteractiveUserSignInLogsTimeGeneratedUserIdSessionIdIPAddressAppDisplayNameResourceDisplayNameResourceIdentityDeviceDetailUserPrincipalNameLocationClientAppUsedUserAgentAuthenticationDetailsRiskEventTypesRiskLevelDuringSignInRiskLevelAggregatedAccountObjectIdAccountDisplayNameUserTypeGroupMembershipAssignedRolesAccountSIDOnPremisesDistinguishedNameMailAddressJobTitleDepartmentManagerUserAccountControlRiskLevelRiskStateCorrelationIds

Operators

lettoscalar_GetWatchlisthassummarizemake_listisnotemptynothas_anyhas_allstrcatstrcat_arraybetweenagoarg_maxproject-awaymv-expandmatchesregexdistinctunionin==coalescetostringdynamicstringtake_anyifjoinkindleftouterontolowerproject

Actions