
Prueba de concepto diferencial para CVE-2026-40176, que demuestra inyección de comandos del sistema operativo en el controlador Perforce de Composer a través de una URL de repositorio maliciosa, con pruebas A/B automatizadas contra versiones afectadas y corregidas.
Una prueba de concepto autocontenida, con estilo OOP en PHP, que demuestra y verifica diferencialmente una vulnerabilidad de inyección de comandos en el controlador de repositorio Perforce de Composer.
La PoC ejecuta el mismo composer.json malicioso contra dos binarios de Composer: una versión afectada (2.9.5) y una versión corregida (2.9.6), y demuestra el error observando un efecto secundario (un archivo marcador escrito por un comando de shell inyectado) que se activa en la versión afectada pero no en la corregida.
⚠️ Solo para investigación de seguridad autorizada y pruebas defensivas. Consulte Uso responsable.
| CVE | CVE-2026-40176 |
| Componente | Composer — controlador de repositorio/VCS Perforce (perforce) |
| Clase | Inyección de comandos del sistema operativo a través de URL de repositorio controlada por el atacante |
| Superficie de ataque | Un composer.json que contiene una entrada repositories manipulada con type: perforce |
| Afectado | Composer 2.9.5 |
| Corregido | Composer 2.9.6 |
| Disparador | Resolución/actualización de dependencias (composer update) contra el manifiesto malicioso |
| Impacto | Ejecución arbitraria de comandos en la máquina que ejecuta Composer |
| Lenguaje de la PoC | PHP (archivo único, sin dependencias externas) |
Composer puede resolver paquetes de varios sistemas de control de versiones. Para Perforce, el repositorio se identifica mediante una URL p4:// que codifica el host, puerto y usuario/stream. Cuando el controlador Perforce de Composer construye la línea de comandos subyacente de p4, los campos tomados de la URL controlada por el atacante no se sanitizan suficientemente antes de pasarse a un shell.
Debido a que el autor del manifiesto controla completamente la URL del repositorio, un atacante que pueda lograr que una víctima ejecute composer update/composer install contra un composer.json malicioso (por ejemplo, una dependencia envenenada, un repositorio hostil o un trabajo de CI que procese archivos de proyecto no confiables) puede escapar de la invocación prevista de p4 y ejecutar comandos arbitrarios del sistema operativo con los privilegios del proceso de Composer.
Esto pertenece a la misma familia que los problemas históricos de inyección de argumentos en los controladores VCS de Composer, donde los valores de URL/rama/stream fluyen hacia comandos de shell sin escape. Composer 2.9.6 refuerza el controlador Perforce para que el payload inyectado ya no se ejecute.
La descripción autorizada del comportamiento demostrado aquí es el código fuente de la PoC (
CVE202640176Test.php); consulte el aviso oficial y el registro de cambios de Composer para obtener detalles sobre la corrección upstream.
La PoC es una única clase, CVE202640176Test, que realiza un experimento controlado A/B (diferencial):
--version tanto en el binario afectado (2.9.5) como en el corregido (2.9.6) de Composer y aborta temprano si alguno no puede invocarse.composer.json cuya sección repositories contiene una entrada perforce con una URL p4:// maliciosa que lleva un payload de shell inyectado.composer update en ese directorio.finally siempre restaura el composer.json original en el directorio del proyecto.PASS solo cuando la ejecución afectada muestra el efecto secundario y la ejecución corregida no.Para cada ejecución, validateRun() verifica tres cosas:
| Verificación | Qué demuestra |
|---|---|
| Existe el archivo marcador y contiene el ID de ejecución | El payload inyectado touch/echo realmente se ejecutó — es decir, la inyección de comandos tuvo éxito. |
La salida de Composer menciona p4 | Se alcanzó la ruta de código del controlador Perforce (el payload fue procesado por el componente correcto, no por algún paso no relacionado). |
| La versión de Composer analizada coincide con la esperada | El binario correcto (2.9.5 vs 2.9.6) fue el que se ejecutó. |
Una ejecución es "OK" solo cuando las tres pasan. La prueba general pasa cuando la ejecución afectada es OK y la ejecución corregida no — la firma precisa de una vulnerabilidad real que fue parcheada posteriormente.
La URL del repositorio malicioso se construye en writeComposerJson():
p4://127.0.0.1:1666:attacker_user;touch <marcador> && echo '<idEjecucion>' > <marcador>:client_test
Desglosándola:
p4://127.0.0.1:1666:attacker_user — una URL de Perforce de aspecto bien formado (host, puerto 1666, usuario).;touch <marcador> && echo '<idEjecucion>' > <marcador> — los comandos de shell inyectados. El ; inicial termina el comando p4 previsto; touch crea el archivo marcador, y echo '<idEjecucion>' > <marcador> escribe el ID de ejecución único en él para que la PoC pueda confirmar que el payload (y no algún proceso no relacionado) produjo el archivo.:client_test — texto final para mantener el resto del análisis de la URL plausible.En el controlador afectado, los metacaracteres del shell se respetan y se crea el archivo marcador. En el controlador corregido, el valor se escapa/entrecomilla adecuadamente, por lo que la misma cadena se trata como datos inertes y no aparece ningún marcador.
Nota: la PoC utiliza un ID de ejecución único con marca de tiempo y escribe su marcador dentro de un directorio temporal aislado, por lo que el payload es benigno y se autolimpia en lugar de ser destructivo.
2.9.5 (afectado)2.9.6 (corregido)exec() ejecuta cd … && php …). Diseñado para Linux/macOS.composer.json base en el directorio del proyecto (se lee al inicio, se copia en cada ejecución temporal y se restaura después).Generalmente no necesita un servidor Perforce en funcionamiento: la vulnerabilidad está en cómo Composer construye la línea de comandos de
p4, y el payload inyectado se ejecuta antes/alrededor de cualquier conexión real ap4. Composer puede registrar un error de conexión de Perforce — eso es esperado y no afecta la prueba del archivo marcador.
Clone / coloque la PoC en un directorio de trabajo.
Proporcione un composer.json en el mismo directorio que la PoC. Uno mínimo es suficiente: