Query Details

RULE 32 AD Accessibility Feature IFEO Backdoor

Query

// ============================================================
// RULE-32 | AD Security: Accessibility Feature / IFEO Backdoor
// ============================================================
// Description:
//   Detects persistence/privilege escalation via Image File Execution Options
//   (IFEO) debugger hijacking of Windows accessibility binaries (Sticky Keys,
//   Utilman, Narrator, On-Screen Keyboard, Magnifier, Display Switch, ATBroker).
//   Attackers (e.g., Sticky-Keys-Slayer) replace or redirect sethc.exe/utilman.exe
//   to cmd.exe, allowing SYSTEM-level shell access from the Windows logon screen
//   via the accessibility shortcut keys — no credentials required.
//
//   Detection covers TWO attack vectors:
//     1. Registry modification to IFEO debugger key (EventID 4657)
//     2. Suspicious process spawn where accessibility binary is parent (EventID 4688)
//
// Mitre ATT&CK:
//   - T1546.008: Event Triggered Execution — Accessibility Features
//   - T1543:     Create or Modify System Process
//   - TA0003:    Persistence
//   - TA0004:    Privilege Escalation
//
// Data Sources: SecurityEvent (EventID 4657, 4688), DeviceProcessEvents
// Severity:     High
// Frequency:    Every 10 minutes | Lookback 10 minutes
// Alert Suppression: 4 hours per affected host
// ============================================================

// ──────────────────────────────────────────────────────────────
// PART 1 — Registry IFEO Debugger Key Write (EventID 4657)
//   Auditing prerequisite: Enable "Audit Registry" in Advanced Audit Policy
// ──────────────────────────────────────────────────────────────
let AccessibilityBinaries = dynamic([
    "sethc.exe",        // Sticky Keys (Shift x5)
    "utilman.exe",      // Utility Manager (Win+U)
    "osk.exe",          // On-Screen Keyboard
    "magnify.exe",      // Magnifier
    "narrator.exe",     // Narrator
    "displayswitch.exe",// Display Switch (Win+P)
    "atbroker.exe"      // Assistive Technology Broker
]);
let SuspiciousDebuggers = dynamic([
    "cmd.exe", "powershell.exe", "powershell_ise.exe",
    "pwsh.exe", "mshta.exe", "wscript.exe", "cscript.exe",
    "rundll32.exe", "regsvr32.exe", "msiexec.exe",
    "net.exe", "whoami.exe"
]);
// --- Vector 1: IFEO registry write ---
let IFEORegistryWrites = SecurityEvent
| where TimeGenerated >= ago(10m)
| where EventID == 4657
| where ObjectName has_any (
    @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe",
    @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\utilman.exe",
    @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\osk.exe",
    @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\magnify.exe",
    @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\narrator.exe",
    @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\displayswitch.exe",
    @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\atbroker.exe"
)
| where ObjectValueName =~ "Debugger" or ObjectValueName =~ "GlobalFlag"
| extend
    Actor              = tolower(strcat(SubjectDomainName, "\\", SubjectUserName)),
    AffectedHost       = Computer,
    TargetKey          = ObjectName,
    ValueName          = ObjectValueName,
    NewValue           = NewValue,
    OldValue           = OldValue,
    DetectionVector    = "IFEO_RegistryWrite",
    MITRE_Technique    = "T1546.008"
| project
    TimeGenerated, AffectedHost, Actor,
    TargetKey, ValueName, OldValue, NewValue,
    DetectionVector, MITRE_Technique;
// --- Vector 2: Accessibility binary spawns shell (EventID 4688) ---
let AccessibilityShellSpawn = SecurityEvent
| where TimeGenerated >= ago(10m)
| where EventID == 4688
| extend
    ParentProcess = tolower(tostring(extract(@"([^\\]+)$", 1, ParentProcessName))),
    ChildProcess  = tolower(tostring(extract(@"([^\\]+)$", 1, NewProcessName)))
| where ParentProcess in~ (AccessibilityBinaries)
| where ChildProcess in~ (SuspiciousDebuggers)
| extend
    Actor              = tolower(strcat(SubjectDomainName, "\\", SubjectUserName)),
    AffectedHost       = Computer,
    TargetKey          = strcat("_AccessibilityParent:", ParentProcess),
    ValueName          = "ProcessSpawn",
    OldValue           = ParentProcess,
    NewValue           = strcat(NewProcessName, " ", CommandLine),
    DetectionVector    = "AccessibilityBinary_ShellSpawn",
    MITRE_Technique    = "T1546.008"
| project
    TimeGenerated, AffectedHost, Actor,
    TargetKey, ValueName, OldValue, NewValue,
    DetectionVector, MITRE_Technique;
