Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-56848 — Exploit PoC pour CVE-2026-56848, une vulnérabilité heap-use-after-free de Node.js HTTP/2 permettant un déni de service (DoS) à distance sans authentification. Inclut un déclencheur raw-socket, des instructions de compilation ASan et une cible basée sur Docker. | Kitploit
Outils/GitHubGitHub/open-flaw/cve-2026-56848
Analyse des VulnérabilitésAnalyse Dynamique de Code (DAST)ExploitationSécurité WebSécurité Réseau
GitHubopen-flaw/cve-2026-56848

CVE-2026-56848

Exploit PoC pour CVE-2026-56848, une vulnérabilité heap-use-after-free de Node.js HTTP/2 permettant un déni de service (DoS) à distance sans authentification. Inclut un déclencheur raw-socket, des instructions de compilation ASan et une cible basée sur Docker.

Voir le dépôt
62il y a 2 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-56848

Description NVD

Une faille dans la gestion HTTP/2 de Node.js permet à nghttp2_session_mem_send() d'être appelée de manière réentrante pendant que nghttp2_session_mem_recv() est en cours d'exécution, ce qui entraîne un heap-use-after-free.

Cette vulnérabilité affecte Node.js 26.x, 24.x et 22.x.

(Remarque : les versions mentionnées dans la description s'appliquent uniquement au paquet nodejs amont et non au paquet nodejs tel que distribué par Alpine.

Ligne de versionVulnérableCorrigé
22.x (LTS)≤ 22.23.122.23.2
24.x (LTS)≤ 24.18.024.18.1
26.x≤ 26.5.026.5.1
  • Gravité : Élevée (CVSS 7.5, AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — DoS à distance, non authentifié, via corruption du tas)
  • Signalé par : hahahkim (HackerOne #3833629)
  • Corrigé par : Matteo Collina (mcollina)
  • Commit du correctif (v22) : daa6d25e3dce — "http2: defer rst stream while in scope" (nodejs-private/node-private#921)
  • Divulgué : versions de sécurité Node.js, 2026-07-29

Cause racine

Http2Stream::SubmitRstStream() dans src/node_http2.cc force une purge des données sortantes en attente avant de mettre le RST_STREAM en file d'attente :```cpp void Http2Stream::SubmitRstStream(const uint32_t code) { CHECK(!this->is_destroyed()); code_ = code;

// (NGHTTP2_CANCEL is deferred — fix for an older double-free) if (session_->is_in_scope() && is_stream_cancel(code)) { session_->AddPendingRstStream(id_); return; }

// If possible, force a purge of any currently pending data here to make // sure it is sent before closing the stream. ... if (session_->SendPendingData() != 0) { // ← RE-ENTRANT mem_send() session_->AddPendingRstStream(id_); return; }

FlushRstStream(); }

`SendPendingData()` appelle `nghttp2_session_mem_send()` ([node_http2.cc:1970](https://github.com/nodejs/node/blob/v22.23.1/src/node_http2.cc#L1970)). Sa seule protection contre la réentrance est `is_sending()`, qui protège contre *un envoi pendant un envoi* (une écriture déjà en cours) — **pas contre un envoi pendant une réception**. Quand `SubmitRstStream()` s’exécute depuis l’intérieur d’une chaîne de callbacks `nghttp2_session_mem_recv()` (dans la portée), la purge exécute `mem_send()` de manière réentrante.

Le `mem_send()` réentrant transmet les trames dont le traitement côté envoi détruit les flux (`nghttp2_session_close_stream_on_goaway()` → `on_stream_close` → `Http2Stream::Destroy()` → libération du `Http2Stream` C++). Le flux libéré est toujours référencé par l’opération de réception en cours : `SubmitRstStream()` lui-même continue de s’exécuter sur le `this` libéré (son `FlushRstStream()` final lit `is_destroyed()`), et le `mem_recv()` externe continue de parcourir l’état des trames/en-têtes du flux fermé → **heap-use-after-free**.

### Chaîne de déclenchement (le tout dans un seul appel `nghttp2_session_mem_recv()`)

1. L’attaquant envoie `GOAWAY(lastStreamID=0, NO_ERROR)` immédiatement suivi de trames `HEADERS` pour de nouveaux flux (3, 5, 7, …) dans un seul segment TCP.
2. Le `mem_recv()` du serveur traite GOAWAY → JS `session.close()` → `session.closed = true` et un GOAWAY sortant est **soumis mais pas encore envoyé**.
3. nghttp2 ne refuse les nouveaux flux entrants qu’une fois que GOAWAY est effectivement *envoyé* (`session_allow_incoming_new_stream()` vérifie `TERM_ON_SEND | SENT`, pas `SUBMITTED`), donc `HEADERS(3)` est encore accepté.
4. `onSessionHeaders()` côté JS voit un nouveau flux sur une session fermée et le refuse : `handle.rstStream(NGHTTP2_REFUSED_STREAM)` (lib/internal/http2/core.js).
5. Le `SubmitRstStream(NGHTTP2_REFUSED_STREAM)` C++ s’exécute dans la portée (dans `mem_recv`), `REFUSED_STREAM ≠ CANCEL` → enchaîne sur `SendPendingData()` → **`nghttp2_session_mem_send()` réentrant**.
6. L’envoi réentrant transmet le GOAWAY sortant ; le traitement du GOAWAY par nghttp2 côté envoi ferme les flux entrants avec id > 1 (`session_close_stream_on_goaway(..., NGHTTP2_REFUSED_STREAM)`), déclenchant `on_stream_close` → `Http2Stream::Destroy()` libère l’objet flux C++ du flux 3.
7. L’exécution retourne dans `SubmitRstStream()` sur l’objet libéré (`FlushRstStream()`), et le `mem_recv()` externe reprend sur un état session/flux corrompu → UAF.

Preuve avec `NODE_DEBUG_NATIVE=http2` sur un serveur vulnérable (une lecture de 86 octets) :```
receiving 86 bytes, offset 0
complete frame received: type: 7          ← GOAWAY
submitting goaway                          ← GOAWAY submitted, NOT yet sent
beginning headers for stream 3             ← still accepted (only SUBMITTED)
handle headers frame for stream 3          ← JS: session.closed → refuse
sending rst_stream with code 7             ← SubmitRstStream(REFUSED_STREAM), in scope
sending pending data                       ← RE-ENTRANT mem_send()
stream 3 closed with code: 7               ← GOAWAY send closes stream 3
Removing stream: 3 / destroying stream     ← Http2Stream freed mid-recv

Signature comportementale

La sortie au niveau du fil est identique sur les builds vulnérables et corrigés (les deux finissent par n'envoyer que le GOAWAY sortant — sur les builds vulnérables, le RST est soumis contre un flux déjà fermé, sur les builds corrigés, nghttp2 abandonne les RST en file d'attente une fois le GOAWAY envoyé en premier). La différence est interne, visible avec NODE_DEBUG_NATIVE=http2 :

  • Vulnérable : sending pending data apparaît entre sending rst_stream with code 7 et stream 3 closed with code: 7 — le mem_send() réentrant s'exécute en pleine réception et ferme/détruit le flux 3 pendant que mem_recv() est toujours en vol (crash sous ASan).
  • Corrigé : pas de sending pending data entre les deux — le RST est simplement mis en file d'attente ; le flux 3 ne se ferme que lors du flush normal post-réception.

PoC```

server.js # minimal http2.createServer() target (no handler needed) exploit.js # raw-socket HTTP/2 client that drives the trigger

### Exécution rapide```bash
# Terminal 1: the target (any vulnerable node: 22.23.1 / 24.18.0 / 26.5.0 or older in their lines)
node server.js 8000                       # NODE_BIN=/path/to/node for a specific binary

# Terminal 2: the attack — a crash shows up in terminal 1 (ASan report / segfault)
node exploit.js --port 8000 --iterations 200

./bin/node (la compilation ASan locale utilisée ci-dessous) n'est pas suivi dans git — compilez-le avec les instructions de la section "Compilation d'un Node.js vulnérable instrumenté avec ASan", ou passez par Docker.

L'exploit rapporte le statut de chaque connexion ; SKIPPED (no handshake) après la première connexion signifie que la cible a déjà succombé à l'attaque.

Docker

Le Dockerfile construit une cible vulnérable v22.23.1 avec ASan dans un conteneur (aucune chaîne d'outils locale n'est nécessaire — seul le démon Docker est requis) :```bash docker build -t cve-2026-56848 . docker run --rm -p 8000:8000 --name cve-target cve-2026-56848

from the host, in another terminal:

you could reuse ./bin/node

node exploit.js --port 8000 --iterations 10

Télécharger l’outil