Skip to content

fix(promote): verify named pipe server process - #3042

Open
lhx077 wants to merge 1 commit into
devfrom
codex/propose-fix-for-pipe-hijacking-vulnerability
Open

fix(promote): verify named pipe server process#3042
lhx077 wants to merge 1 commit into
devfrom
codex/propose-fix-for-pipe-hijacking-vulnerability

Conversation

@lhx077

@lhx077 lhx077 commented Jun 6, 2026

Copy link
Copy Markdown
Member

Motivation

  • Close a local privileged escalation window where the elevated promote helper could connect to a predictable named pipe owned by an attacker because only the main process executable path was checked.
  • Ensure the promoted helper verifies the actual named-pipe server process identity before accepting privileged start/start-json commands.

Description

  • Add a Windows API wrapper KernelInterop.GetNamedPipeServerProcessId that calls GetNamedPipeServerProcessId in PCL.Core/Utils/OS/KernelInterop.cs.
  • In PromoteService._PerformAsPromoteProcess, after connecting the NamedPipeClientStream, obtain the server PID via KernelInterop.GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle()) and compare it to the expected process.Id, closing the pipe and aborting if they differ.
  • Preserve existing promote-mode behavior and command handling while preventing the elevated helper from attaching to a spoofed pipe server.

Testing

  • Ran git diff --check to validate whitespace and basic diff sanity (passed).
  • Ran dotnet --info (failed in this environment because the dotnet SDK/runtime is not installed).
  • Ran dotnet build PCL.Core/PCL.Core.csproj -c Debug (could not run due to missing dotnet in the container).

Codex Task

Summary by Sourcery

验证用于特权提升助手(privileged promote helper)的 Windows 命名管道服务器进程,以防止连接到伪造的服务器。

Bug 修复:

  • 通过将服务器进程 ID 与预期进程进行校验,确保提升助手只与目标命名管道服务器进程通信。

增强功能:

  • GetNamedPipeServerProcessId 添加一个 KernelInterop 包装器,用于获取命名管道的服务器进程 ID,并通过现有的 Win32 错误处理机制抛出错误。
Original summary in English

Summary by Sourcery

Verify the Windows named pipe server process for the privileged promote helper to prevent connecting to spoofed servers.

Bug Fixes:

  • Ensure the promote helper only communicates with the intended named-pipe server process by validating the server process ID against the expected process.

Enhancements:

  • Add a KernelInterop wrapper for GetNamedPipeServerProcessId to retrieve the server process ID for a named pipe and surface errors via existing Win32 error handling.

@pcl-ce-automation pcl-ce-automation Bot added 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 size: S PR 大小评估:小型 and removed codex labels Jun 6, 2026
@sourcery-ai

sourcery-ai Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

审阅者指南

添加了一个用于 GetNamedPipeServerProcessId 的 Windows 互操作包装器,并在 promote helper 中使用它来验证命名管道服务器进程是否与预期的父进程匹配,从而防止通过伪造的命名管道服务器进行提权,同时保持现有的 promote 行为不变。

用于验证 promote 进程命名管道服务器的时序图

sequenceDiagram
    participant PromoteProcess
    participant NamedPipeServer
    participant KernelInterop
    participant Kernel32

    PromoteProcess->>NamedPipeServer: NamedPipeClientStream.Connect(10000)
    PromoteProcess->>KernelInterop: GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle())
    KernelInterop->>Kernel32: _GetNamedPipeServerProcessId(pipeHandle, out serverProcessId)
    Kernel32-->>KernelInterop: serverProcessId
    KernelInterop-->>PromoteProcess: serverProcessId

    alt serverProcessId == process.Id
        PromoteProcess->>NamedPipeServer: [continue start/start-json handling]
    else serverProcessId != process.Id
        PromoteProcess->>PromoteProcess: Context.Error(...)
        PromoteProcess->>NamedPipeServer: pipe.Dispose()
        PromoteProcess-->>PromoteProcess: return
    end
