[Runner][Interop] Authenticate Settings/Quick Access named-pipe clients before dispatch (CWE-732/CWE-862) (#49527)

## Summary of the Pull Request

The Runner is the **server** for the two-way named pipes it uses to talk
to `PowerToys.Settings.exe` and the Quick Access host, and it dispatched
privileged JSON commands (`killrunner`, `restart_elevation`,
`module_status`, `powertoys`, `language`, ...) **without authenticating
the caller**. When PowerToys runs elevated ("Run as administrator"), the
pipe DACL grants the shared **Logon SID**, so **any same-user Medium-IL
process could connect and inject commands** — a local privilege
escalation (CWE-732 / CWE-862). The pipe DACL cannot distinguish the
legitimate Medium-IL Settings child from a same-user attacker (identical
user SID, integrity level, and logon session), so this PR authenticates
the connecting process's **binary identity** before any dispatch.

## PR Checklist

- [x] **Tests:** Added/updated and all pass (native gate tests + C#
regression)
- [x] **Localization:** No new end-user-facing strings (only a
diagnostic runner log line)
- [x] **New binaries:** None — the new code compiles into the existing
`PowerToys.Interop` and `runner` binaries; tests were added to the
existing `Common.Utils.UnitTests` project

## Detailed Description of the Pull Request / Additional comments

New `src/common/interop/pipe_caller_auth.{h,cpp}` adds
`interop_auth::AuthenticateClient`, invoked from
`TwoWayPipeMessageIPC::handle_pipe_connection` **before** a message is
queued (fail-closed). A connecting client is accepted only if it is:

- under the **Runner-relative install directory**
(`get_module_folderpath()\WinUI3Apps`, so it adapts to installed and
dev-build layouts),
- an **allow-listed basename** (`PowerToys.Settings.exe` /
`PowerToys.QuickAccess.exe`),
- the Runner's **exact file version** (anti-downgrade), and
- **Microsoft Authenticode-signed**.

The signature is anchored to the **LOCAL MACHINE root store**
(`HCCE_LOCAL_MACHINE` +
`CertVerifyCertificateChainPolicy(AUTHENTICODE)`) rather than
`WinVerifyTrust`'s default user+machine union: the Runner runs as the
same user as a potential attacker and would otherwise trust a forged
signer added to `CurrentUser\Root`. Verdicts are cached per `(pid,
process-creation-time, policy)` with a short TTL so the check isn't
re-run on every message (each `send` opens a new connection). Rejections
are logged.

The gate is added via an **additive** `start(HANDLE, CallerPolicy)`
overload; the managed `start(nullptr)` path is unchanged (gate
disabled), so there is **no ABI break** to `PowerToys.Interop`.
`PIPE_REJECT_REMOTE_CLIENTS` is also set. In **Debug** builds only the
signature check is relaxed (directory/basename/version stay enforced) so
local unsigned builds still connect; the relaxation is compiled out of
Release.

**Scope:** this PR covers the two elevated Runner-server pipes (Settings
+ Quick Access), which are the actual EoP surface. The reverse
Runner->Settings response direction, the duplicated Workspaces
transport, and the AdvancedPaste/PowerDisplay module pipes are
intentionally out of scope and can be handled as follow-ups.

## Validation Steps Performed

**Automated**
- **Native unit tests** (`Common.Utils.UnitTests`,
`PipeCallerAuthTests`): legitimate self-caller accepted; wrong
basename/directory rejected with the reject-log callback firing; version
reading. 6/6 pass.
- **C# regression** (`Microsoft.Interop.Tests.TestSend`): managed
gate-disabled round-trip still works.
- **Builds:** runner Debug + Release, `PowerToys.Interop` Debug +
Release, and the test project all build/link clean.

**Official signed build (validates the Release-only signature path that
local Debug builds skip)**
- Queued the internal "PowerToys Signed YAML Release Build" for this
branch — **green** (`result: succeeded`). The produced installers are
Authenticode `Valid`, signer `Microsoft Corporation`.

**Manual testing on the signed installer (elevated Runner) — passed**
- Installed the signed build and ran the Runner **as administrator**.
Settings and Quick Access open and are fully functional; settings apply,
module toggles work, and the hotkey-conflict request/response
round-trips.
- **No** `Rejected unauthenticated ...` lines during legitimate use →
the genuine signed `PowerToys.Settings.exe` is accepted by the
machine-root signature + version + directory checks (verified in
`RunnerLogs\runner-log_*.log`, requests dispatched normally).
- **Security (negative) check:** a non-elevated `powershell.exe`
discovered the runner pipe via the pipe namespace and attempted
`{"killrunner":true}`; the write failed ("Pipe is broken") because the
Runner rejected the caller and disconnected before dispatch.
`PowerToys.exe` stayed running and logged: `Rejected unauthenticated
Settings pipe client: pid=... image='...\powershell.exe'
reason=bad-directory`.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1d8f85ec-aaee-468a-9348-ba79b059cbaf
This commit is contained in:
Boliang Zhang
2026-08-03 22:46:44 +08:00
committed by GitHub
parent ba2e89c428
commit 1980ca5ece
13 changed files with 900 additions and 3 deletions

View File

@@ -40,6 +40,8 @@ ALLINPUT
Allman
Allmodule
ALLNOISE
allowlist
allowlisted
ALLOWUNDO
ALLVIEW
ALPHATYPE
@@ -120,6 +122,8 @@ backticks
Badflags
Badmode
Badparam
basename
basenames
bbwe
BCIE
Belarusian
@@ -182,6 +186,7 @@ CALG
callbackptr
calpwstr
Cangjie
canonicalize
CANRENAME
Canvascustomlayout
CAPTUREBLT
@@ -366,6 +371,7 @@ debouncer
debugbreak
decryptor
Dedup
deduped
Deduplicator
DEFAULTBOOTSTRAPPERINSTALLFOLDER
DEFAULTCOLOR
@@ -679,6 +685,7 @@ hbr
HBRBACKGROUND
hbrush
hcblack
HCCE
HCRYPTHASH
HCRYPTPROV
hcursor
@@ -1384,6 +1391,7 @@ Pitjantjatjara
PKBDLLHOOKSTRUCT
pkgfamily
PKI
PKIX
plib
ploc
ploca
@@ -1753,6 +1761,7 @@ SNAPPROCESS
snk
snwprintf
softline
softpub
SOURCECLIENTAREAONLY
sourced
sourcedoc
@@ -1977,6 +1986,7 @@ uncompilable
UNCPRIORITY
UNDNAME
unescaped
unforgeable
ungroup
UNICODETEXT
unins
@@ -1992,6 +2002,7 @@ unparsable
unremapped
Unsend
Unsubscribes
untampered
untriaged
unvirtualized
unwide
@@ -2145,6 +2156,7 @@ winrt
winsdk
winsta
WINTHRESHOLD
wintrust
WINVER
winword
winxamlmanager