fix(sandbox): drop Authenticated Users from both restricting lists — close the C:\\-root escape, CIM unavailable in every confined mode
workspace-write now runs [logon SID, Everyone, orphan]: the two lists differ only by the orphan, and the keep-alive group (logon SID + Everyone) is the single shared invariant. The WMI namespace security check fails in BOTH modes (0x80041003), so CIM/Get-ComputerInfo are unavailable everywhere — the price of closing the C:\\-root tree-creation escape (AU:(AD) + AU:(OI)(CI)(IO)(M)) in workspace-write too. The unused WinLocalSid/WinInteractiveSid/WinAuthenticatedUserSid constants and their ABI-probe prints are removed; the enforcement 'full' claim now stands on a closed NTFS surface. New regression: a C:\\Users\\Public subdirectory write is denied under BOTH modes (the ambient-writable blind spot the review flagged — INTERACTIVE is absent from both lists). FAT-class (non-ACL) targets outside the granted roots remain writable (no security descriptors to intersect) — documented as a legacy residue, warn-only, not engineered around. Docs/design note/PR body updated in both languages (list I/J terminology gone everywhere).
This commit is contained in:
@@ -101,44 +101,41 @@ function buildRestrictingSids(sids: readonly NativePtr[]): Buffer {
|
||||
return buffer
|
||||
}
|
||||
|
||||
/** The well-known SIDs packed into every restricted token's restricting list. */
|
||||
/** The well-known SID packed into every restricted token's restricting list. */
|
||||
export interface RestrictingSidSet {
|
||||
world: NativePtr
|
||||
authUser: NativePtr
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the write-restricted token with the mode-selected restricting list
|
||||
* (dual lists verified on Win11 26200, see the POC-worktree restrict-variant
|
||||
* harness):
|
||||
* - list I (read-only): [logon SID, EVERYONE]
|
||||
* - list J (workspace-write): [logon SID, EVERYONE, Authenticated Users, orphan]
|
||||
* (verified on Win11 26200, see the POC-worktree restrict-variant harness):
|
||||
* - read-only: [logon SID, EVERYONE]
|
||||
* - workspace-write: [logon SID, EVERYONE, orphan]
|
||||
*
|
||||
* The logon SID and EVERYONE are shared: they keep the early startup chain
|
||||
* (0xC0000142 without them) and CNG (`\Device\CNG` write trustee — pwsh
|
||||
* crashes 0xE0434352 without EVERYONE) alive. Authenticated Users exists in
|
||||
* list J ONLY because the CIM path's WMI namespace security check requires it
|
||||
* (0x80041003 otherwise) — read-only drops it for a zero ambient-write
|
||||
* surface (it closes the host's C:\-root tree-creation escape, where
|
||||
* `AU:(AD)` + `AU:(OI)(CI)(IO)(M)` ACEs stand) at the cost of CIM
|
||||
* unavailability; documented in README. List I also carries NO orphan: a
|
||||
* standing grant ACE from an earlier workspace-write period (a
|
||||
* `/permission` mode downgrade, or a crash-resumed session) must stay INERT
|
||||
* under read-only — the WRITE_RESTRICTED pass-2 check grants only what the
|
||||
* restricting list carries, so omitting the orphan keeps read-only strictly
|
||||
* zero-grant even with stale ACEs standing, while the unrevoked ACE keeps
|
||||
* the re-upgrade free (the seam's grant map hits it — no re-propagation).
|
||||
* INTERACTIVE/LOCAL are absent from BOTH lists — the host's Public tree
|
||||
* grants write to INTERACTIVE, so removing it closes that escape. S-1-2-1
|
||||
* (console logon) is intentionally absent: see win32-abi.ts for the
|
||||
* verified failure modes. FAILS CLOSED: any failure throws — never spawn
|
||||
* unrestricted.
|
||||
* The logon SID + EVERYONE keep-alive group is shared by both modes: early
|
||||
* DLL init dies with 0xC0000142 and CNG (`\Device\CNG` write trustee —
|
||||
* pwsh crashes 0xE0434352) fails without them. The orphan SID joins ONLY
|
||||
* workspace-write — read-only carries no orphan, so a standing grant ACE
|
||||
* from an earlier workspace-write period (a `/permission` mode downgrade, or
|
||||
* a crash-resumed session) stays INERT under read-only: the WRITE_RESTRICTED
|
||||
* pass-2 check grants only what the restricting list carries, keeping
|
||||
* read-only strictly zero-grant even with stale ACEs standing, while the
|
||||
* unrevoked ACE keeps the re-upgrade free (the seam's grant map hits it — no
|
||||
* re-propagation). Authenticated Users is absent from BOTH lists: the WMI
|
||||
* namespace security check fails (0x80041003), so CIM is unavailable in
|
||||
* every confined mode, and the C:\-root tree-creation escape (standing
|
||||
* `AU:(AD)` + `AU:(OI)(CI)(IO)(M)` ACEs) is closed in both — documented in
|
||||
* README. INTERACTIVE/LOCAL are absent from BOTH lists too — the host's
|
||||
* Public tree grants write to INTERACTIVE, so removing it closes that
|
||||
* escape. S-1-2-1 (console logon) is intentionally absent: see win32-abi.ts
|
||||
* for the verified failure modes. FAILS CLOSED: any failure throws — never
|
||||
* spawn unrestricted.
|
||||
* @param api - the binding table.
|
||||
* @param currentToken - the process token to restrict.
|
||||
* @param logonSid - the copied logon session SID.
|
||||
* @param writeSid - the orphan SID forming the write allowlist (list J only).
|
||||
* @param writeSid - the orphan SID forming the write allowlist (workspace-write only).
|
||||
* @param known - the well-known SIDs entering the restricting list.
|
||||
* @param mode - selects the restricting list (I for read-only, J for workspace-write).
|
||||
* @param mode - selects the restricting list (workspace-write adds the orphan).
|
||||
* @returns the restricted token handle.
|
||||
*/
|
||||
export function createRestrictedToken(
|
||||
@@ -151,7 +148,7 @@ export function createRestrictedToken(
|
||||
): NativePtr {
|
||||
const restrictingSids = buildRestrictingSids(mode === 'read-only'
|
||||
? [logonSid, known.world]
|
||||
: [logonSid, known.world, known.authUser, writeSid])
|
||||
: [logonSid, known.world, writeSid])
|
||||
const tokenSlot = allocPtrSlot()
|
||||
const created = api.createRestrictedToken(
|
||||
currentToken,
|
||||
|
||||
Reference in New Issue
Block a user