HUNT 24 Guest Invite Privileged Role
Query
// Hunt : Hunt - Guest (B2B) Invitation Followed by Privileged Role (30d)
// Tactics : Persistence, PrivilegeEscalation
// MITRE : T1078.004, T1098.003
// Purpose : Correlates external guest (B2B) invitations/redemptions with a subsequent
// privileged role assignment to that same guest within 7 days. Attackers who gain
// admin rights invite an attacker-controlled external identity and grant it standing
// access that survives the victim's password resets. Investigate every hit.
//==========================================================================================
let Window = 30d;
let CorrelationWindow = 7d;
let GuestInvites = AuditLogs
| where TimeGenerated > ago(Window)
| where OperationName has_any ("Invite external user", "Redeem external user invite", "Add external user")
| where Result =~ "success"
| extend GuestUPN = tostring(TargetResources[0].userPrincipalName)
| extend GuestId = tostring(TargetResources[0].id)
| extend Inviter = coalesce(tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))
| where isnotempty(GuestId)
| project InviteTime = TimeGenerated, GuestUPN, GuestId, Inviter;
let PrivRoleGrants = AuditLogs
| where TimeGenerated > ago(Window)
| where OperationName has_any ("Add member to role", "Add eligible member to role",
"Add app role assignment to service principal")
| where Result =~ "success"
| extend RoleName = tostring(TargetResources[0].displayName)
| mv-expand TR = TargetResources
| extend GranteeId = tostring(TR.id), GranteeUPN = tostring(TR.userPrincipalName)
| where isnotempty(GranteeId)
| project RoleTime = TimeGenerated, RoleName, GranteeId, GranteeUPN,
Grantor = coalesce(tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName));
GuestInvites
| join kind=inner PrivRoleGrants on $left.GuestId == $right.GranteeId
| where RoleTime between (InviteTime .. (InviteTime + CorrelationWindow))
| project InviteTime, RoleTime, GuestUPN, Inviter, RoleName, Grantor, GuestId
| order by RoleTime descExplanation
This query is designed to detect potential security threats involving external guest users (B2B) who are invited to an organization and then granted privileged roles within a short period. Here's a simplified explanation of what the query does:
-
Time Frame: It looks at activities within the last 30 days.
-
Guest Invitations: It identifies successful invitations or redemptions of external users (guests) by checking logs for operations like "Invite external user" or "Redeem external user invite." It captures details like the guest's user principal name (UPN), ID, and who invited them.
-
Privileged Role Assignments: It also checks for successful assignments of privileged roles to users by looking for operations like "Add member to role." It captures details such as the role name, the user who received the role, and who granted the role.
-
Correlation: The query then correlates these two activities by matching the guest's ID with the ID of the user who received a privileged role. It specifically looks for cases where the role assignment happened within 7 days after the guest invitation.
-
Output: The results include the times of invitation and role assignment, the guest's UPN, the inviter, the role name, the grantor, and the guest's ID. The results are sorted by the time of role assignment in descending order.
The purpose of this query is to identify potential security risks where an attacker might invite an external identity they control and then grant it privileged access, which could persist even after password resets. Each result should be investigated to ensure there is no unauthorized access.
