|
| 1 | +# Admin Protection Bypasses via UIAccess |
| 2 | + |
| 3 | +{{#include ../../banners/hacktricks-training.md}} |
| 4 | + |
| 5 | +## Overview |
| 6 | +- Windows AppInfo exposes `RAiLaunchAdminProcess` to spawn UIAccess processes (intended for accessibility). UIAccess bypasses most User Interface Privilege Isolation (UIPI) message filtering so accessibility software can drive higher-IL UI. |
| 7 | +- Enabling UIAccess directly requires `NtSetInformationToken(TokenUIAccess)` with **SeTcbPrivilege**, so low-priv callers rely on the service. The service performs three checks on the target binary before setting UIAccess: |
| 8 | + - Embedded manifest contains `uiAccess="true"`. |
| 9 | + - Signed by any certificate trusted by the Local Machine root store (no EKU/Microsoft requirement). |
| 10 | + - Located in an administrator-only path on the system drive (e.g., `C:\Windows`, `C:\Windows\System32`, `C:\Program Files`, excluding specific writable subpaths). |
| 11 | +- `RAiLaunchAdminProcess` performs no consent prompt for UIAccess launches (otherwise accessibility tooling could not drive the prompt). |
| 12 | + |
| 13 | +## Token shaping and integrity levels |
| 14 | +- If the checks succeed, AppInfo **copies the caller token**, enables UIAccess, and bumps Integrity Level (IL): |
| 15 | + - Limited admin user (user is in Administrators but running filtered) ➜ **High IL**. |
| 16 | + - Non-admin user ➜ IL increased by **+16 levels** up to a **High** cap (System IL is never assigned). |
| 17 | + - If the caller token already has UIAccess, IL is left unchanged. |
| 18 | +- “Ratchet” trick: a UIAccess process can disable UIAccess on itself, relaunch via `RAiLaunchAdminProcess`, and gain another +16 IL increment. Medium➜High takes 255 relaunches (noisy, but works). |
| 19 | + |
| 20 | +## Why UIAccess enables an Admin Protection escape |
| 21 | +- UIAccess lets a lower-IL process send window messages to higher-IL windows (bypassing UIPI filters). At **equal IL**, classic UI primitives like `SetWindowsHookEx` **do allow code injection/DLL loading** into any process that owns a window (including **message-only windows** used by COM). |
| 22 | +- Admin Protection launches the UIAccess process under the **limited user’s identity** but at **High IL**, silently. Once arbitrary code runs inside that High-IL UIAccess process, the attacker can inject into other High-IL processes on the desktop (even belonging to different users), breaking the intended separation. |
| 23 | + |
| 24 | +## Secure-directory validation weaknesses (AppInfo `AiCheckSecureApplicationDirectory`) |
| 25 | +AppInfo resolves the supplied path via `GetFinalPathNameByHandle` and then applies **string allow/deny checks** against hardcoded roots/exclusions. Multiple bypass classes stem from that simplistic validation: |
| 26 | +- **Directory named streams**: Excluded writable directories (e.g., `C:\Windows\tracing`) can be bypassed with a named stream on the directory itself, e.g. `C:\Windows\tracing:file.exe`. The string checks see `C:\Windows\` and miss the excluded subpath. |
| 27 | +- **Writable file/directory inside an allowed root**: `CreateProcessAsUser` does **not require a `.exe` extension**. Overwriting any writable file under an allowed root with an executable payload works, or copying a signed `uiAccess="true"` EXE into any writable subdirectory (e.g., update leftovers such as `Tasks_Migrated` when present) lets it pass the secure-path check. |
| 28 | +- **MSIX into `C:\Program Files\WindowsApps` (fixed)**: Non-admins could install signed MSIX packages that landed in `WindowsApps`, which was not excluded. Packaging a UIAccess binary inside the MSIX then launching it via `RAiLaunchAdminProcess` yielded a **promptless High-IL UIAccess process**. Microsoft mitigated by excluding this path; the `uiAccess` restricted MSIX capability itself already requires admin install. |
| 29 | + |
| 30 | +## Attack workflow (High IL without a prompt) |
| 31 | +1. Obtain/build a **signed UIAccess binary** (manifest `uiAccess="true"`). |
| 32 | +2. Place it where AppInfo’s allowlist accepts it (or abuse a path-validation edge case/writable artifact as above). |
| 33 | +3. Call `RAiLaunchAdminProcess` to spawn it **silently** with UIAccess + elevated IL. |
| 34 | +4. From that High-IL foothold, target another High-IL process on the desktop using **window hooks/DLL injection** or other same-IL primitives to fully compromise the admin context. |
| 35 | + |
| 36 | +## Enumerating candidate writable paths |
| 37 | +Run the PowerShell helper to discover writable/overwritable objects inside nominally secure roots from the perspective of a chosen token: |
| 38 | + |
| 39 | +```powershell |
| 40 | +$paths = "C:\\Windows","C:\\Program Files","C:\\Program Files (x86)" |
| 41 | +Get-AccessibleFile -Win32Path $paths -Access Execute,WriteData ` |
| 42 | + -DirectoryAccess AddFile -Recurse -ProcessId <PID> |
| 43 | +``` |
| 44 | + |
| 45 | +- Run as Administrator for broader visibility; set `-ProcessId` to a low-priv process to mirror that token’s access. |
| 46 | +- Filter manually to exclude known disallowed subdirectories before using candidates with `RAiLaunchAdminProcess`. |
| 47 | + |
| 48 | +## References |
| 49 | +- [Bypassing Administrator Protection by Abusing UI Access](https://projectzero.google/2026/02/windows-administrator-protection.html) |
| 50 | + |
| 51 | +{{#include ../../banners/hacktricks-training.md}} |
0 commit comments