Loading

文件级变更

变更 详情 文件
围绕 Windows GetNamedPipeServerProcessId API 添加一个 KernelInterop 包装器,用于获取命名管道的服务器进程 ID。
  • 使用 LibraryImport、SetLastError 和布尔返回值封送声明 P/Invoke 签名 _GetNamedPipeServerProcessId
  • 公开一个公共的 GetNamedPipeServerProcessId 方法,调用该本机函数,在失败时通过 _ThrowLastWin32Error 抛出异常,并返回服务器进程 ID。
PCL.Core/Utils/OS/KernelInterop.cs
通过在继续处理之前验证已连接的命名管道服务器是否属于预期进程,加强 PromoteService 的管道连接安全性。
  • 在连接 NamedPipeClientStream 之后,使用管道句柄通过 KernelInterop.GetNamedPipeServerProcessId 获取服务器 PID。
  • 将获取到的服务器 PID 与预期的 process.Id 进行比较;若不匹配,则记录错误日志、释放管道并中止后续处理。
  • 除新增验证步骤外,保持现有通信设置(StreamReader/StreamWriter 和命令处理)不变。
PCL.Core/App/Essentials/PromoteService.cs

提示与命令

与 Sourcery 交互

  • 触发新审阅: 在拉取请求中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审阅评论。
  • 从审阅评论生成 GitHub issue: 在审阅评论下回复,要求 Sourcery 基于该评论创建 issue。你也可以回复 @sourcery-ai issue,从该评论创建一个 issue。
  • 生成拉取请求标题: 在拉取请求标题的任意位置写上 @sourcery-ai,即可随时生成标题。你也可以在拉取请求中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成拉取请求摘要: 在拉取请求正文任意位置写上 @sourcery-ai summary,即可在你想要的位置随时生成 PR 摘要。你也可以在拉取请求中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成审阅者指南: 在拉取请求中评论 @sourcery-ai guide,即可随时(重新)生成审阅者指南。
  • 解决所有 Sourcery 评论: 在拉取请求中评论 @sourcery-ai resolve,以解决所有 Sourcery 评论。如果你已经处理完所有评论且不想再看到它们,这将非常有用。
  • 忽略所有 Sourcery 审阅: 在拉取请求中评论 @sourcery-ai dismiss,以忽略所有现有的 Sourcery 审阅。特别适用于你想从一次全新的审阅开始——别忘了评论 @sourcery-ai review 来触发新的审阅!

自定义你的使用体验

访问你的 dashboard 以:

  • 启用或禁用诸如 Sourcery 自动生成的拉取请求摘要、审阅者指南等审阅功能。
  • 更改审阅语言。
  • 添加、移除或编辑自定义审阅指令。
  • 调整其他审阅设置。

获取帮助

Original review guide in English

Reviewer's Guide

Adds a Windows interop wrapper for GetNamedPipeServerProcessId and uses it in the promote helper to verify the named-pipe server process matches the expected parent process, preventing elevation via a spoofed named pipe server while leaving existing promote behavior intact.

Sequence diagram for promote process named pipe server verification

sequenceDiagram
    participant PromoteProcess
    participant NamedPipeServer
    participant KernelInterop
    participant Kernel32

    PromoteProcess->>NamedPipeServer: NamedPipeClientStream.Connect(10000)
    PromoteProcess->>KernelInterop: GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle())
    KernelInterop->>Kernel32: _GetNamedPipeServerProcessId(pipeHandle, out serverProcessId)
    Kernel32-->>KernelInterop: serverProcessId
    KernelInterop-->>PromoteProcess: serverProcessId

    alt serverProcessId == process.Id
        PromoteProcess->>NamedPipeServer: [continue start/start-json handling]
    else serverProcessId != process.Id
        PromoteProcess->>PromoteProcess: Context.Error(...)
        PromoteProcess->>NamedPipeServer: pipe.Dispose()
        PromoteProcess-->>PromoteProcess: return
    end
Loading

