How to Isolate Only the Administrator-Required Work in a Windows App
Bottom line
When a Windows app has work that needs administrator rights, the safe and practical approach is not to run the whole app elevated, but to split only the administrator-required part into a separate EXE.
- Keep the UI app at
asInvoker - Move the administrator work into a separate helper EXE marked
requireAdministrator - Launch the helper with
runas - Use IPC such as a named pipe to talk to the helper
- Send only typed requests to the helper
- Re-validate every request on the helper side
Premise: you cannot elevate just part of the same process
Windows UAC is per-process. You cannot say “only this one method runs as administrator” inside a single process. If a piece of work needs administrator rights, it has to live in a different execution unit such as a separate process or a service.
Choosing a separation model
| Model | What it is | When it fits |
|---|---|---|
| Administrator Broker | asInvoker UI + administrator helper EXE |
You only need UAC at the moment the work happens |
| OS Service | UI + a long-running service | Always-on management features |
| Elevated Task | UI + an elevated scheduled task | One-shot routine jobs |
| COM Object | UI + an elevated COM object | Niche cases where you already have a COM design |
The easiest one to start with is the broker EXE. It is a great fit when only a specific button on a settings screen needs administrator rights.
Recommended shape
[MyApp.exe] asInvoker
|
| runas
v
[MyApp.AdminBroker.exe] requireAdministrator
|
| named pipe
v
[run the administrator operation]
Key points:
- The UI process stays unelevated from start to finish
- The administrator helper is short-lived
- The helper only accepts a fixed allow-list of operations
Rules to follow in the implementation
Do not let the helper become a generic command runner
Bad: the UI sends a raw command string (reg add ..., etc.) to the helper. If the UI gets compromised, the helper goes down with it.
Good: the operation name is fixed (set-explorer-context-menu, install-service, and so on). Arguments are limited to bool, enum, numbers, and tightly constrained strings.
Things to watch when launching
- Always pass the helper EXE’s path as an absolute path
- In .NET, set
UseShellExecute=trueexplicitly (Verb="runas"only works withUseShellExecute=true) runasdoes not play well with stdio redirection, so use a named pipe for IPC
Named-pipe security
- Do not rely on the default ACL; set an explicit
PipeSecurity PipeOptions.CurrentUserOnlycannot be used between an unelevated UI and an elevated helper (it also checks elevation level)- On the UI side, get your own SID and pass it to the helper
- On the helper side, grant pipe-connect rights only to that UI user’s SID
- Use
GetNamedPipeClientProcessIdto also verify the connecting PID
About PID verification
PID verification is defense-in-depth against “another process running as the same user connects first.” Even if the PID matches, a compromised UI can still send dangerous requests, so the operation allow-list and argument re-validation are the real defenses.
Concrete code sketches
Shared contract (BrokerProtocol)
Define the operation names and request/response types in a shared project.
public static class BrokerOperations
{
public const string SetExplorerContextMenu = "set-explorer-context-menu";
}
public sealed record BrokerRequest(string Operation, JsonElement Payload);
public sealed record BrokerResponse(bool Success, string? ErrorCode, string? Message);
Frames on the pipe are length-prefixed JSON (a 4-byte header plus payload).
UI side (ElevationBrokerClient)
- Launch the helper via
ProcessStartInfowithUseShellExecute=true, Verb="runas" - Generate a random pipe name and pass your own PID and SID to the helper
- After the pipe connects, send a typed request
Helper side (AdminBroker)
- Accept the pipe name, client PID, and client SID from the launch arguments
- Build the pipe ACL explicitly (grant connect rights to the calling UI user’s SID)
- After the connection comes in, verify the client PID
- Dispatch only fixed operations using
switch (request.Operation)
Example administrator operation
For something like registering an Explorer right-click menu in the registry:
- Do not accept an arbitrary registry path from the UI
- Do not accept an arbitrary command string from the UI
- The target EXE for the registration is resolved on the helper side, hard-coded
- The only thing the request carries is
Enabled(abool)
The helper then has exactly one meaning: “toggle the registration state of the Explorer right-click menu.”
Common anti-patterns
- Marking the whole UI as
requireAdministrator— sloppily destroys the privilege boundary - Sending a raw command string to the helper — turns the helper into a general-purpose execution endpoint
- Using the default ACL of the named pipe — set the ACL explicitly
- Reaching for
CurrentUserOnly— not suitable for an unelevated UI talking to an elevated helper - A helper that takes an arbitrary path and acts on it — always lock operations down
Summary
- UI is
asInvoker, helper isrequireAdministrator - IPC is a named pipe; restrict the caller via the pipe ACL and the client PID
- The helper only accepts fixed operations and re-validates the arguments
- This design is also easy to migrate into a service later
References
- Original article: A Minimum Security Checklist for Windows Application Development
https://comcomponent.com/en/blog/2026/03/14/001-windows-app-security-minimum-checklist/ - Administrator Broker Model - Win32 apps
- Developing Applications that Require Administrator Privilege
- Operating System Service Model - Win32 apps
- Elevated Task Model - Win32 apps
- Administrator COM Object Model - Win32 apps
- The COM Elevation Moniker
- How User Account Control works
- ProcessStartInfo.UseShellExecute
- Named Pipe Security and Access Rights
- PipeOptions Enum
- NamedPipeServerStreamAcl.Create
- GetNamedPipeClientProcessId
- RegistryView Enum
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
Best Practices for Avoiding Plaintext Secrets in Windows App Config Files
On Windows desktop apps, leaving passwords or tokens in plaintext config is risky. This post lays out where DPAPI (ProtectedData) actuall...
A Minimum Security Checklist for Windows Application Development
A minimum security checklist for Windows app development (WPF, WinForms, WinUI, C++, C#) covering privileges, signing, secrets, communica...
When Windows Admin Privileges Are Actually Required: UAC, Protected Areas, and How to Tell from the Design
A practical guide to when Windows admin rights are truly required, organized around UAC, protected locations, services, drivers, and per-...
How to Use Windows Sandbox to Speed Up Windows App Validation - Admin Rights, Clean Environments, and Reproducing Missing-Permission or Low-Resource Cases
A practical guide to validating Windows apps with Windows Sandbox. Covers first-install checks in a clean environment, isolating admin-ri...
How DLL Name Resolution Works on Windows: A Practical Look at Search Order, Known DLLs, API Sets, and SxS
A practical walkthrough of Windows DLL name resolution covering redirection, API sets, SxS manifests, Known DLLs, the loaded-module list,...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.