// --- Vector 3: DeviceProcessEvents (MDE) - accessibility binary parent ---
let MDEAccessibilitySpawn = DeviceProcessEvents
| where TimeGenerated >= ago(10m)
| extend
    ParentBin = tolower(tostring(extract(@"([^\\]+)$", 1, InitiatingProcessFileName))),
    ChildBin  = tolower(FileName)
| where ParentBin in~ (AccessibilityBinaries)
| where ChildBin in~ (SuspiciousDebuggers)
| extend
    Actor              = tolower(strcat(InitiatingProcessAccountDomain, "\\", InitiatingProcessAccountName)),
    AffectedHost       = DeviceName,
    TargetKey          = strcat("_AccessibilityParent:", ParentBin),
    ValueName          = "ProcessSpawn",
    OldValue           = InitiatingProcessFileName,
    NewValue           = strcat(FolderPath, "\\", FileName, " | ", ProcessCommandLine),
    DetectionVector    = "MDE_AccessibilitySpawn",
    MITRE_Technique    = "T1546.008"
| project
    TimeGenerated, AffectedHost, Actor,
    TargetKey, ValueName, OldValue, NewValue,
    DetectionVector, MITRE_Technique;
// ──────────────────────────────────────────────────────────────
// UNION all vectors and finalize
// ──────────────────────────────────────────────────────────────
union IFEORegistryWrites, AccessibilityShellSpawn, MDEAccessibilitySpawn
| summarize
    FirstDetected   = min(TimeGenerated),
    LastDetected    = max(TimeGenerated),
    EventCount      = count(),
    Vectors         = make_set(DetectionVector),
    NewValues       = make_set(NewValue),
    OldValues       = make_set(OldValue),
    AffectedKeys    = make_set(TargetKey)
    by AffectedHost, Actor, MITRE_Technique
| extend
    Severity = iff(
        Vectors has "IFEO_RegistryWrite" and (Vectors has "AccessibilitySpawn" or Vectors has "MDE"),
        "Critical",   // Both registry write AND spawn observed — confirmed exploitation
        "High"        // Either vector alone is already high-confidence
    ),
    AlertName = strcat("Accessibility Feature Backdoor (IFEO): ", Actor, " on ", AffectedHost)
| project-reorder
    FirstDetected, LastDetected, Severity, AlertName,
    AffectedHost, Actor,
    Vectors, AffectedKeys, NewValues, OldValues,
    EventCount, MITRE_Technique

Explanation

This query is designed to detect potential security threats on Windows systems related to the misuse of accessibility features for persistence or privilege escalation. Here's a simplified breakdown:

  1. Purpose: The query aims to identify if attackers are exploiting Windows accessibility features to gain unauthorized access or maintain persistence on a system. This is done by hijacking the Image File Execution Options (IFEO) debugger settings for certain accessibility binaries (like Sticky Keys or Utility Manager) to execute malicious commands.

  2. Detection Vectors:

    • Registry Modification: It checks for changes in the Windows registry related to IFEO settings, specifically looking for modifications to the debugger keys of accessibility binaries. This is captured by EventID 4657. - Suspicious Process Creation: It monitors for suspicious processes being spawned by accessibility binaries, captured by EventID 4688. This could indicate that a legitimate accessibility feature is being used to launch a command shell or other suspicious processes.
    • Device Process Events: It also checks for similar suspicious process activities using data from DeviceProcessEvents, which provides additional context and verification.
  3. Data Sources: The query uses security events (EventID 4657 and 4688) and device process events to gather data.

  4. Severity and Frequency: The severity of detected threats is considered high, and the query runs every 10 minutes, looking back over the last 10 minutes of data. Alerts are suppressed for 4 hours per affected host to prevent alert fatigue.

  5. Output: The query outputs a summary of detected threats, including the first and last detection times, the number of events, the vectors involved, and the affected hosts and actors. It assigns a severity level based on the combination of detected vectors and generates an alert name for easy identification.

In essence, this query helps security teams identify and respond to potential backdoor attacks using accessibility features on Windows systems.

Details

David Alonso profile picture

David Alonso

Released: March 24, 2026

Tables

SecurityEventDeviceProcessEvents

Keywords

SecurityEventDeviceProcessEventsAccessibilityBinariesSuspiciousDebuggersAffectedHostActorTargetKeyValueNameOldValueNewValueDetectionVectorMITRE_Technique

Operators

letdynamicagohas_any=~tolowerstrcatprojectextendin~tostringextractunionsummarizeminmaxcountmake_setiffstrcatproject-reorder

Actions