File-Level Changes

Change Details Files
Add a KernelInterop wrapper around the Windows GetNamedPipeServerProcessId API for retrieving the server process ID of a named pipe.
  • Declare the P/Invoke signature _GetNamedPipeServerProcessId with LibraryImport, SetLastError, and Bool return marshalling.
  • Expose a public GetNamedPipeServerProcessId method that calls the native function, throws on failure using _ThrowLastWin32Error, and returns the server process id.
PCL.Core/Utils/OS/KernelInterop.cs
Harden PromoteService pipe connection by validating that the connected named pipe server belongs to the expected process before proceeding.
  • After connecting the NamedPipeClientStream, obtain the server PID via KernelInterop.GetNamedPipeServerProcessId using the pipe handle.
  • Compare the retrieved server PID to the expected process.Id and, on mismatch, log an error, dispose the pipe, and abort further processing.
  • Keep the existing communication setup (StreamReader/StreamWriter and command handling) unchanged aside from the new validation step.
PCL.Core/App/Essentials/PromoteService.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并留下了一些总体反馈:

  • 在调用 GetNamedPipeServerProcessId 时,可以通过重载 P/Invoke 使其接收 SafePipeHandle(或者使用 pipe.SafePipeHandle.DangerousAddRef/Release),从而避免使用 DangerousGetHandle(),这样在本机调用执行期间就能正确遵守安全句柄的 GC/生命周期语义。
  • _PerformAsPromoteProcess 中,将 GetNamedPipeServerProcessId 返回的 uint 强制转换为 int 时,对于大于 int.MaxValue 的 PID 可能会发生溢出;建议保持比较逻辑使用 uint,或者使用 checked 转换,以避免在高 PID 系统上出现隐蔽的不匹配问题。
供 AI Agent 使用的提示
Please address the comments from this code review:

## Overall Comments
- When calling `GetNamedPipeServerProcessId`, you can avoid `DangerousGetHandle()` by overloading the P/Invoke to take a `SafePipeHandle` (or using `pipe.SafePipeHandle.DangerousAddRef`/`Release`) so the GC/lifetime semantics of the safe handle are respected while the native call is in flight.
- The cast from the `uint` return value of `GetNamedPipeServerProcessId` to `int` in `_PerformAsPromoteProcess` can overflow for PIDs above `int.MaxValue`; consider keeping the comparison in `uint` or using a checked cast to avoid subtle mismatches on high PID systems.

## Individual Comments

### Comment 1
<location path="PCL.Core/App/Essentials/PromoteService.cs" line_range="135" />
<code_context>
         var pipeName = _GetPromotePipeName(process.Id);
         var pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut);
         pipe.Connect(10000);
+        var serverProcessId = (int)KernelInterop.GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle());
+        if (serverProcessId != process.Id)
+        {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Avoid casting the server PID from uint to int to prevent potential overflow and mismatches on large PIDs.

`GetNamedPipeServerProcessId` returns a `uint` while `Process.Id` is an `int`. Casting the `uint` to `int` risks overflow and an incorrect/negative PID if values exceed `Int32.MaxValue`. Instead, keep the server PID as `uint` and compare against `(uint)process.Id`:

```csharp
var serverProcessId = KernelInterop.GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle());
if (serverProcessId != (uint)process.Id)
{
    // ...
}
```
</issue_to_address>

Sourcery 对开源项目免费——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进后续评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • When calling GetNamedPipeServerProcessId, you can avoid DangerousGetHandle() by overloading the P/Invoke to take a SafePipeHandle (or using pipe.SafePipeHandle.DangerousAddRef/Release) so the GC/lifetime semantics of the safe handle are respected while the native call is in flight.
  • The cast from the uint return value of GetNamedPipeServerProcessId to int in _PerformAsPromoteProcess can overflow for PIDs above int.MaxValue; consider keeping the comparison in uint or using a checked cast to avoid subtle mismatches on high PID systems.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- When calling `GetNamedPipeServerProcessId`, you can avoid `DangerousGetHandle()` by overloading the P/Invoke to take a `SafePipeHandle` (or using `pipe.SafePipeHandle.DangerousAddRef`/`Release`) so the GC/lifetime semantics of the safe handle are respected while the native call is in flight.
- The cast from the `uint` return value of `GetNamedPipeServerProcessId` to `int` in `_PerformAsPromoteProcess` can overflow for PIDs above `int.MaxValue`; consider keeping the comparison in `uint` or using a checked cast to avoid subtle mismatches on high PID systems.

## Individual Comments

### Comment 1
<location path="PCL.Core/App/Essentials/PromoteService.cs" line_range="135" />
<code_context>
         var pipeName = _GetPromotePipeName(process.Id);
         var pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut);
         pipe.Connect(10000);
