It's a Plan: Codeausführung über Chat-Nachricht als CLI-Option

Ein Projektmitglied mit der Standardrolle lädt über eine mit Bindestrich beginnende Chat-Nachricht ein JavaScript-Modul auf der Maschine, die den Agent-Runner betreibt.

Advisory-ID: TP-2026-122
Produkt: It's a Plan (selbst gehostete Projektverwaltung und Issue-Tracking mit KI-Agenten)
Schwachstellentyp: Argument Injection (CWE-88)
CVE: nicht beantragt
CVSS 3.1: 8.5 (Hoch) · CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H
Betroffene Versionen: @itsaplan/runner >= 0.2.0, < 0.7.0
Hersteller-Advisory: GHSA-x99f-4j7w-gvh9
Behoben in: @itsaplan/runner 0.7.0 (ausgeliefert mit It's a Plan 1.7.0)
Gemeldet: 11. Oktober 2026

Zusammenfassung

It's a Plan ist eine selbst gehostete Projektverwaltung, in der externe Coding-Agenten über einen Runner auf der Maschine des Betreibers Aufgaben aus dem Projekt-Chat bearbeiten. Der Runner übergibt die Chat-Nachricht eines Projektmitglieds als letztes argv-Element an die Coding-CLI, und bei drei der sieben ausgelieferten Presets (opencode, pi, omp) trennt nichts diesen Text von den Optionen des Presets. Eine Nachricht, die mit einem Bindestrich beginnt, liest die CLI deshalb als Option statt als Aufgabe. Beim Preset omp lädt --extension=<pfad> ein JavaScript-Modul beim Start, vor der Modellauswahl und unabhängig vom eingestellten Freigabemodus. Ein Projektmitglied mit der niedrigsten ausgelieferten Rolle führt so eigenen Code unter dem Konto des Betreibers auf dem Runner-Host aus, sobald die Moduldatei dort liegt.

Ursache

presetArgv hängt die Nachricht nach dem tail des Presets als letztes argv-Element an (packages/runner/src/presets.ts:168-182). Das Preset omp setzt promptVia: 'arg' mit leerem tail: [] (packages/runner/src/presets.ts:136-149), obwohl der Typkommentar das Feld als stdin-Marker oder als die Option beschreibt, der der Prompt folgt (packages/runner/src/presets.ts:25). Auf einem Thread, der bereits an eine CLI-Sitzung gebunden ist, leitet die API die Nachricht ohne Rahmung weiter (apps/api/src/modules/agents/chat/service.ts:507). Der Chat-Endpunkt verlangt nur ['ai_agents', 'read'] (apps/api/src/modules/agents/chat/index.ts:85), das defaultMemberPermissions() jedem Mitglied gewährt (packages/db/src/permissions.ts:92), und runnerScope steht standardmäßig auf team (apps/api/src/modules/agents/core/service.ts:631). Der eigene Parser von omp akzeptiert --flag=value in einem Element (src/cli/args.ts:201 in @oh-my-pi/pi-coding-agent) und lädt --extension=<pfad>, bevor er ein Modell auflöst.

Proof of Concept

Nachricht 1 (bindet den Thread an eine CLI-Sitzung):  hello
Nachricht 2:                                          --extension=/tmp/rce3/work/notes.mjs

-> Runner startet: omp ... --auto-approve --resume OMP-SESSION-1 --extension=/tmp/rce3/work/notes.mjs
$ cat /tmp/rce3/PWNED.txt
RCE-3: code executed on the operator machine as uid=1000 cwd=/tmp/rce3/work

Die zweite Nachricht landet ohne ---Trenner als eigenes argv-Element hinter den Optionen des Presets, sodass omp sie als Option auswertet und das Modul lädt. Der Code läuft auch unter --approval-mode always-ask und ohne API-Schlüssel, weil die Option vor dem Modellaufruf greift.

Auswirkung

  • JavaScript nach Wahl des Mitglieds läuft unter dem Konto des Betreibers, außerhalb jedes Containers.
  • Modellverweigerung und Freigabemodus greifen nicht, weil die Option vor dem Modellaufruf ausgewertet wird.
  • Erreichbar für die niedrigste ausgelieferte Rolle mit zwei Chat-Nachrichten, ohne Zutun des Eigentümers.
  • Voraussetzung ist ein Runner mit Preset omp und eine Moduldatei auf dem Runner-Host; die Presets claude, codex, antigravity und copilot sind nicht betroffen.

Referenzen

Steckt so etwas in Ihrer Software?

Diese Schwachstelle hat unser Team im Rahmen seiner Arbeit gefunden. Lassen Sie Ihre Anwendungen von denselben Spezialisten prüfen, mit einem Penetrationstest von turingpoint.