
Prova di concetto differenziale per CVE-2026-40176, che dimostra l'iniezione di comandi del sistema operativo nel driver Perforce di Composer tramite un URL di repository malintenzionato, con test A/B automatizzati sulle versioni affette e corrette.
Una proof of concept autonoma, in stile OOP PHP, che dimostra e verifica in modo differenziale una vulnerabilità di iniezione di comandi nel driver del repository Perforce di Composer.
La PoC esegue lo stesso composer.json malevolo contro due binari di Composer — una versione affetta (2.9.5) e una versione corretta (2.9.6) — e dimostra il bug osservando un effetto collaterale (un file marcatore scritto da un comando shell iniettato) che si attiva sulla versione affetta ma non su quella corretta.
⚠️ Solo per ricerca sulla sicurezza autorizzata e test difensivi. Vedi Uso Responsabile.
| CVE | CVE-2026-40176 |
| Componente | Composer — driver repository/VCS Perforce (perforce) |
| Classe | Iniezione di comandi del sistema operativo tramite URL di repository controllato dall'attaccante |
| Superficie d'attacco | Un composer.json contenente una voce repositories manipolata di tipo perforce |
| Affetto | Composer 2.9.5 |
| Corretto | Composer 2.9.6 |
| Attivazione | Risoluzione/aggiornamento delle dipendenze (composer update) contro il manifest malevolo |
| Impatto | Esecuzione arbitraria di comandi sulla macchina che esegue Composer |
| Linguaggio PoC | PHP (file singolo, senza dipendenze esterne) |
Composer può risolvere pacchetti da diversi sistemi di controllo versione. Per Perforce, il repository è identificato da un URL p4:// che codifica host, porta e utente/stream. Quando il driver Perforce di Composer costruisce la riga di comando p4 sottostante, i campi presi dall'URL controllato dall'attaccante non sono sufficientemente sanificati prima di essere passati a una shell.
Poiché l'autore del manifest controlla completamente l'URL del repository, un attaccante che riesca a far eseguire a una vittima composer update/composer install contro un composer.json malevolo (ad esempio, una dipendenza avvelenata, un repository ostile o un job CI che elabora file di progetto non fidati) può uscire dall'invocazione p4 prevista ed eseguire comandi OS arbitrari con i privilegi del processo Composer.
Questo appartiene alla stessa famiglia di storici problemi di iniezione di argomenti nei driver VCS di Composer, dove i valori di URL/branch/stream finiscono in comandi shell senza escaping. Composer 2.9.6 rafforza il driver Perforce in modo che il payload iniettato non venga più eseguito.
La descrizione autorevole del comportamento qui dimostrato è il codice sorgente della PoC stesso (
CVE202640176Test.php); consultare l'avviso ufficiale e il changelog di Composer per i dettagli della correzione a monte.
La PoC è una singola classe, CVE202640176Test, che esegue un esperimento controllato A/B (differenziale):
--version su entrambi i binari di Composer, quello affetto (2.9.5) e quello corretto (2.9.6), e si interrompe anticipatamente se uno dei due non può essere invocato.composer.json la cui sezione repositories contiene una voce perforce con un URL p4:// malevolo che trasporta un payload shell iniettato.composer update in quella directory.finally ripristina sempre il composer.json originale nella directory del progetto.PASS solo quando l'esecuzione affetta mostra l'effetto collaterale e l'esecuzione corretta no.Per ogni esecuzione, validateRun() controlla tre cose:
| Controllo | Cosa prova |
|---|---|
| Il file marcatore esiste e contiene l'ID di esecuzione | Il payload iniettato touch/echo è effettivamente eseguito — cioè l'iniezione di comandi è riuscita. |
L'output di Composer menziona p4 | Il percorso del codice del driver Perforce è stato raggiunto (il payload è stato elaborato dal componente giusto, non da qualche passaggio non correlato). |
| Versione di Composer analizzata == attesa | Il binario corretto (2.9.5 vs 2.9.6) è stato quello eseguito. |
Un'esecuzione è "OK" solo quando tutti e tre passano. Il test complessivo passa quando l'esecuzione affetta è OK e quella corretta non lo è — la firma precisa di una vulnerabilità reale che è stata successivamente patchata.
L'URL del repository malevolo è costruito in writeComposerJson():
p4://127.0.0.1:1666:attacker_user;touch <marcatore> && echo '<runId>' > <marcatore>:client_test
Analizzandolo:
p4://127.0.0.1:1666:attacker_user — un URL Perforce dall'aspetto ben formato (host, porta 1666, utente).;touch <marcatore> && echo '<runId>' > <marcatore> — i comandi shell iniettati. Il ; iniziale termina il comando p4 previsto; touch crea il file marcatore, e echo '<runId>' > <marcatore> scrive l'ID di esecuzione univoco nel file in modo che la PoC possa confermare che il payload (e non un processo non correlato) ha prodotto il file.:client_test — testo finale per mantenere plausibile il resto dell'analisi dell'URL.Sul driver affetto i metacaratteri della shell sono onorati e il file marcatore viene creato. Sul driver corretto il valore è opportunamente escaped/citato, quindi la stessa stringa viene trattata come dati inerti e nessun marcatore appare.
Nota: la PoC utilizza un ID di esecuzione univoco con timestamp e scrive il suo marcatore all'interno di una directory temporanea isolata, quindi il payload è benigno e si autopulisce piuttosto che essere distruttivo.
2.9.5 (affetto)2.9.6 (corretto)exec() esegue cd … && php …). Progettato per Linux/macOS.composer.json base nella directory del progetto (viene letto all'avvio, copiato in ogni esecuzione temporanea e ripristinato dopo).Generalmente non è necessario un server Perforce attivo: la vulnerabilità risiede nel modo in cui Composer costruisce la riga di comando
p4, e il payload iniettato viene eseguito prima/intorno a qualsiasi connessionep4reale. Composer potrebbe registrare un errore di connessione Perforce — ciò è previsto e non influisce sulla prova del file marcatore.
Clonare / posizionare la PoC in una directory di lavoro.
Fornire un composer.json nella stessa directory della PoC. Un minimo è sufficiente:
{
"name": "research/cve-2026-40176-poc",
"description": "Manifest base per la PoC differenziale CVE-2026-40176",
"require": {}
}
Ottenere i due binari Composer e posizionarli dove la PoC li aspetta (valori predefiniti mostrati):