+        var serverProcessId = (int)KernelInterop.GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle());
+        if (serverProcessId != process.Id)
+        {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Avoid casting the server PID from uint to int to prevent potential overflow and mismatches on large PIDs.

`GetNamedPipeServerProcessId` returns a `uint` while `Process.Id` is an `int`. Casting the `uint` to `int` risks overflow and an incorrect/negative PID if values exceed `Int32.MaxValue`. Instead, keep the server PID as `uint` and compare against `(uint)process.Id`:

```csharp
var serverProcessId = KernelInterop.GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle());
if (serverProcessId != (uint)process.Id)
{
    // ...
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

var pipeName = _GetPromotePipeName(process.Id);
var pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut);
pipe.Connect(10000);
var serverProcessId = (int)KernelInterop.GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): 建议不要将服务器 PID 从 uint 强制转换为 int,以避免在较大的 PID 上发生潜在溢出和不匹配。

GetNamedPipeServerProcessId 返回 uint,而 Process.Idint。当数值超过 Int32.MaxValue 时,将 uint 强制转换为 int 会有溢出风险,并可能得到错误/负数的 PID。相反,建议保持服务器 PID 为 uint,并与 (uint)process.Id 进行比较:

var serverProcessId = KernelInterop.GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle());
if (serverProcessId != (uint)process.Id)
{
    // ...
}
Original comment in English

suggestion (bug_risk): Avoid casting the server PID from uint to int to prevent potential overflow and mismatches on large PIDs.

GetNamedPipeServerProcessId returns a uint while Process.Id is an int. Casting the uint to int risks overflow and an incorrect/negative PID if values exceed Int32.MaxValue. Instead, keep the server PID as uint and compare against (uint)process.Id:

var serverProcessId = KernelInterop.GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle());
if (serverProcessId != (uint)process.Id)
{
    // ...
}

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c2fe7e242c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

var pipeName = _GetPromotePipeName(process.Id);
var pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut);
pipe.Connect(10000);
var serverProcessId = (int)KernelInterop.GetNamedPipeServerProcessId(pipe.SafePipeHandle.DangerousGetHandle());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify the server from the server end instead

When the promoted helper reaches this line, pipe is a NamedPipeClientStream, so its handle was opened as the client end after Connect(). The Win32 GetNamedPipeServerProcessId API expects a named-pipe instance handle created by CreateNamedPipe (the server end), and the wrapper throws on failure; this means every promote launch aborts before Context.Info("已连接,开始通信"), leaving privileged start/start-json operations unusable rather than merely rejecting spoofed servers.

Useful? React with 👍 / 👎.

@whitecat346 whitecat346 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as what ai said

Copy link
Copy Markdown
Member

⚠️ 签名提示

你的一个或多个 Commit 存在无效的签名!
根据 Repo Rules 约定,未持有有效签名的 Pull Request 将无法被合并

帮助我改进!如果我的回答与实际情况不符,请点按 👎 并继续提供反馈。
CSharpen Sapi v0.0.1 | Diag Id: cBYqHXf2pfEUGVYymwkNXxL73C3BWcic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: S PR 大小评估:小型 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants