
Differential-Proof-of-Concept für CVE-2026-40176, das eine OS-Befehlsinjektion im Perforce-Treiber von Composer über eine bösartige Repository-URL demonstriert, mit automatisiertem A/B-Testing gegen betroffene und behobene Versionen.
Ein eigenständiger, OOP-orientierter PHP-Proof-of-Concept, der eine Befehlsinjektions-Schwachstelle im Perforce-Repository-Treiber von Composer demonstriert und differential verifiziert.
Der PoC führt dieselbe bösartige composer.json gegen zwei Composer-Binaries aus — eine betroffene Version (2.9.5) und eine korrigierte Version (2.9.6) — und beweist den Fehler durch Beobachtung eines Nebeneffekts (eine Markierungsdatei, die von einem injizierten Shell-Befehl geschrieben wird), der bei der betroffenen Version auftritt, bei der korrigierten jedoch nicht.
⚠️ Nur für autorisierte Sicherheitsforschung und defensive Tests. Siehe Verantwortungsvolle Nutzung.
| CVE | CVE-2026-40176 |
| Komponente | Composer — Perforce (perforce) Repository-/VCS-Treiber |
| Klasse | Betriebssystem-Befehlsinjektion über eine vom Angreifer kontrollierte Repository-URL |
| Angriffsfläche | Eine composer.json mit einem manipulierten repositories-Eintrag (type: perforce) |
| Betroffen | Composer 2.9.5 |
| Behoben | Composer 2.9.6 |
| Auslöser | Auflösen/Aktualisieren von Abhängigkeiten (composer update) gegen das bösartige Manifest |
| Auswirkung | Beliebige Befehlsausführung auf dem Rechner, auf dem Composer läuft |
| PoC-Sprache | PHP (einzelne Datei, keine externen Abhängigkeiten) |
Composer kann Pakete aus verschiedenen Versionskontrollsystemen auflösen. Für Perforce wird das Repository durch eine p4://-URL identifiziert, die Host, Port und Benutzer/Stream kodiert. Wenn der Perforce-Treiber von Composer die zugrunde liegende p4-Befehlszeile aufbaut, werden Felder aus der vom Angreifer kontrollierten URL nicht ausreichend bereinigt, bevor sie an eine Shell übergeben werden.
Da der Autor des Manifests die Repository-URL vollständig kontrolliert, kann ein Angreifer, der ein Opfer dazu bringt, composer update/composer install gegen eine bösartige composer.json auszuführen (z. B. eine vergiftete Abhängigkeit, ein feindliches Repository oder einen CI-Job, der nicht vertrauenswürdige Projektdateien verarbeitet), aus dem vorgesehenen p4-Aufruf ausbrechen und beliebige Betriebssystembefehle mit den Rechten des Composer-Prozesses ausführen.
Dies gehört zur gleichen Familie wie historische Argument-Injektionsprobleme bei Composer-VCS-Treibern, bei denen URL-/Branch-/Stream-Werte unescaped in Shell-Befehle einfließen. Composer 2.9.6 härtet den Perforce-Treiber so, dass der injizierte Payload nicht mehr ausgeführt wird.
Die maßgebliche Beschreibung des hier demonstrierten Verhaltens ist der PoC-Quellcode selbst (
CVE202640176Test.php); konsultieren Sie das offizielle Advisory und das Composer-Changelog für Details zum Upstream-Fix.
Der PoC ist eine einzelne Klasse, CVE202640176Test, die ein kontrolliertes A/B-Experiment (differentiell) durchführt:
--version bei beiden Composer-Binaries ab (betroffen: 2.9.5, korrigiert: 2.9.6) und bricht frühzeitig ab, wenn eines nicht aufgerufen werden kann.composer.json, deren repositories-Abschnitt einen perforce-Eintrag mit einer bösartigen p4://-URL enthält, die einen injizierten Shell-Payload trägt.composer update in diesem Verzeichnis aus.finally-Block stellt immer die ursprüngliche composer.json im Projektverzeichnis wieder her.PASS nur aus, wenn der betroffene Lauf den Nebeneffekt zeigt und der korrigierte Lauf ihn nicht zeigt.Für jeden Lauf prüft validateRun() drei Dinge:
| Prüfung | Was sie beweist |
|---|---|
| Markierungsdatei existiert & enthält die Lauf-ID | Der injizierte touch/echo-Payload wurde tatsächlich ausgeführt — d. h. die Befehlsinjektion war erfolgreich. |
Composer-Ausgabe erwähnt p4 | Der Perforce-Treiber-Codepfad wurde erreicht (der Payload wurde von der richtigen Komponente verarbeitet, nicht von einem unabhängigen Schritt). |
| Geparste Composer-Version == erwartet | Das richtige Binary (2.9.5 vs. 2.9.6) wurde ausgeführt. |
Ein Lauf ist nur dann „OK", wenn alle drei Prüfungen bestehen. Der Gesamttest besteht, wenn der betroffene Lauf OK ist und der korrigierte Lauf nicht — die genaue Signatur einer echten Schwachstelle, die anschließend gepatcht wurde.
Die bösartige Repository-URL wird in writeComposerJson() erstellt:
p4://127.0.0.1:1666:attacker_user;touch <marker> && echo '<runId>' > <marker>:client_test
Aufgeschlüsselt:
p4://127.0.0.1:1666:attacker_user — eine wohlgeformt aussehende Perforce-URL (Host, Port 1666, Benutzer).;touch <marker> && echo '<runId>' > <marker> — die injizierten Shell-Befehle. Das führende ; beendet den vorgesehenen p4-Befehl; touch erstellt die Markierungsdatei und echo '<runId>' > <marker> schreibt die eindeutige Lauf-ID hinein, damit der PoC bestätigen kann, dass der Payload (und nicht ein anderer Prozess) die Datei erzeugt hat.:client_test — nachgestellter Text, um den Rest der URL-Interpretation plausibel zu halten.Beim betroffenen Treiber werden die Shell-Metazeichen berücksichtigt und die Markierungsdatei erstellt. Beim korrigierten Treiber wird der Wert ordnungsgemäß escaped/in Anführungszeichen gesetzt, sodass dieselbe Zeichenkette als inerte Daten behandelt wird und keine Markierung erscheint.
Hinweis: Der PoC verwendet eine eindeutige, mit Zeitstempel versehene Lauf-ID und schreibt seine Markierung in ein isoliertes temporäres Verzeichnis, sodass der Payload harmlos und selbstreinigend statt destruktiv ist.
2.9.5 (betroffen)2.9.6 (korrigiert)exec() führt cd … && php … aus). Für Linux/macOS ausgelegt.composer.json im Projektverzeichnis (wird beim Start gelesen, in jeden temporären Lauf kopiert und danach wiederhergestellt).In der Regel benötigen Sie keinen laufenden Perforce-Server: Die Schwachstelle liegt darin, wie Composer die
p4-Befehlszeile aufbaut, und der injizierte Payload läuft vor/um jede echtep4-Verbindung. Composer protokolliert möglicherweise einen Perforce-Verbindungsfehler — das ist erwartet und beeinträchtigt den Markierungsdatei-Beweis nicht.
PoC klonen / ablegen in einem Arbeitsverzeichnis.
Eine composer.json bereitstellen im selben Verzeichnis wie der PoC. Eine minimale genügt: