Repository navigation
Description
Severity: CVSS 7.8 (High) CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H · MSRC Important
Attacker: local co-tenant (same build account, different session/integrity; or pipe squat) · Metalama analogue: metalama/Metalama#1651 (+ metalama/Metalama#1648)
Scope: Windows, .NET Framework attended-build pipe-server mode
Affected code
Core/PostSharp.Compiler.Client.Protocol/Pipe/PipeStream.cs:354-366—CreateNamedPipe(..., PIPE_UNLIMITED_INSTANCES, ..., IntPtr.Zero)(NULL security descriptor)Core/PostSharp.Compiler.Client.Protocol/Pipe/PipeEndpointName.cs:17-50— predictable name (embeds the user SID)Core/PostSharp.Compiler.Hosting.NetFramework/PipeServerWorker.cs:242-260,103-166,302-333— accepts an unauthenticatedPipeStartPacket, runs its WorkingDirectory+Arguments in a FullTrust AppDomainCore/PostSharp.Compiler.Client.Protocol/Pipe/PipeStartPacket.cs:27-44Core/PostSharp.Compiler.Hosting/Program.cs:825-838,979-988— args → project fileCore/PostSharp.Compiler.Engine/Sdk/Extensibility/Project.cs:699-723,1390-1432,2530-2543—.dll/.exeloaded viaAssemblyLoadHelper.LoadAssemblyFromFile, or<Using>/<Tasks AssemblyFile>from a.config; only a syntax path checkCore/PostSharp.MSBuild/Pipe/PipeClient.cs:453-465— client blindly trusts the server Hello (squat direction)
Description
The server pipe is created with a NULL security descriptor (the process default token DACL, not owner-only) and PIPE_UNLIMITED_INSTANCES; its name is predictable. The server reads a PipeStartPacket with no peer authentication and uses its arguments to load and execute an arbitrary project file / plugin DLL / PostSharp.config (<Using>) in a FullTrust AppDomain.
Exploit scenario
(A) Code execution: a same-account process at a lower integrity level or in a different logon session (the NULL-SD default DACL admits same-SID cross-IL/cross-session access) connects to \\.\pipe\postsharp-<SID>... and sends a Start-packet whose args point at an attacker-writable evil.dll / /X:evil.config. The host loads & runs it in its FullTrust AppDomain → code execution in the build host's context.
(B) Squat / theft: because of unlimited instances + predictable name + no server authentication on the client, an attacker pre-creates a same-named server; the victim's MSBuild client connects and hands over the project path and /L: license keys, and accepts forged results.
Preconditions
Same build account (cross-session/cross-integrity), or the squat race; Windows + .NET Framework pipe-server mode. A plain same-account/same-session/same-integrity peer is non-additive (already equivalent power).
Recommendation
Create the pipe with an explicit owner-only PipeSecurity (and session/integrity restriction) instead of IntPtr.Zero; set FILE_FLAG_FIRST_PIPE_INSTANCE; authenticate the peer SID on accept (GetNamedPipeClientProcessId) and on connect (GetNamedPipeServerProcessId); constrain loadable project/plugin paths to the expected output dirs.
Backward compatibility
The pipe SD/flag change is shared between host and MSBuild client (they ship together, but users may mix package versions) → negotiate via a capability bit in the Hello packet; do not change the wire packet layout. No public API/IL impact.
Severity notes
Most debatable: Scope. S:C treats same-SID cross-integrity/cross-session as an authority crossing (the weakest S:C in the set since the SID is identical); S:U → 7.0, still High. If you reject the cross-session/cross-IL attacker entirely, the code-exec path is non-additive (informational) and only the squat info-disclosure remains.
Part of #56.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
[-]Placeholder for #50[/-][+][Security] Build-daemon named pipe: NULL DACL + unauthenticated Start-packet loads arbitrary assemblies (code execution)[/+]gfraiteur commentedon Jun 27, 2026
Released in v2024.0.24.
-- Claude for Gael
gfraiteur commentedon Jun 27, 2026
Also released in v2026.0.13, which includes the upstream 2024.0.24 changes.
-- Claude for Gael