Query Details
// =========================================================
// RULE-24 | AD-Disabled-Account-Reactivated
// Description : "Walking Dead" — Disabled privileged account
// re-enabled. Event 4722 (User Account Enabled)
// for an account that previously held membership
// in a high-privilege group (Domain Admins,
// Enterprise Admins, Backup Operators, etc.)
// within the last 30 days.
// Disabled accounts retain all their group
// memberships, ACLs, SPNs, and attributes.
// Re-enabling them instantly restores all
// accumulated privileges — making them a stealthy
// persistence and escalation vector.
// Severity : High → Critical (DA/EA account re-enabled)
// Frequency : Every 15 minutes, look-back 15 minutes
// MITRE : T1098 — Account Manipulation
// T1078 — Valid Accounts
// Tables : SecurityEvent
// =========================================================
let LookBack = 15m;
// Groups that make an account "privileged"
let PrivilegedGroups = dynamic([
"Domain Admins", "Enterprise Admins", "Schema Admins",
"Administrators", "Backup Operators", "Server Operators",
"Account Operators", "DnsAdmins", "Group Policy Creator Owners",
"Remote Desktop Users", "Network Configuration Operators",
"Cert Publishers"
]);
// Recent membership in privileged groups (last 30 days)
let RecentPrivilegedMembers = SecurityEvent
| where TimeGenerated > ago(30d)
| where EventID in (4728, 4732, 4756)
| where TargetUserName has_any (PrivilegedGroups)
| summarize
GroupMemberships = make_set(TargetUserName, 10),
IsTierZero = anyif(true,
TargetUserName has_any ("Domain Admins", "Enterprise Admins",
"Schema Admins", "Administrators"))
by PrivMember = tolower(MemberName);
// Accounts re-enabled in the detection window
SecurityEvent
| where TimeGenerated > ago(LookBack)
| where EventID == 4722 // User Account Enabled
| extend
ReenabledAccount = tolower(TargetUserName),
ReenabledBy = strcat(SubjectDomainName, "\\", SubjectUserName)
| join kind=inner (RecentPrivilegedMembers)
on $left.ReenabledAccount == $right.PrivMember
| extend
Severity = case(
IsTierZero, "Critical",
"High"
),
WhySuspicious = strcat(
"Disabled_Privileged_Account_Reenabled; ",
iff(IsTierZero, "WAS_DA_EA_SA_Critical; ", ""),
"Account: ", TargetUserName, "; ",
"PreviousGroups: ", strcat_array(GroupMemberships, ", "), "; ",
"ReenabledBy: ", ReenabledBy
)
| project
TimeGenerated,
Severity,
WhySuspicious,
ReenabledAccount = TargetUserName,
ReenabledBy,
GroupMemberships,
IsTierZero,
Computer,
SubjectUserName,
SubjectDomainName
| order by Severity asc, TimeGenerated desc
This query is designed to detect and alert on the reactivation of previously disabled privileged user accounts in an Active Directory environment. Here's a simplified explanation of what it does:
Purpose: The query identifies "walking dead" accounts, which are disabled accounts that have been re-enabled. These accounts previously belonged to high-privilege groups, such as Domain Admins or Enterprise Admins, within the last 30 days. Reactivating such accounts can be a security risk because they regain all their previous privileges.
Severity: The severity of this event is considered high, and it is marked as critical if the account was part of top-tier groups like Domain Admins or Enterprise Admins.
Frequency: The query runs every 15 minutes and looks back over the last 15 minutes to catch any re-enabled accounts.
Detection Logic:
Output: The query outputs details such as the time of the event, the severity, why the account is suspicious, the account that was re-enabled, who re-enabled it, and the previous group memberships. It orders the results by severity and time.
MITRE ATT&CK Framework: The query relates to techniques T1098 (Account Manipulation) and T1078 (Valid Accounts), indicating potential misuse of account privileges.
Overall, this query helps in identifying potential security threats by monitoring the reactivation of privileged accounts, which could be used for unauthorized access or privilege escalation.

David Alonso
Released: March 24, 2026
Tables
Keywords
Operators